$fn = 72;

/*
 * Top screw holes are 15mm from top of case
 * Side screws are 6.5mm from side of case
 * Top middle screw post is 29.3mm long
 * Other screw posts are 12mm long
 * Screw post outer diameter 6.25mm
 * Screw post inner diameter 3mm
 * Horizontal ridge interval: 55.5mm
 * Vertical ridge interval: 31.3mm
 * Ridge thickness: 1.25mm
 * First horizontal ridge: Starts at keyboard X offset
 * First vertical ridge: 8mm
 */

key_switch_height = 11.10;
key_switch_sizes  = [
    [0.5,  1.0,  1.0,  1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0],
    [1.0,  1.0,  1.0,  1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.5],
    [1.25, 1.0,  1.0,  1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.25],
    [1.75, 1.0,  1.0,  1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.75],
    [1.0,  1.25, 1.25, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0]
];

pcb_height      =  1.600;
pcb_screw_holes = [
    [  4.7625, 85.7250],
    [ 66.6750, 85.7250],
    [161.9250, 85.7250],
    [238.1250, 85.7250],
    [ 61.9125, 47.6250],
    [138.1125, 47.6250],
    [214.3125, 47.6250],
    [ 20.2406,  9.5250],
    [123.8250,  9.5250],
    [238.1250,  9.5250]
];


module top_case(key_switch_sizes, pcb_screw_holes) {
    case_width_top     = 317.5000;
    case_width_bottom  = 320.0000;
    case_length_top    = 150.0000;
    case_length_bottom = 151.5000;

    wall_width  =  2.5000;
    wall_height = 17.2500;

    key_switch_width  = 19.0500;
    key_switch_length = 19.0500;
    keyboard_width    = key_switch_width  * 13.5;
    keyboard_length   = key_switch_length *  5.0;
    keyboard_x_offset = 12.2500;
    keyboard_y_offset = 12.2500;

    accent_width    =  1.0000;
    accent_depth    = 0.5;
    accent_y_stride = (keyboard_length - accent_width) / 5.0;
    accent_y_bottom = keyboard_y_offset;

    fin_width        =  2.0000;
    fin_gap_width    =  2.0000;
    fin_gap_length   = wall_width;
    fin_gap_height   = 12.0000;
    fin_gap_count    = 32;
    fin_gap_x_first  = keyboard_x_offset + keyboard_width - fin_gap_count * (fin_width + fin_gap_width);
    fin_gap_y_offset = case_length_bottom - fin_gap_length;

    module fascia() {
        linear_extrude(wall_width)
        difference() {
            square([case_width_bottom, case_length_bottom], false);
            translate([keyboard_x_offset, keyboard_y_offset, 0])
            square([keyboard_width, keyboard_length], false);
        }
    }

    module fin_gaps() {
        for (i = [0:fin_gap_count]) {
            x = fin_gap_x_first + (i * (fin_width + fin_gap_width));

            translate([x, fin_gap_y_offset, wall_height - fin_gap_height])
            cube([fin_gap_width, fin_gap_length, fin_gap_height], false);
        }
    }

    module accents() {
        for (y = [accent_y_bottom: accent_y_stride: case_length_bottom]) {
            /* Top */
            translate([0, y, wall_height - accent_depth])
            cube([case_width_bottom, accent_width, accent_depth], false);

            /* Right */
            translate([case_width_bottom - accent_depth, y, 0])
            cube([accent_depth, accent_width, wall_height], false);

            /* Left */
            translate([0, y, 0])
            cube([accent_depth, accent_width, wall_height], false);
        }
    }

    module body() {
        translate([0, 0, wall_height - wall_width])
        fascia();

        /* Upper */
        translate([0, case_length_bottom - wall_width, 0])
        cube([case_width_bottom, wall_width, wall_height - wall_width], false);

        /* Right */
        translate([case_width_bottom - wall_width, 0, 0])
        cube([wall_width, case_length_bottom, wall_height - wall_width], false);

        /* Lower */
        cube([case_width_bottom, wall_width, wall_height - wall_width], false);

        /* Left */
        cube([wall_width, case_length_bottom, wall_height - wall_width], false);
    }

    module screw_holes() {
        pcb_screw_diameter = 2.5;

        for (screw_hole = pcb_screw_holes) {
            translate([screw_hole[0], screw_hole[1], 0])
            cylinder(h=wall_width, d=pcb_screw_diameter);
        }
    }

    module keyboard_deck() {
        key_switch_footprint = [15.25, 15.25];

        function add(v)            = [for(p=v) 1]*v;
        function slice(v, x, y)    = [for (i=[x:y]) v[i]];
        function addrange(v, x, y) = add(slice(v, x, y));

        module key_switch_plate(key_switch_size) {
            plate_width  = key_switch_width * key_switch_size;
            plate_length = key_switch_length;

            if (key_switch_size <= 0.5) {
                cube([key_switch_width  * key_switch_size,
                      key_switch_length,
                      wall_width], false);
            } else {
                hole_width  = key_switch_footprint[0];
                hole_length = key_switch_footprint[1];

                hole_x = plate_width  / 2 - hole_width  / 2;
                hole_y = plate_length / 2 - hole_length / 2;

                linear_extrude(wall_width)
                difference() {
                    square([plate_width, plate_length], false);
                    
                    translate([hole_x, hole_y, 0])
                    square([hole_width, hole_length], false);
                }
            }
        }

        module walls() {
            /* Upper */
            translate([-wall_width, keyboard_length, 0])
            cube([keyboard_width + 2 * wall_width, wall_width, key_switch_height - wall_width], false);

            /* Right */
            translate([keyboard_width, -wall_width, 0])
            cube([wall_width, keyboard_length + 2 * wall_width, key_switch_height - wall_width], false);

            /* Lower */
            translate([-wall_width, -wall_width, 0])
            cube([keyboard_width + 2 * wall_width, wall_width, key_switch_height - wall_width], false);

            /* Left */
            translate([-wall_width, -wall_width, 0])
            cube([wall_width, keyboard_length + 2 * wall_width, key_switch_height - wall_width], false);
        }

        module body() {
            rows = len(key_switch_sizes);

            for (i = [0: rows-1]) {
                y = key_switch_length * (rows - 1 - i);

                key_switch_row = key_switch_sizes[i];
                cols           = len(key_switch_row);

                for (j = [0: cols-1]) {
                    x = key_switch_width * ((j == 0)?
                        0:
                        addrange(key_switch_row, 0, j-1));

                    key_switch_size = key_switch_row[j];

                    translate([x, y, 0])
                    key_switch_plate(key_switch_size);
                }
            }
        }

        difference() {
            body();
            screw_holes();
        }

        walls();
    }

    difference() {
        body();
        accents();
        fin_gaps();
    }

    keyboard_deck_z_offset = wall_height - key_switch_height;

    translate([keyboard_x_offset, keyboard_y_offset, keyboard_deck_z_offset])
    keyboard_deck();
}

top_case(key_switch_sizes, pcb_screw_holes);