use crate::types::{helper::{InterleavedXASector, InterleavedXASizes}, RawData};
use cdtypes::types::{helper::*, sector::*};

pub struct SubModeBuilder {
    sub_mode: SubMode
}

impl SubModeBuilder {
    pub fn new_mode1() -> Self {
        SubModeBuilder{sub_mode: SubMode::default_form1()}
    }

    pub fn set_eor(mut self) -> Self {
        self.sub_mode.set_eor();
        self
    }

    pub fn set_eof(mut self) -> Self {
        self.sub_mode.set_eof();
        self
    }

    pub fn create(self) -> SubMode {
        self.sub_mode
    }
}

pub fn create_audio_for_vec(audio_samples: &Vec<AudioSample>) -> Vec<Audio> {
    let     samples_to_parse = sector_count_audio(audio_samples.len()*std::mem::size_of::<AudioSample>());
    let mut samples          = &audio_samples[0..audio_samples.len()];
    let mut parsed_samples   = Vec::new();

    for _ in 0..samples_to_parse {
        let mut audio = Audio::new();

        samples = copy_array(&mut audio.samples, samples);
        parsed_samples.push(audio);
    }
    parsed_samples
}

pub fn create_xa_data_for_raw(mut sub_mode: SubMode, data: &[u8; Mode2Form1::DATA_SIZE]) -> Mode2Form1 {
    let mut sector = Mode2Form1::new();

    sub_mode.set_data();

    sector.sub_header.sub_mode = sub_mode;
    sector.data = *data;
    sector
}

pub fn create_xa_audio_for_raw(mut sub_mode: SubMode, coding_info: CodingInfo, channel: u8, data: &[u8; Mode2Form2::DATA_SIZE]) -> Mode2Form2 {
    let mut sector = Mode2Form2::new();

    sub_mode.set_audio();

    sector.sub_header.file_number    = 1;
    sector.sub_header.channel_number = channel;
    sector.sub_header.sub_mode       = sub_mode;
    sector.sub_header.coding_info    = coding_info;
    sector.data                      = *data;
    sector
}

pub fn create_xa_data_for<T>(sub_mode: SubMode, data: &T) -> Mode2Form1 {
    create_xa_data_for_raw(sub_mode, unsafe {std::mem::transmute::<&T, &[u8; Mode2Form1::DATA_SIZE]>(data)})
}

pub fn create_xa_data_for_vec(sub_mode: Option<SubMode>, data: &RawData, fill_bytes: u8) -> Vec<Mode2Form1> {
    let     sectors_to_parse = sector_count_mode2_form1(data.len());
    let mut data             = &data[0..data.len()];
    let mut sectors          = vec![Mode2Form1::new(); sectors_to_parse];
    let     sub_mode         = {
        if let Some(sub_mode) = sub_mode {
            sub_mode
        }

        else {
            SubMode::default_form1()
        }
    };

    for cur_sector in 0..sectors_to_parse {
        let mut raw_data = [fill_bytes; Mode2Form1::DATA_SIZE];
        let     sub_mode = {
            let mut sub_mode = sub_mode.clone();

            if cur_sector + 1 == sectors_to_parse {
                sub_mode.set_eof();
                sub_mode.set_eor();
            }

            sub_mode
        };

        data                = copy_array(&mut raw_data, data);
        sectors[cur_sector] = create_xa_data_for_raw(sub_mode, &raw_data);
    }

    sectors
}

pub fn create_xa_audio_for(data: &Vec<RawData>) -> Vec<InterleavedXASector> {
    let     (channel_count, sectors_to_parse) = {
        let size_info = InterleavedXASizes::new(data);
        (size_info.channels, size_info.biggest_file_sectors)
    };
    let mut channels         = {
        let mut new_channel = Vec::new();
        for channel in data {
            new_channel.push(&channel[0..channel.len()]);
        }
        new_channel
    };
    let mut sectors       = vec![InterleavedXASector::empty(); sectors_to_parse*channel_count];
    let mut cur_sector_id = 0;

    for _sector_id in 0..sectors_to_parse {
        let mut channel_id = 0;
        for channel in &mut channels {
            let sector = {
                if channel.is_empty() {
                    InterleavedXASector::data({
                        let mut sector = create_xa_data_zero();
                        sector.sub_header.channel_number = channel_id;
                        sector
                    })
                }

                else {
                    let mut sub_header = SubHeader::default();
                    let mut raw_data   = [0u8; Mode2Form2::DATA_SIZE];

                    //                  v Skip sync and header
                    *channel = &channel[0x10..channel.len()];
                    *channel = copy_object(&mut sub_header, &channel);
                    //                  v Copy of sub-header
                    *channel = &channel[0x4..channel.len()];
                    *channel = copy_array(&mut raw_data, &channel);
                    //                  v Skip EDC
                    *channel = &channel[0x4..channel.len()];
                    
                    let sub_mode = {
                        let mut sub_mode = SubMode::default_form2();
                        sub_mode.set_real_time();

                        sub_mode
                    };
                    InterleavedXASector::audio(create_xa_audio_for_raw(sub_mode, sub_header.coding_info, channel_id, &raw_data))
                }
            };
            
            sectors[cur_sector_id] = sector;
            channel_id    += 1;
            cur_sector_id += 1;
        }
    }

    for channel in channels {
        if !channel.is_empty() {
            tool_helper::print_warning(format!("Channel not fully encoded!"));
        }
    }

    sectors
}

pub fn create_mode0_zero() -> Mode0 {
    Mode0::new()
}

pub fn create_xa_data_zero() -> Mode2Form1 {
    create_xa_data_for_raw(SubMode::default_form1(), &[0u8; Mode2Form1::DATA_SIZE])
}

pub fn create_xa_audio_zero(channel: u8) -> Mode2Form2 {
    create_xa_audio_for_raw(SubMode::default_form2(), CodingInfo::default_form2(), channel, &[0u8; Mode2Form2::DATA_SIZE])
}

fn copy_object<'a, T, S>(dst: &mut T, src: &'a [S]) -> &'a [S] {
    let data_size = std::mem::size_of::<T>();
    unsafe{
        let dst:*mut T = &mut *dst;
        std::ptr::copy_nonoverlapping(src.as_ptr(), dst as *mut S, data_size)
    };
    &src[data_size..src.len()]
}

fn copy_array<'a, T, const SIZE:usize>(dst: &mut [T; SIZE], src: &'a [T]) -> &'a [T] {
    let data_size = {
        if src.len() > SIZE {
            SIZE
        }

        else {
            src.len()
        }
    };

    unsafe{std::ptr::copy_nonoverlapping(src.as_ptr(), dst.as_mut_ptr(), data_size)};
    &src[data_size..src.len()]
}