// Pin header footprint for a small RP2040-based controller board from AliExpress called "Pico Mini RP2040".
// It has a similar footprint to the Pro Micro (NOT pin-compatible though!), but has an additional row of pins
// on the short edge of the board (basically forming a "U").
// -------------------------------
// Params
//  orientation: if up (default), the controller will be on the front side of the PCB, otherwise on the back side

module.exports = {
    params: {
        designator: 'MCU',
        orientation: 'up',

        // Left side of pins (with USB port at the top), top to bottom
        GP0: {type: 'net', value: 'GP0'},
        GP1: {type: 'net', value: 'GP1'},
        GP2: {type: 'net', value: 'GP2'},
        GP3: {type: 'net', value: 'GP3'},
        GP4: {type: 'net', value: 'GP4'},
        GP5: {type: 'net', value: 'GP5'},
        GP6: {type: 'net', value: 'GP6'},
        GP7: {type: 'net', value: 'GP7'},
        GP8: {type: 'net', value: 'GP8'},
        GP9: {type: 'net', value: 'GP9'},
        GP10: {type: 'net', value: 'GP10'},
        GP11: {type: 'net', value: 'GP11'},
        GP12: {type: 'net', value: 'GP12'},
        GP13: {type: 'net', value: 'GP13'},

        // Bottom side of pins, left to right (i.e. between GP13 and GP19)
        GP14: {type: 'net', value: 'GP14'},
        GP15: {type: 'net', value: 'GP15'},
        GP16: {type: 'net', value: 'GP16'},
        GP17: {type: 'net', value: 'GP17'},
        GP18: {type: 'net', value: 'GP18'},

        // Right side of pins (with USB port at the top), top to bottom
        '5V': {type: 'net', value: '5V'},  // 5V USB voltage
        GND: {type: 'net', value: 'GND'},
        RST: {type: 'net', value: 'RST'},  // Reset pin: Pull down to reset
        '3V3': {type: 'net', value: '3V3'},  // 3.3V voltage used by the MCU
        GP29: {type: 'net', value: 'GP29'},  // also: ADC4
        GP28: {type: 'net', value: 'GP28'},  // also: ADC2
        GP27: {type: 'net', value: 'GP27'},  // also: ADC1
        GP26: {type: 'net', value: 'GP26'},  // also: ADC0
        GP25: {type: 'net', value: 'GP25'},
        GP23: {type: 'net', value: 'GP23'},
        GP22: {type: 'net', value: 'GP22'},
        GP21: {type: 'net', value: 'GP21'},
        GP20: {type: 'net', value: 'GP20'},
        GP19: {type: 'net', value: 'GP19'},
    },
    body: p => {
        let sign, side, mirror;

        if (p.orientation === 'down') {
            sign = -1;
            side = 'B';
            mirror = 'mirror';
        } else {
            sign = 1;
            side = 'F';
            mirror = '';
        }

        const pins_left = [
            'GP0', 'GP1', 'GP2', 'GP3', 'GP4', 'GP5', 'GP6', 'GP7', 'GP8', 'GP9', 'GP10', 'GP11', 'GP12', 'GP13',
        ];
        const pins_bottom = [
            'GP14', 'GP15', 'GP16', 'GP17', 'GP18',
        ];
        const pins_right = [
            '5V', 'GND', 'RST', '3V3', 'GP29', 'GP28', 'GP27', 'GP26', 'GP25', 'GP23', 'GP22', 'GP21', 'GP20', 'GP19',
        ];

        // Functions to map coordinates from a "pin grid" to actual X/Y coordinates.
        // The grid is defined as follows (assuming the board is in "up" orientation with the USB port on top):
        // - The X coordinates go from 0 (left) to 6 (right)
        // - The Y coordinates go from 0 (top) to 13 (bottom)
        // This means the pin rows are on these coordinates:
        // - Left side of pins: 0/0 to 0/13
        // - Right side of pins: 6/0 to 6/13
        // - Additional pins on the bottom (not counting the left and right pins): 1/13 to 5/13
        // These grid coordinates have to be mapped to footprint coordinates so that the board is centered, meaning
        // the grid position 3/6.5 needs to be mapped to footprint coordinates 0/0. The grid is a standard 2.54mm grid.
        function pin_pos_x(pin_number) {
            // Use integers to avoid floating point issues
            return sign * (254 * (pin_number - 3)) / 100;
        }
        function pin_pos_y(pin_number) {
            // Use integers to avoid floating point issues
            return (254 * (pin_number - 7) + 127) / 100;
        }

        function pin_label(pin_name, pos_x, pos_y, pin_side) {
            let offset_x = 0;
            let offset_y = 0;
            let offset_rotation = 0;
            let justify = 'left';

            if (pin_side === 'left') {
                offset_x = -1.3 * sign;
                justify = 'right';
            } else if (pin_side === 'right') {
                offset_x = 1.3 * sign;
            } else if (pin_side === 'bottom') {
                offset_y = 1.7;
                offset_rotation = -90 * sign;
            }

            // Shorten the name of the "GPxx" pins to save a bit of space on the PCB
            if (pin_name.startsWith('GP'))  {
                pin_name = pin_name.slice(2);
            }

            return `
                (fp_text user ${pin_name}
                    (at ${pin_pos_x(pos_x) + offset_x} ${pin_pos_y(pos_y) + offset_y} ${p.r + offset_rotation})
                    (unlocked yes) (layer ${side}.SilkS)
                    (effects (font (size 0.8 0.8) (thickness 0.15)) (justify ${justify} ${mirror}))
                )`;
        }

        function pin_pad(pad_number, pos_x, pos_y, pin_name) {
            return `
                (pad ${pad_number} thru_hole circle
                    (at ${pin_pos_x(pos_x)} ${pin_pos_y(pos_y)} 0)
                    (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.Mask)
                    ${p[pin_name]}
                )`;
        }

        function render_pins() {
            let pin_label_body = '';
            let pin_pad_body = '';
            let pad_number = 1;

            for (let i = 0; i < pins_left.length; i++) {
                pin_label_body += pin_label(pins_left[i], 0, i, 'left');
                pin_pad_body += pin_pad(pad_number++, 0, i, pins_left[i]);
            }
            for (let i = 0; i < pins_bottom.length; i++) {
                pin_label_body += pin_label(pins_bottom[i], 1 + i, 13, 'bottom');
                pin_pad_body += pin_pad(pad_number++, 1 + i, 13, pins_bottom[i]);
            }
            for (let i = 0; i < pins_right.length; i++) {
                pin_label_body += pin_label(pins_right[i], 6, i, 'right');
                pin_pad_body += pin_pad(pad_number++, 6, i, pins_right[i]);
            }

            return pin_pad_body + '\n' + pin_label_body;
        }

        return `
            (module RP2040_PICO_MINI (layer F.Cu) (tedit 66FD3C6D)
                ${p.at /* parametric position */}

                ${'' /* footprint reference */}
                (fp_text reference "${p.ref}" (at 0 0) (layer ${side}.SilkS) ${p.ref_hide}
                    (effects (font (size 1.27 1.27) (thickness 0.15)))
                )
                (fp_text value "" (at 0 0) (layer ${side}.SilkS) hide
                    (effects (font (size 1.27 1.27) (thickness 0.15)))
                )

                ${'' /* component outline */}
                (fp_line (start -8.8 -18.0) (end  8.8 -18.0) (layer ${side}.SilkS) (width 0.15))
                (fp_line (start  8.8 -18.0) (end  8.8  18.0) (layer ${side}.SilkS) (width 0.15))
                (fp_line (start  8.8  18.0) (end -8.8  18.0) (layer ${side}.SilkS) (width 0.15))
                (fp_line (start -8.8  18.0) (end -8.8 -18.0) (layer ${side}.SilkS) (width 0.15))

                ${'' /* illustration of the USB port overhang */}
                (fp_line (start -4.3 -18.8) (end  4.3 -18.8) (layer Dwgs.User) (width 0.15))
                (fp_line (start  4.3 -18.8) (end  4.3 -11.8) (layer Dwgs.User) (width 0.15))
                (fp_line (start  4.3 -11.8) (end -4.3 -11.8) (layer Dwgs.User) (width 0.15))
                (fp_line (start -4.3 -11.8) (end -4.3 -18.8) (layer Dwgs.User) (width 0.15))

                ${'' /* pin labels and pads */}
                ${render_pins()}
            )
        `;
    }
}
