pub mod consts;

// Main Items
const MN_INPUT            :u8 = 0b1000_00_00;
const MN_OUTPUT           :u8 = 0b1001_00_00;
const MN_FEATURE          :u8 = 0b1011_00_00;
const MN_COLLECTION_BEGIN :u8 = 0b1010_00_00;
const MN_COLLECTION_END   :u8 = 0b1100_00_00;
const MN_EXTENDED         :u8 = 0b1111_11_00;

// Global Items
const GB_USAGE_PAGE    :u8 = 0b0000_01_00;
const GB_LOGICAL_MIN   :u8 = 0b0001_01_00;
const GB_LOGICAL_MAX   :u8 = 0b0010_01_00;
const GB_PHYSICAL_MIN  :u8 = 0b0011_01_00;
const GB_PHYSICAL_MAX  :u8 = 0b0100_01_00;
const GB_UNIT_EXPONENT :u8 = 0b0101_01_00;
const GB_UNIT          :u8 = 0b0110_01_00;
const GB_REPORT_SIZE   :u8 = 0b0111_01_00;
const GB_REPORT_ID     :u8 = 0b1000_01_00;
const GB_REPORT_COUNT  :u8 = 0b1001_01_00;
const GB_PUSH          :u8 = 0b1010_01_00;
const GB_POP           :u8 = 0b1011_01_00;

// Local Items
const LC_USAGE            :u8 = 0b0000_10_00;
const LC_USAGE_MIN        :u8 = 0b0001_10_00;
const LC_USAGE_MAX        :u8 = 0b0010_10_00;
const LC_DESIGNATOR_INDEX :u8 = 0b0011_10_00;
const LC_DESIGNATOR_MIN   :u8 = 0b0100_10_00;
const LC_DESIGNATOR_MAX   :u8 = 0b0101_10_00;
const LC_STRING_INDEX     :u8 = 0b0111_10_00;
const LC_STRING_MIN       :u8 = 0b1000_10_00;
const LC_STRING_MAX       :u8 = 0b1001_10_00;
const LC_DELIMITER        :u8 = 0b1010_10_00;

#[derive(Clone, Copy)]
struct GlobalState {
    pub usage_page:u16,
    pub logical_min:i32,
    pub logical_max:i32,
    pub physical_min:i32,
    pub physical_max:i32,
    pub unit_exponent:i32,
    pub unit:u32,
    pub report_id:u32,
    pub report_size:u32,
    pub report_count:u32,
}
impl GlobalState {
    pub fn new() -> Self
    {
        Self {
            usage_page:0,
            logical_min:0,
            logical_max:0,
            physical_min:0,
            physical_max:0,
            unit_exponent:0,
            unit:0,
            report_id:0,
            report_size:0,
            report_count:0,
        }
    }
}

struct LocalState {
    pub usages:Vec<u32>,
    pub usage_range:[u32;2],
}
impl LocalState {
    pub fn new() -> Self
    {
        Self {
            usages:Vec::new(),
            usage_range:[0, 0],
        }
    }
}

struct Collection {
    pub usage:u32,
    pub index:u32,
}

pub struct Interface {
    pub usage_page:u16,
    pub usage:u16,
    pub collection:u16,
    pub index:u16,

    working_value:i32,
    pub value:i32,
}

pub struct Field {
    container:u8,

    is_const:bool,
    is_var:bool,
    is_rel:bool,
    is_wrap:bool,
    is_linear:bool,
    has_pref:bool,
    has_null:bool,
    is_volatile:bool,
    is_buffered:bool,

    report_size:usize,
    report_count:usize,

    logical_min:i32,
    logical_max:i32,
    physical_min:i32,
    physical_max:i32,
    unit_exponent:i32,
    unit:u32,

    interfaces:Vec<usize>,

    bit_offset:usize,
}

pub struct Report {
    pub id:u32,
    pub length:usize,
    pub fields:Vec<Field>,
}

pub struct ReportDescriptor {
    pub interfaces:Vec<Interface>,
    pub inputs:Vec<usize>,
    pub outputs:Vec<usize>,
    pub features:Vec<usize>,
    pub reports:Vec<Report>,

    has_id:bool,
}
impl ReportDescriptor {
    pub fn parse(bytes:&[u8]) -> Result<Self,()>
    {
        // Output
        let mut descriptor = Self {
            interfaces:Vec::new(),
            inputs:Vec::new(),
            outputs:Vec::new(),
            features:Vec::new(),
            reports:Vec::new(),

            has_id:false,
        };

        // Global State
        let mut global_state = Vec::<GlobalState>::new();
        global_state.push(GlobalState::new());

        // Local State
        let mut local_state = LocalState::new();

        // Tracking
        let mut collection_stack = vec![Collection { usage:0, index:0 }];
        let mut collection_index = 0;
        //let mut delimiter = false;

        let mut index  = 0;
        while index < bytes.len() {
            let mut sz = bytes[index] & 0x03;
            let mut tg = bytes[index] & 0xFC;

            // Get byte size from index
            sz = [ 0, 1, 2, 4 ][sz as usize];

            index += 1;

            if bytes.len() - index >= sz as usize {
                let global_state_index = global_state.len() - 1;
                let collection_stack_index = collection_stack.len() - 1;

                // Handle long tag
                if sz == 2 && tg == MN_EXTENDED {
                    sz = bytes[index + 1];
                    tg = bytes[index + 2];

                    if bytes.len() - index < sz as usize {
                        return Err(());
                    }
                }
                
                if sz <= 4 {
                    let udata = Self::unpack(&bytes, &index, sz as usize);
                    let idata = Self::unpack_signed(&bytes, &index, sz as usize);

                    match tg {
                        MN_INPUT | MN_OUTPUT | MN_FEATURE => {
                            /*
                            ** 0: Data(0), Constant(1)
                            ** 1: Array(0), Variable(1)
                            ** 2: Absolute(0), Relative(1)
                            ** 3: NoWrap(0), Wrap(1)
                            ** 4: Linear(0), NonLinear(1)
                            ** 5: PreferredState(0), NoPreferred(1)
                            ** 6: NoNull(0), NullState(1)
                            ** 7: reserved
                            ** 8: BitField(0), BufferedBytes(1)
                            ** _: reserved
                            */

                            let mut field = Field {
                                container:tg,

                                is_const:(udata & 0x01) != 0,
                                is_var:(udata & 0x02) != 0,
                                is_rel:(udata & 0x04) != 0,
                                is_wrap:(udata & 0x04) != 0,
                                is_linear:(udata & 0x04) != 0,
                                has_pref:(udata & 0x04) != 0,
                                has_null:(udata & 0x04) != 0,
                                is_volatile:(udata & 0x04) != 0,
                                is_buffered:(udata & 0x04) != 0,
                                
                                report_size:global_state[global_state_index].report_size as usize,
                                report_count:global_state[global_state_index].report_count as usize,

                                logical_min:global_state[global_state_index].logical_min,
                                logical_max:global_state[global_state_index].logical_max,
                                physical_min:global_state[global_state_index].physical_min,
                                physical_max:global_state[global_state_index].physical_max,
                                unit_exponent:global_state[global_state_index].unit_exponent,
                                unit:global_state[global_state_index].unit,

                                interfaces:Vec::new(),

                                bit_offset:0,
                            };

                            // Add data interfaces to descriptor.
                            if !field.is_const {
                                let interface_count = if field.is_var {
                                    global_state[global_state_index].report_count as usize
                                } else {
                                    if local_state.usage_range != [0; 2] {
                                        (local_state.usage_range[1] - local_state.usage_range[0]) as usize
                                    } else {
                                        local_state.usages.len() as usize
                                    }
                                };

                                for i in 0..interface_count {

                                    let mut usage_index = 0;
                                    let usage = if local_state.usage_range != [0; 2] {
                                        if (i as u32) <= local_state.usage_range[1] - local_state.usage_range[0] {
                                            local_state.usage_range[0] + i as u32
                                        } else {
                                            usage_index += 1;
                                            local_state.usage_range[1]
                                        }
                                    } else if i < local_state.usages.len() {
                                        local_state.usages[i]
                                    } else if local_state.usages.len() > 0 {
                                        let index = local_state.usages.len() - 1;
                                        usage_index += 1;
                                        local_state.usages[index]
                                    } else {
                                        0
                                    };

                                    let usage_page = if usage <= 0xFFFF {
                                        global_state[global_state_index].usage_page
                                    } else {
                                        (usage >> 16) as u16
                                    };

                                    let usage = usage as u16;
                                    let collection = collection_stack[collection_stack_index].index as u16;

                                    // Check if interface already exists
                                    let mut existing_index = None;
                                    for i in 0..descriptor.interfaces.len() {
                                        if descriptor.interfaces[i].usage_page == usage_page
                                        && descriptor.interfaces[i].usage == usage
                                        && descriptor.interfaces[i].collection == collection
                                        && descriptor.interfaces[i].index == usage_index
                                        {
                                            existing_index = Some(i);
                                            break;
                                        }
                                    }

                                    if let Some(existing_index) = existing_index {
                                        field.interfaces.push(existing_index);
                                    } else {
                                        let interface = Interface {
                                            usage_page,
                                            usage:usage,
                                            collection,
                                            index:usage_index,

                                            working_value:0,
                                            value:0,
                                        };

                                        descriptor.interfaces.push(interface);

                                        let id = descriptor.interfaces.len() - 1;
                                        field.interfaces.push(id);

                                        match tg {
                                            MN_OUTPUT => { descriptor.outputs.push(id); }
                                            MN_FEATURE => { descriptor.features.push(id); }
                                            _ => { descriptor.inputs.push(id); }
                                        }
                                    }
                                }
                            }

                            let report_index = if let Some(report_index) = descriptor.find_report(global_state[global_state_index].report_id) {
                                report_index
                            } else {
                                descriptor.reports.push(Report {
                                    id:global_state[global_state_index].report_id,
                                    length:0,
                                    fields:Vec::new(),
                                });
                                descriptor.reports.len() - 1
                            };

                            // Add interfaces to report.
                            let report = &mut descriptor.reports[report_index];

                            let bit_offset = if let Some(field) = report.fields.last() {
                                field.bit_offset + field.report_size
                            } else { 0 };

                            field.bit_offset = bit_offset;
                            //field.bit_length = global_state[global_state_index].report_size as usize;

                            report.length += field.report_size * field.report_count;
                            report.fields.push(field);

                            local_state = LocalState::new();
                        }

                        MN_COLLECTION_BEGIN => {
                            /*
                            ** 00: Physical
                            ** 01: Application
                            ** 02: Logical
                            ** 03: Report
                            ** 04: NamedArray
                            ** 05: UsageSwitch
                            ** 06: UsageModifier
                            ** 07-7F: reserved
                            ** 80-FF: vendor-defined
                            */
                            
                            collection_stack.push(Collection {
                                usage:udata,
                                index:collection_index,
                            });
                            collection_index += 1;
                            local_state = LocalState::new();
                        }

                        MN_COLLECTION_END => {
                            collection_stack.pop();
                            local_state = LocalState::new();
                        }

                        GB_USAGE_PAGE => {
                            global_state[global_state_index].usage_page = udata as u16;
                        }

                        GB_LOGICAL_MIN => {
                            global_state[global_state_index].logical_min = idata;
                        }

                        GB_LOGICAL_MAX => {
                            global_state[global_state_index].logical_max = idata;
                        }

                        GB_PHYSICAL_MIN => {
                            global_state[global_state_index].physical_min = idata;
                        }

                        GB_PHYSICAL_MAX => {
                            global_state[global_state_index].physical_max = idata;
                        }

                        GB_UNIT_EXPONENT => {
                            global_state[global_state_index].unit_exponent = idata;
                        }

                        GB_UNIT => {
                            global_state[global_state_index].unit = udata;
                        }

                        GB_REPORT_SIZE => {
                            global_state[global_state_index].report_size = udata;
                        }

                        GB_REPORT_ID => {
                            descriptor.has_id = true;
                            global_state[global_state_index].report_id = udata;

                            // Create new report if id not found.
                            if descriptor.find_report(udata).is_none() {
                                descriptor.reports.push(Report {
                                    id:udata,
                                    length:0,
                                    fields:Vec::new(),
                                });
                            }
                        }

                        GB_REPORT_COUNT => {
                            global_state[global_state_index].report_count = udata;
                        }

                        GB_PUSH => {
                            global_state.push(global_state[global_state.len() - 1]);
                        }

                        GB_POP => {
                            if global_state.len() > 1 {
                                global_state.pop();
                            }
                        }

                        LC_USAGE => {
                            local_state.usages.push(udata);
                        }

                        LC_USAGE_MIN => {
                            local_state.usage_range[0] = udata;
                        }

                        LC_USAGE_MAX => {
                            local_state.usage_range[1] = udata;
                        }

                        LC_DESIGNATOR_INDEX => {

                        }

                        LC_DESIGNATOR_MIN => {

                        }

                        LC_DESIGNATOR_MAX => {

                        }

                        LC_STRING_INDEX => {

                        }

                        LC_STRING_MIN => {

                        }

                        LC_STRING_MAX => {

                        }

                        LC_DELIMITER => {

                        }

                        _ => { return Err(()); }
                    }
                }

                index += sz as usize;
            }
        }

        Ok(descriptor)
    }

    pub fn read_report(&mut self, bytes:&[u8]) -> Result<Vec<usize>,()>
    {
        let mut start = 0;

        if bytes.len() == 0 { return Err(()); }

        // Read report id
        let report_id = if self.has_id {
            start += 1;
            bytes[0]
        } else {
            0
        } as u32;

        if let Some(report_index) = self.find_report(report_id) {
            let report = &self.reports[report_index];
            let mut changes = Vec::new();

            for field in &report.fields {

                // Set interface working values to zero.
                for inf in &field.interfaces {
                    self.interfaces[*inf].working_value = 0;
                }

                // Get report values.
                for ri in 0..field.report_count {
                    let bit_offset = field.bit_offset + (ri * field.report_size);

                    let byte = bit_offset / 8;
                    let bit = bit_offset % 8;
                    let byte_count = ((bit_offset + field.report_size) / 8) - byte;

                    let mut value = (bytes[start + byte] as i64) >> bit;
                    for i in 1..byte_count {
                        value |= (bytes[start + byte + i] as i64) << ((i * 8) - bit);
                    }

                    if value >= field.logical_min as i64 && value <= field.logical_max as i64 {
                        if field.is_var {
                            let interface = &mut self.interfaces[field.interfaces[ri]];
                            
                            // Normalize value
                            if field.physical_min != 0 || field.physical_max != 0 {
                                let domain = (field.logical_max - field.logical_min) as i64;
                                let range = (field.physical_max - field.physical_min) as i64;
                                let basis = 1 << 24;

                                interface.working_value = field.physical_min + (((value - field.logical_min as i64) * (range * basis)) / domain) as i32;
                            }
                        } else {
                            let interface = &mut self.interfaces[field.interfaces[value as usize]];
                            interface.working_value = 1 << 24;
                        }
                    }
                }

                // Set interface working values to zero.
                for inf in &field.interfaces {
                    if self.interfaces[*inf].working_value != self.interfaces[*inf].value {
                        self.interfaces[*inf].value = self.interfaces[*inf].working_value;
                        changes.push(*inf);
                    }
                }
            }

            Ok(changes)
        } else {
            Err(())
        }
    }

    pub fn write_report(&self) -> Vec<u8>
    {
        Vec::new()
    }

    fn find_report(&self, id:u32) -> Option<usize>
    {
        let mut ri = 0;
        while ri < self.reports.len() {
            if self.reports[ri].id == id { return Some(ri); }
            ri += 1;
        }
        None
    }

    fn unpack(data:&[u8], index:&usize, size:usize) -> u32
    {
        let mut result = 0u32;
        if data.len() - *index >= size {
            match size {
                1 => {
                    result = data[*index] as u32;
                }

                2 => {
                    result = (data[*index] as u32)
                    | ((data[*index + 1] as u32) << 8);
                }
                
                4 => {
                    result = (data[*index] as u32)
                    | ((data[*index + 1] as u32) << 8)
                    | ((data[*index + 2] as u32) << 16)
                    | ((data[*index + 3] as u32) << 24);
                }

                _ => { }
            }
        }
        result
    }

    fn unpack_signed(data:&[u8], index:&usize, size:usize) -> i32
    {
        let mut result = 0i32;
        if data.len() - *index >= size {
            match size {
                1 => {
                    result = data[*index] as i32;
                }

                2 => {
                    result = (data[*index] as i32)
                    | ((data[*index + 1] as i32) << 8);
                }
                
                4 => {
                    result = (data[*index] as i32)
                    | ((data[*index + 1] as i32) << 8)
                    | ((data[*index + 2] as i32) << 16)
                    | ((data[*index + 3] as i32) << 24);
                }

                _ => { }
            }
        }
        result
    }
}
