use libpso::character::character;
use crate::common::leveltable::CharacterStats;
use crate::entity::character::Character;
use crate::ship::items::ActiveInventory;

// TODO: exp
pub struct CharacterBytesBuilder<'a> {
    character: Option<&'a Character>,
    stats: Option<&'a CharacterStats>,
    level: Option<u32>,
}


impl<'a> CharacterBytesBuilder<'a> {
    pub fn new() -> CharacterBytesBuilder<'a> {
        CharacterBytesBuilder {
            character: None,
            stats: None,
            level: None,
        }
    }

    pub fn character(self, character: &'a Character) -> CharacterBytesBuilder<'a> {
        CharacterBytesBuilder {
            character: Some(character),
            ..self
        }
    }

    pub fn stats(self, stats: &'a CharacterStats) -> CharacterBytesBuilder<'a> {
        CharacterBytesBuilder {
            stats: Some(stats),
            ..self
        }
    }

    pub fn level(self, level: u32) -> CharacterBytesBuilder<'a> {
        CharacterBytesBuilder {
            level: Some(level),
            ..self
        }
    }

    pub fn build(self) -> character::Character {
        let character = self.character.unwrap();
        let stats = self.stats.unwrap();
        let level = self.level.unwrap();
        character::Character {
            name: libpso::utf8_to_utf16_array!(character.name, 16),
            hp: stats.hp,
            atp: stats.atp,
            mst: stats.mst,
            evp: stats.evp,
            dfp: stats.dfp,
            ata: stats.ata,
            lck: stats.lck,
            level: level,
            section_id: character.section_id.into(),
            ch_class: character.char_class.into(),
            costume: character.appearance.costume,
            skin: character.appearance.skin,
            face: character.appearance.face,
            head: character.appearance.head,
            hair: character.appearance.hair,
            hair_r: character.appearance.hair_r,
            hair_g: character.appearance.hair_g,
            hair_b: character.appearance.hair_b,
            prop_x: character.appearance.prop_x,
            prop_y: character.appearance.prop_y,
            config: character.config.as_bytes(),
            techniques: character.techs.as_bytes(),
            ..character::Character::default()
        }
    }
}


pub struct FullCharacterBytesBuilder<'a> {
    character: Option<&'a Character>,
    stats: Option<&'a CharacterStats>,
    level: Option<u32>,
    inventory: Option<&'a ActiveInventory>,
    key_config: Option<&'a [u8; 0x16C]>,
    joystick_config: Option<&'a [u8; 0x38]>,
}


impl<'a> FullCharacterBytesBuilder<'a> {
    pub fn new() -> FullCharacterBytesBuilder<'a> {
        FullCharacterBytesBuilder {
            character: None,
            stats: None,
            level: None,
            inventory: None,
            key_config: None,
            joystick_config: None,
        }
    }

    pub fn character(self, character: &'a Character) -> FullCharacterBytesBuilder<'a> {
        FullCharacterBytesBuilder {
            character: Some(character),
            ..self
        }
    }

    pub fn stats(self, stats: &'a CharacterStats) -> FullCharacterBytesBuilder<'a> {
        FullCharacterBytesBuilder {
            stats: Some(stats),
            ..self
        }
    }

    pub fn level(self, level: u32) -> FullCharacterBytesBuilder<'a> {
        FullCharacterBytesBuilder {
            level: Some(level),
            ..self
        }
    }

    pub fn inventory(self, inventory: &'a ActiveInventory) -> FullCharacterBytesBuilder<'a> {
        FullCharacterBytesBuilder {
            inventory: Some(inventory),
            ..self
        }
    }

    pub fn key_config(self, key_config: &'a [u8; 0x16C]) -> FullCharacterBytesBuilder<'a> {
        FullCharacterBytesBuilder {
            key_config: Some(key_config),
            ..self
        }
    }

    pub fn joystick_config(self, joystick_config: &'a [u8; 0x38]) -> FullCharacterBytesBuilder<'a> {
        FullCharacterBytesBuilder {
            joystick_config: Some(joystick_config),
            ..self
        }
    }


    pub fn build(self) -> character::FullCharacter {
        let character = self.character.unwrap();
        let stats = self.stats.unwrap();
        let level = self.level.unwrap();
        let inventory = self.inventory.unwrap();
        let key_config = self.key_config.unwrap();
        let joystick_config = self.joystick_config.unwrap();

        character::FullCharacter {
            character: CharacterBytesBuilder::new()
                .character(&character)
                .stats(&stats)
                .level(level - 1)
                .build(),
            inventory: character::Inventory {
                item_count: inventory.count() as u8,
                items: inventory.as_client_inventory_items(),
                ..character::Inventory::default()
            },
            key_team_config: character::KeyTeamConfig {
                key_config: *key_config,
                joystick_config: *joystick_config,
                ..character::KeyTeamConfig::default()
            },
            info_board: character.info_board.as_bytes(),
            ..character::FullCharacter::default()
        }
    }
}