#include QMK_KEYBOARD_H

#define MAXIMIZ A(G(KC_F))
#define NXTDSPL C(A(G(KC_RIGHT)))
#define BTMHALF A(G(KC_DOWN))
#define TOPHALF A(G(KC_UP))
#define NXTTHRD SS_LCTL(SS_LALT(SS_TAP(X_RIGHT)))
#define PRVTHRD C(A(KC_LEFT))

#define LT_MSTP LT(_LAYER_SELECT, KC_MSTP)
#define LT_ESC LT(_LAYER_SELECT, KC_ESC)
#define LT_NXTH TD(TD_SPEC)

#define writeLayerLedState(layer) writePin(layer)
#define layer_led_0_off B6, false
#define layer_led_1_off D5, true
#define layer_led_2_off B0, false
#define layer_led_0_on B6, true
#define layer_led_1_on D5, false
#define layer_led_2_on B0, true

void led_init_animation(void);
void led_set_layer(int layer);
void td_spectacles_finish(qk_tap_dance_state_t *state, void *user_data);
void td_spectacles_reset(qk_tap_dance_state_t *state, void *user_data);

enum layer_led_mode {
    ALL_LAYERS_OFF = -1,
    LAYER_0,
    LAYER_1,
    LAYER_2,
    LAYER_3,
    LAYER_4,
    LAYER_5,
    ALL_LAYERS_ON,
};

enum mini_layers {
    _MEDIA,
    _COPYPASTA,
    _SPECTACLES,
    _LAYER_3,
    _LAYER_4,
    _LAYER_5,
    _LAYER_SELECT,
};

enum { TD_SPEC = 0 };

qk_tap_dance_action_t tap_dance_actions[] = {
    /* Tap once for spectacles macro, hold for layer toggle */
    [TD_SPEC] = ACTION_TAP_DANCE_FN_ADVANCED(NULL, td_spectacles_finish, td_spectacles_reset),
};

/*
 *   Key Layout
 *   _____   _____   _____
 *  |     | |     | |     |
 *  |  1  | |  2  | |  3  |
 *  |_____| |_____| |_____|
 *   _____   _____   _____
 *  |     | |     | |     |
 *  |  4  | |  5  | |  6  |
 *  |_____| |_____| |_____|
 *
 *  Hold 1 and:
 *    - press 4, 5, or 6 to change layers
 *    - press 3 to reset, or press and hold 3 to enter DFU programming mode
 *
 *  If something prevents you from using the above to reprogram, you can hold 3 while powering on as a fallback.
 */

/* clang-format off */
const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
    /*
     *  Layer 0 (Media Keys)
     *   _____   _____   _____
     *  |     | |     | |     |
     *  |Stop | |VolUp| |Play |
     *  |_____| |_____| |_____|
     *   _____   _____   _____
     *  |     | |     | |     |
     *  |Prev | |VolDn| |Next |
     *  |_____| |_____| |_____|
     *
     */
    [_MEDIA] = LAYOUT(
        LT_MSTP, KC_VOLU, KC_MPLY,
        KC_MPRV, KC_VOLD, KC_MNXT
    ),

    /*
     *  Layer 1 (Copy and Paste Macros)
     *   _____   _____   _____
     *  |     | |     | |     |
     *	| ESC | |Ctl+Z| |CSf+Z|
     *	|_____| |_____| |_____|
     *   _____   _____   _____
     *  |     | |     | |     |
     *	|Ctl+X| |Ctl+C| |Ctl+V|
     *	|_____| |_____| |_____|
     *
     */

    [_COPYPASTA] = LAYOUT(
        LT_ESC,  C(KC_Z), C(S(KC_Z)),
        C(KC_X), C(KC_C), C(KC_V)
    ),

    /*
     *   Layer 2 (Spectacles Macros)
     *   _LT&_   _____   _____
     *  | Win | | Win | | Win |
     *  | Next| | Top | |Mximze
     *  |_1/3_| |_1/2_| |_____|
     *   _____	 _____	 _____
     *  | Win | | Win | | Win |
     *  | Prev| | Bot | | Next|
     *  |_1/3_| |_1/2_| |Displ|
     *
     */
    [_SPECTACLES] = LAYOUT(
        LT_NXTH,TOPHALF,MAXIMIZ,
        PRVTHRD,BTMHALF,NXTDSPL
    ),

    /*
     *  Layer 6 (Layer Select/Reset)
     *   _____   _____   _____
     *  |     | |     | |     |
     *  |None | |None | |Reset|
     *  |_____| |_____| |_____|
     *   _____   _____   _____
     *  |Layer| |Layer| |Layer|
     *  |  0  | |  1  | |  2  |
     *  |_____| |_____| |_____|
     *
     *  Layers 0, 1, and 2 have the top left button held to activate this layer.
     *  Then press the specific layer to switch to it.
     *
     */
    [_LAYER_SELECT] = LAYOUT(
        _______, _______, RESET,
        TO(_MEDIA),TO(_COPYPASTA),TO(_SPECTACLES)
    )
};
/* clang-format on */

/*
 *	Knops Mini LED Numbers:
 *   _____   _____   _____
 *  |     | |     | |     |
 *  |  1  | |  2  | |  3  |    <---
 *  |_____| |_____| |_____|       |      These LEDs are called 'Switch LEDs'
 *   _____   _____   _____        |----- To turn on/off these leds, use:
 *  |     | |     | |     |       |          set_switch_led( [1-6], [true/false]);
 *  |  4  | |  5  | |  6  |    <---
 *  |_____| |_____| |_____|
 *
 *	 < 0 >   < 1 >   < 2 >     <---      These front-LEDs are called 'Layer LEDs'
 *                                       To turn on/off these leds, use:
 *                                           set_layer_led( [0-2], [true/false] );
 */

void set_switch_led(int ledId, bool state) {
    switch (ledId) {
        case 1:
            writePin(D7, state);
            break;
        case 2:
            writePin(readPin(B7) ? C6 : C7, state);
            break;
        case 3:
            writePin(D4, state);
            break;
        case 4:
            writePin(E6, state);
            break;
        case 5:
            writePin(B4, state);
            break;
        case 6:
            writePin(D6, state);
            break;
    }
}

void set_layer_led(int layerLedMode) {
    writeLayerLedState(layer_led_0_off);
    writeLayerLedState(layer_led_1_off);
    writeLayerLedState(layer_led_2_off);

    switch (layerLedMode) {
        case ALL_LAYERS_OFF:
            break;
        case LAYER_0:
            writeLayerLedState(layer_led_0_on);
            break;
        case LAYER_1:
            writeLayerLedState(layer_led_1_on);
            break;
        case LAYER_2:
            writeLayerLedState(layer_led_2_on);
            break;
        case LAYER_3:
            writeLayerLedState(layer_led_0_on);
            writeLayerLedState(layer_led_1_on);
            break;
        case LAYER_4:
            writeLayerLedState(layer_led_1_on);
            writeLayerLedState(layer_led_2_on);
            break;
        case LAYER_5:
            writeLayerLedState(layer_led_0_on);
            writeLayerLedState(layer_led_2_on);
            break;
        default:
            writeLayerLedState(layer_led_0_on);
            writeLayerLedState(layer_led_1_on);
            writeLayerLedState(layer_led_2_on);
            break;
    }
}

void led_init_animation() {
    for (int i = ALL_LAYERS_OFF; i <= ALL_LAYERS_ON; i++) {
        led_set_layer(i);
    }

    led_set_layer(LAYER_0);
}

/*
 * This function led_set_layer gets called when you switch between layers.
 * It allows you to turn on and off leds for each different layer and do
 * other cool stuff.
 */
void led_set_layer(int layer) {
    switch (layer) {
        case ALL_LAYERS_OFF:
            set_switch_led(1, false);
            set_switch_led(2, false);
            set_switch_led(3, false);
            set_switch_led(4, false);
            set_switch_led(5, false);
            set_switch_led(6, false);

        case _LAYER_SELECT:
            set_switch_led(1, false);
            set_switch_led(2, false);
            set_switch_led(3, false);
            set_switch_led(4, true);
            set_switch_led(5, true);
            set_switch_led(6, true);
            break;

        default:
            set_switch_led(1, true);
            set_switch_led(2, true);
            set_switch_led(3, true);
            set_switch_led(4, true);
            set_switch_led(5, true);
            set_switch_led(6, true);
            break;
    }

    set_layer_led(layer);
}

/*
 * Prepare all LED ports for output
 */
void led_init_ports() {
    setPinOutput(D7);  // Switch 1 LED
    setPinOutput(C6);  // Switch 2 LED Pin1
    setPinOutput(C7);  // Switch 2 LED Pin2
    setPinOutput(D4);  // Switch 3 LED
    setPinOutput(E6);  // Switch 4 LED
    setPinOutput(B4);  // Switch 5 LED
    setPinOutput(D6);  // Switch 6 LED
    setPinOutput(D5);  // Layer 0 LED
    setPinOutput(B6);  // Layer 1 LED
    setPinOutput(B0);  // Layer 2 LED
}

// Runs on layer change, no matter where the change was initiated
layer_state_t layer_state_set_user(layer_state_t state) {
    set_layer_led(get_highest_layer(state));
    led_set_layer(get_highest_layer(state));
    return state;
}

void matrix_init_user(void) {
    led_init_ports();
    led_init_animation();
}

void td_spectacles_finish(qk_tap_dance_state_t *state, void *user_data) {
    if (state->pressed) {
        layer_on(_LAYER_SELECT);
    } else {
        SEND_STRING(NXTTHRD);
    }
}

void td_spectacles_reset(qk_tap_dance_state_t *state, void *user_data) { layer_off(_LAYER_SELECT); }