use std::fs::File;
use std::io::{Write};
use bitflags::bitflags;
use crate::controller::physical::ControllerState;
use crate::controller::emulated::{DeviceDescriptor, ENDPOINT1};

pub const DESCRIPTORS: [u8; 80] = [0x01, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
0x09, 0x04, 0x00, 0x00, 0x02, 0x03, 0x00, 0x00, 0x00,
0x09, 0x21, 0x11, 0x01, 0x00, 0x01, 0x22, 0x5E, 0x00,
0x07, 0x05, 0x02, 0x03, 0x40, 0x00, 0x05,
0x07, 0x05, 0x81, 0x03, 0x40, 0x00, 0x05,
0x09, 0x04, 0x00, 0x00, 0x02, 0x03, 0x00, 0x00, 0x00,
0x09, 0x21, 0x11, 0x01, 0x00, 0x01, 0x22, 0x5E, 0x00,
0x07, 0x05, 0x02, 0x03, 0x40, 0x00, 0x05,
0x07, 0x05, 0x81, 0x03, 0x40, 0x00, 0x05];
pub const STRINGS: [u8; 16] = [0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];

pub const DEVICE_DESCRIPTOR: DeviceDescriptor = DeviceDescriptor{b_device_class: 0x0, b_device_sub_class: 0x0, id_vendor: 0x33DD, id_product: 0x0001, bcd_device: 0x0106, i_manufacturer: "TAITO", i_product: "Densha de Go! Plug & Play (NS One Handle mode)", i_serial_number: "ZKNS-001"};

pub const HID_REPORT_DESCRIPTOR: [u8; 94] = [
    0x05, 0x01,        // Usage Page (Generic Desktop Ctrls)
    0x09, 0x05,        // Usage (Game Pad)
    0xA1, 0x01,        // Collection (Application)
    0x15, 0x00,        //   Logical Minimum (0)
    0x25, 0x01,        //   Logical Maximum (1)
    0x35, 0x00,        //   Physical Minimum (0)
    0x45, 0x01,        //   Physical Maximum (1)
    0x75, 0x01,        //   Report Size (1)
    0x95, 0x0E,        //   Report Count (14)
    0x05, 0x09,        //   Usage Page (Button)
    0x19, 0x01,        //   Usage Minimum (0x01)
    0x29, 0x0E,        //   Usage Maximum (0x0E)
    0x81, 0x02,        //   Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
    0x95, 0x02,        //   Report Count (2)
    0x81, 0x01,        //   Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
    0x05, 0x01,        //   Usage Page (Generic Desktop Ctrls)
    0x25, 0x07,        //   Logical Maximum (7)
    0x46, 0x3B, 0x01,  //   Physical Maximum (315)
    0x75, 0x04,        //   Report Size (4)
    0x95, 0x01,        //   Report Count (1)
    0x65, 0x14,        //   Unit (System: English Rotation, Length: Centimeter)
    0x09, 0x39,        //   Usage (Hat switch)
    0x81, 0x42,        //   Input (Data,Var,Abs,No Wrap,Linear,Preferred State,Null State)
    0x65, 0x00,        //   Unit (None)
    0x95, 0x01,        //   Report Count (1)
    0x81, 0x01,        //   Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
    0x26, 0xFF, 0x00,  //   Logical Maximum (255)
    0x46, 0xFF, 0x00,  //   Physical Maximum (255)
    0x09, 0x30,        //   Usage (X)
    0x09, 0x31,        //   Usage (Y)
    0x09, 0x32,        //   Usage (Z)
    0x09, 0x35,        //   Usage (Rz)
    0x75, 0x08,        //   Report Size (8)
    0x95, 0x04,        //   Report Count (4)
    0x81, 0x02,        //   Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
    0x75, 0x08,        //   Report Size (8)
    0x95, 0x01,        //   Report Count (1)
    0x81, 0x01,        //   Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
    0x0A, 0x4F, 0x48,  //   Usage (0x484F)
    0x75, 0x08,        //   Report Size (8)
    0x95, 0x08,        //   Report Count (8)
    0xB1, 0x02,        //   Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
    0x0A, 0x4F, 0x48,  //   Usage (0x484F)
    0x91, 0x02,        //   Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
    0xC0,              // End Collection
];

const POWER_NOTCHES: [u8; 6] = [0x80, 0x9F, 0xB7, 0xCE, 0xE6, 0xFF];
const BRAKE_NOTCHES: [u8; 10] = [0x80, 0x65, 0x57, 0x49, 0x3C, 0x2E, 0x20, 0x13, 0x05, 0x00];

bitflags! {
    struct Buttons1: u8 {
        const NONE = 0;
        const Y = 1;
        const B = 2;
        const A = 4;
        const X = 8;
        const L = 16;
        const R = 32;
        const ZL = 64;
    }
    struct Buttons2: u8 {
        const NONE = 0;
        const SELECT = 1;
        const START = 2;
        const HOME = 16;
    }
}

pub fn update_gadget(state: &mut ControllerState) {
    // Calculate data for handles
    let mut handle = POWER_NOTCHES[state.power as usize];
    if state.brake > 0 {
        handle = BRAKE_NOTCHES[state.brake as usize];
    }

    // Calculate data for buttons
    let mut buttons1 = Buttons1::NONE;
    let mut buttons2 = Buttons2::NONE;
    if !state.button_select_hold && state.button_select && state.button_left {
        buttons1.insert(Buttons1::L);
        state.combo = true;
    }
    if !state.button_select_hold && state.button_select && state.button_right {
        buttons1.insert(Buttons1::R);
        state.combo = true;
    }
    if !state.button_select_hold && state.button_start && state.button_select {
        buttons2.insert(Buttons2::HOME);
        state.combo = true;
    }
    if state.button_a { buttons1.insert(Buttons1::Y) }
    if state.button_b { buttons1.insert(Buttons1::B) }
    if state.button_c { buttons1.insert(Buttons1::A) }
    if state.button_d { buttons1.insert(Buttons1::X) }
    if state.brake == 9 { buttons1.insert(Buttons1::ZL) }
    if !state.combo && state.button_start { buttons2.insert(Buttons2::START) }
    if !state.combo && state.button_select_hold { buttons2.insert(Buttons2::SELECT) }

    // Calculate data for D-pad
    let mut dpad: u8 = 0xF;
    if state.button_up { dpad = 0x0 }
    if state.button_down { dpad = 0x4 }
    if !state.combo && state.button_left { dpad = 0x6 }
    if !state.combo && state.button_right { dpad = 0x2 }
    if !state.combo && state.button_up & state.button_left { dpad = 0x7 }
    if !state.combo && state.button_up & state.button_right { dpad = 0x1 }
    if !state.combo && state.button_down & state.button_left { dpad = 0x5 }
    if !state.combo && state.button_down & state.button_right { dpad = 0x3 }

    // Assemble data and send it to gadget
    let data = [buttons1.bits, buttons2.bits, dpad, 0x80, handle, 0x80, 0x80, 0x00];
    if let Ok(mut file) = File::create(ENDPOINT1) {
        file.write(&data).ok();
    }
}