import * as _0603 from '@typecad/passives/0603';
import { PCB, Component, TrackBuilder } from '@typecad/typecad';
import { ESP32_S3_MINI_1_N8 } from './ESP32_S3_MINI_1_N8';
import { I2C, UART, USB, Power } from '@typecad/typecad';

// Configuration options for the ESP32S3 module
interface ESP32Options {
    pcb: PCB;
    reference?: string;
    externalPower?: Power;
    passives?: typeof _0603;
}

/**
 * ### rd_esp32s3 - ESP32-S3 Development Module
 * 
 * #### Input Connections
 * externalPower: Power - Optional 3.3V external power supply
 * 
 * #### Available Interfaces
 * - i2c: I2C interface on pins IO8/IO9
 * - uart_0: Primary UART on RXD0/TXD0
 * - uart_1: Secondary UART on IO18/IO17  
 * - usb: USB interface on IO20/IO19
 */
export class rd_esp32s3 {
    // PCB and component management
    pcb: PCB;
    passives: typeof _0603;

    // Main components
    esp32: ESP32_S3_MINI_1_N8;
    powerCap: _0603.Capacitor;           // C1: Main power capacitor
    filterCap: _0603.Capacitor;          // C3: Power filter capacitor  
    enableCap: _0603.Capacitor;          // C2: Enable pin capacitor
    buttonCap: _0603.Capacitor;          // C4: Button debounce capacitor
    pullupResistor: _0603.Resistor;      // R1: Enable pullup resistor
    buttonResistor: _0603.Resistor;      // R2: Button resistor
    resetButton: Component;              // SW1: Reset button

    // Power management
    internalPower: Power;
    externalPower?: Power;

    // Communication interfaces
    i2c: I2C;
    uart_0: UART;
    uart_1: UART;
    usb: USB;
    
    // PCB elements
    components: (Component | TrackBuilder)[] = [];  // All PCB elements including components and tracks

    constructor(options: ESP32Options) {
        // Extract configuration options
        this.pcb = options.pcb;
        this.passives = options.passives || _0603;
        this.externalPower = options.externalPower;

        // Create the main ESP32 chip
        this.esp32 = new ESP32_S3_MINI_1_N8(options.reference);
        this.esp32.pcb = { x: 158.085, y: 89.75, rotation: 0 };

        // Set up power management
        this.setupPower();
        
        // Create all passive components
        this.createPassiveComponents();
        
        // Create the reset button
        this.createResetButton();
        
        // Set up communication interfaces
        this.setupCommunication();
        
        // Add all components to the components list
        this.components.push(
            this.esp32, this.powerCap, this.filterCap, this.enableCap, 
            this.buttonCap, this.pullupResistor, this.buttonResistor, this.resetButton
        );

        // Connect all the electrical nets
        this.connectNets();

        // Route the PCB tracks (adds tracks to components array)
        this.routeTracks();

        // Group everything together on the PCB
        this.pcb.group('rd_esp32s3', ...this.components);
    }

    private setupPower() {
        // Create internal 3.3V power from ESP32 pins
        this.internalPower = new Power({
            power: this.esp32._3V3,
            gnd: this.esp32.GND_1,
            voltage: 3.3
        });

        // Validate external power if provided
        if (this.externalPower) {
            if (this.externalPower.voltage !== 3.3) {
                throw new Error(`External power must be 3.3V, got ${this.externalPower.voltage}V`);
            }
        }
    }

    private createPassiveComponents() {
        // Main power supply capacitors
        this.powerCap = new this.passives.Capacitor({
            value: '22uF',
            description: 'Main power capacitor',
            pcb: { x: 146.685, y: 83.82, rotation: 180 }
        });

        this.filterCap = new this.passives.Capacitor({
            value: '0.1uF',
            description: 'Power filter capacitor',
            pcb: { x: 146.685, y: 85.725, rotation: 180 }
        });

        // Enable pin components
        this.enableCap = new this.passives.Capacitor({
            value: '1uF',
            description: 'Enable pin capacitor',
            pcb: { x: 169.05, y: 82.55, rotation: 0 }
        });

        this.pullupResistor = new this.passives.Resistor({
            value: '10kΩ',
            description: 'Enable pullup resistor',
            pcb: { x: 169.1, y: 84.455, rotation: 180 }
        });

        // Reset button components
        this.buttonCap = new this.passives.Capacitor({
            value: '0.1uF',
            description: 'Button debounce capacitor',
            pcb: { x: 169.05, y: 88.265, rotation: 0 }
        });

        this.buttonResistor = new this.passives.Resistor({
            value: '100kΩ',
            description: 'Button resistor',
            pcb: { x: 169.1, y: 86.36, rotation: 180 }
        });
    }

    private createResetButton() {
        this.resetButton = new Component({
            footprint: 'Button_Switch_SMD:SW_SPST_TL3342',
            prefix: 'SW',
            pcb: { x: 170.82, y: 94.005, rotation: 90 }
        });
    }

    private setupCommunication() {
        // Set up communication interfaces
        this.i2c = new I2C(this.esp32.IO8, this.esp32.IO9);
        this.uart_0 = new UART(this.esp32.RXD0, this.esp32.TXD0, this.esp32.IO15, this.esp32.IO16);
        this.uart_1 = new UART(this.esp32.IO18, this.esp32.IO17, this.esp32.IO19, this.esp32.IO20);
        this.usb = new USB(this.esp32.IO20, this.esp32.IO19);
    }

    private connectNets() {
        // Connect external power if provided
        if (this.externalPower) {
            this.pcb.net(this.externalPower.power!, this.internalPower.power!);
            this.pcb.net(this.externalPower.gnd!, this.internalPower.gnd!);
        }

        // Connect all ground pins together
        this.connectAllGrounds();

        // Connect power supply capacitors
        this.connectPowerCapacitors();

        // Connect reset button circuit
        this.connectResetButton();

        // Connect enable pin circuit
        this.connectEnablePin();
    }

    private connectAllGrounds() {
        // Connect all ESP32 ground pins together
        this.pcb.net(
            this.esp32.GND_1, this.esp32.GND_2, this.esp32.GND_3, this.esp32.GND_4,
            this.esp32.GND_5, this.esp32.GND_6, this.esp32.GND_7, this.esp32.GND_8,
            this.esp32.GND_9, this.esp32.GND_10, this.esp32.GND_11, this.esp32.GND_12,
            this.esp32.GND_13, this.esp32.GND_14, this.esp32.GND_15, this.esp32.GND_16,
            this.esp32.GND_17, this.esp32.GND_18, this.esp32.GND_19, this.esp32.GND_20,
            this.esp32.GND_21, this.esp32.GND_22, this.esp32.GND_23, this.esp32.GND_24,
            this.esp32.GND_25, this.esp32.GND_26, this.esp32.GND_27, this.esp32.GND_28,
            this.esp32.GND_29, this.esp32.GND_30, this.esp32.GND_31, this.esp32.GND_32
        );
    }

    private connectPowerCapacitors() {
        // Connect power capacitors to 3.3V rail
        this.pcb.net(this.powerCap.pin(1), this.filterCap.pin(1), this.esp32._3V3);
        // Connect power capacitors to ground
        this.pcb.net(this.powerCap.pin(2), this.filterCap.pin(2));
    }

    private connectResetButton() {
        // Connect reset button to debounce capacitor
        this.pcb.net(this.resetButton.pin(1), this.buttonCap.pin(1));
        this.pcb.net(this.resetButton.pin(2), this.buttonCap.pin(2), this.buttonResistor.pin(1));
        // Connect button to enable pin
        this.pcb.net(this.buttonResistor.pin(2), this.esp32.EN);
    }

    private connectEnablePin() {
        // Connect enable pullup resistor
        this.pcb.net(this.esp32._3V3, this.pullupResistor.pin(1));
        // Connect enable RC circuit
        this.pcb.net(this.pullupResistor.pin(2), this.enableCap.pin(1), this.esp32.EN);
        // Connect enable capacitor to ground
        this.pcb.net(this.esp32.GND_1, this.enableCap.pin(2));
    }

    private routeTracks() {
        // PCB routing - these define the copper traces between components
        // Each track connects specific points on the PCB
        // All tracks are added to the components array
        
        this.components.push(this.pcb.track().from({ x: 156.435, y: 91.4 }, "F.Cu", 0.2).to({ x: 159.735, y: 91.4, layer: "F.Cu", width: 0.2 }));
        this.components.push(this.pcb.track().from({ x: 166.061, y: 81.774 }, "F.Cu", 0.2).to({ x: 169.049, y: 81.774, layer: "F.Cu", width: 0.2 }));
        this.components.push(this.pcb.track().from({ x: 158.085, y: 88.1 }, "F.Cu", 0.2).to({ x: 158.085, y: 91.4, layer: "F.Cu", width: 0.2 }));
        this.components.push(this.pcb.track().from({ x: 156.435, y: 89.75 }, "F.Cu", 0.2).to({ x: 159.735, y: 89.75, layer: "F.Cu", width: 0.2 }));
        this.components.push(this.pcb.track().from({ x: 151.085, y: 84.65 }, "F.Cu", 0.2).to({ x: 151.085, y: 82.75, layer: "F.Cu", width: 0.2 }));
        this.components.push(this.pcb.track().from({ x: 165.085, y: 85.5 }, "F.Cu", 0.2).to({ x: 164.885, y: 85.5, layer: "F.Cu", width: 0.2 }));
        this.components.push(this.pcb.track().from({ x: 159.735, y: 88.1 }, "F.Cu", 0.2).to({ x: 159.735, y: 91.4, layer: "F.Cu", width: 0.2 }));
        this.components.push(this.pcb.track().from({ x: 151.085, y: 82.75 }, "F.Cu", 0.2).to({ x: 165.085, y: 82.75, layer: "F.Cu", width: 0.2 }));
        this.components.push(this.pcb.track().from({ x: 164.035, y: 84.65 }, "F.Cu", 0.2).to({ x: 164.035, y: 82.75, layer: "F.Cu", width: 0.2 }));
        this.components.push(this.pcb.track().from({ x: 156.435, y: 88.1 }, "F.Cu", 0.2).to({ x: 159.735, y: 88.1, layer: "F.Cu", width: 0.2 }));
        this.components.push(this.pcb.track().from({ x: 156.435, y: 88.1 }, "F.Cu", 0.2).to({ x: 156.435, y: 91.4, layer: "F.Cu", width: 0.2 }));
        this.components.push(this.pcb.track().from({ x: 164.885, y: 85.5 }, "F.Cu", 0.2).to({ x: 164.035, y: 84.65, layer: "F.Cu", width: 0.2 }));
        this.components.push(this.pcb.track().from({ x: 165.085, y: 82.75 }, "F.Cu", 0.2).to({ x: 166.061, y: 81.774, layer: "F.Cu", width: 0.2 }));
        this.components.push(this.pcb.track().from({ x: 165.085, y: 86.35 }, "F.Cu", 0.2).to({ x: 165.085, y: 85.5, layer: "F.Cu", width: 0.2 }));
        this.components.push(this.pcb.track().from({ x: 169.049, y: 81.774 }, "F.Cu", 0.2).to({ x: 169.825, y: 82.55, layer: "F.Cu", width: 0.2 }));
        this.components.push(this.pcb.track().from({ x: 159.735, y: 91.4 }, "F.Cu", 0.2).to({ x: 165.085, y: 96.75, layer: "F.Cu", width: 0.2 }));
        this.components.push(this.pcb.track().from({ x: 158.085, y: 82.75 }, "F.Cu", 0.2).to({ x: 158.085, y: 88.1, layer: "F.Cu", width: 0.2 }));
        this.components.push(this.pcb.track().from({ x: 151.085, y: 96.75 }, "F.Cu", 0.2).to({ x: 156.435, y: 91.4, layer: "F.Cu", width: 0.2 }));
        this.components.push(this.pcb.track().from({ x: 170.576, y: 82.07589 }, "F.Cu", 0.2).to({ x: 169.87311, y: 81.373, layer: "F.Cu", width: 0.2 }));
        this.components.push(this.pcb.track().from({ x: 169.925, y: 84.455 }, "F.Cu", 0.2).to({ x: 170.576, y: 83.804, layer: "F.Cu", width: 0.2 }));
        this.components.push(this.pcb.track().from({ x: 147.46, y: 85.725 }, "F.Cu", 0.4).to({ x: 147.46, y: 83.82, layer: "F.Cu", width: 0.4 }));
        this.components.push(this.pcb.track().from({ x: 151.06, y: 81.373 }, "F.Cu", 0.2).to({ x: 149.86, y: 82.573, layer: "F.Cu", width: 0.2 }));
        this.components.push(this.pcb.track().from({ x: 148.59, y: 85.725 }, "F.Cu", 0.4).to({ x: 147.46, y: 85.725, layer: "F.Cu", width: 0.4 }));
        this.components.push(this.pcb.track().from({ x: 149.86, y: 82.573 }, "F.Cu", 0.2).to({ x: 149.86, y: 85.5, layer: "F.Cu", width: 0.2 }));
        this.components.push(this.pcb.track().from({ x: 170.576, y: 83.804 }, "F.Cu", 0.2).to({ x: 170.576, y: 82.07589, layer: "F.Cu", width: 0.2 }));
        this.components.push(this.pcb.track().from({ x: 169.87311, y: 81.373 }, "F.Cu", 0.2).to({ x: 151.06, y: 81.373, layer: "F.Cu", width: 0.2 }));
        this.components.push(this.pcb.track().from({ x: 148.815, y: 85.5 }, "F.Cu", 0.4).to({ x: 148.59, y: 85.725, layer: "F.Cu", width: 0.4 }));
        this.components.push(this.pcb.track().from({ x: 151.085, y: 85.5 }, "F.Cu", 0.4).to({ x: 149.86, y: 85.5, layer: "F.Cu", width: 0.4 }).to({ x: 148.815, y: 85.5, layer: "F.Cu", width: 0.4 }));
        this.components.push(this.pcb.track().from({ x: 145.91, y: 83.82 }, "F.Cu", 0.2).to({ x: 145.91, y: 85.725, layer: "F.Cu", width: 0.2 }));
        this.components.push(this.pcb.track().from({ x: 168.92, y: 89.545 }, "F.Cu", 0.2).to({ x: 168.92, y: 97.155, layer: "F.Cu", width: 0.2 }));
        this.components.push(this.pcb.track().from({ x: 168.275, y: 88.9 }, "F.Cu", 0.2).to({ x: 168.92, y: 89.545, layer: "F.Cu", width: 0.2 }));
        this.components.push(this.pcb.track().from({ x: 168.275, y: 88.265 }, "F.Cu", 0.2).to({ x: 168.275, y: 88.9, layer: "F.Cu", width: 0.2 }));
        this.components.push(this.pcb.track().from({ x: 172.085, y: 88.265 }, "F.Cu", 0.2).to({ x: 172.72, y: 88.9, layer: "F.Cu", width: 0.2 }));
        this.components.push(this.pcb.track().from({ x: 172.72, y: 90.855 }, "F.Cu", 0.2).to({ x: 172.72, y: 97.155, layer: "F.Cu", width: 0.2 }));
        this.components.push(this.pcb.track().from({ x: 169.925, y: 88.165 }, "F.Cu", 0.2).to({ x: 169.825, y: 88.265, layer: "F.Cu", width: 0.2 }));
        this.components.push(this.pcb.track().from({ x: 172.72, y: 88.9 }, "F.Cu", 0.2).to({ x: 172.72, y: 90.855, layer: "F.Cu", width: 0.2 }));
        this.components.push(this.pcb.track().from({ x: 169.825, y: 88.265 }, "F.Cu", 0.2).to({ x: 172.085, y: 88.265, layer: "F.Cu", width: 0.2 }));
        this.components.push(this.pcb.track().from({ x: 169.925, y: 86.36 }, "F.Cu", 0.2).to({ x: 169.925, y: 88.165, layer: "F.Cu", width: 0.2 }));
        this.components.push(this.pcb.track().from({ x: 167.005, y: 82.55 }, "F.Cu", 0.2).to({ x: 168.275, y: 82.55, layer: "F.Cu", width: 0.2 }));
        this.components.push(this.pcb.track().from({ x: 165.085, y: 83.8 }, "F.Cu", 0.2).to({ x: 165.755, y: 83.8, layer: "F.Cu", width: 0.2 }));
        this.components.push(this.pcb.track().from({ x: 168.275, y: 82.55 }, "F.Cu", 0.2).to({ x: 168.275, y: 86.36, layer: "F.Cu", width: 0.2 }));
        this.components.push(this.pcb.track().from({ x: 165.755, y: 83.8 }, "F.Cu", 0.2).to({ x: 167.005, y: 82.55, layer: "F.Cu", width: 0.2 }));
    }
}