use libpso::character::character;
use crate::common::leveltable::CharacterStats;
use crate::entity::character::CharacterEntity;
//use crate::ship::items::{CharacterInventory, CharacterBank};
use crate::ship::items::bank::BankState;
use crate::ship::items::inventory::InventoryState;
use crate::entity::item::Meseta;


#[derive(Default)]
pub struct CharacterBytesBuilder<'a> {
    character: Option<&'a CharacterEntity>,
    stats: Option<&'a CharacterStats>,
    level: Option<u32>,
    meseta: Option<Meseta>,
}

impl<'a> CharacterBytesBuilder<'a> {
    #[must_use]
    pub fn character(self, character: &'a CharacterEntity) -> CharacterBytesBuilder<'a> {
        CharacterBytesBuilder {
            character: Some(character),
            ..self
        }
    }

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

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

    #[must_use]
    pub fn meseta(self, meseta: Meseta) -> CharacterBytesBuilder<'a> {
        CharacterBytesBuilder {
            meseta: Some(meseta),
            ..self
        }
    }

    pub fn build(self) -> character::Character {
        let character = self.character.unwrap();
        let stats = self.stats.unwrap();
        let level = self.level.unwrap();
        let meseta = self.meseta.unwrap();
        character::Character {
            name: libpso::utf8_to_utf16_array!(character.name, 16),
            hp: stats.hp,
            atp: stats.atp + character.materials.power as u16 * 2,
            mst: stats.mst + character.materials.mind as u16 * 2,
            evp: stats.evp + character.materials.evade as u16 * 2,
            dfp: stats.dfp + character.materials.def as u16 * 2,
            ata: stats.ata,
            lck: stats.lck + character.materials.luck as u16 * 2,
            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(),
            meseta: meseta.0 as u32,
            exp: character.exp,
            ..character::Character::default()
        }
    }
}

#[derive(Default)]
pub struct FullCharacterBytesBuilder<'a> {
    character: Option<&'a CharacterEntity>,
    stats: Option<&'a CharacterStats>,
    level: Option<u32>,
    meseta: Option<Meseta>,
    inventory: Option<&'a InventoryState>,
    bank: Option<&'a BankState>,
    keyboard_config: Option<&'a [u8; 0x16C]>,
    gamepad_config: Option<&'a [u8; 0x38]>,
    symbol_chat: Option<&'a [u8; 1248]>,
    tech_menu: Option<&'a [u8; 40]>,
    option_flags: Option<u32>,
}

impl<'a> FullCharacterBytesBuilder<'a> {
    #[must_use]
    pub fn character(self, character: &'a CharacterEntity) -> FullCharacterBytesBuilder<'a> {
        FullCharacterBytesBuilder {
            character: Some(character),
            ..self
        }
    }

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

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

    #[must_use]
    pub fn meseta(self, meseta: Meseta) -> FullCharacterBytesBuilder<'a> {
        FullCharacterBytesBuilder {
            meseta: Some(meseta),
            ..self
        }
    }

    #[must_use]
    pub fn inventory(self, inventory: &'a InventoryState) -> FullCharacterBytesBuilder<'a> {
        FullCharacterBytesBuilder {
            inventory: Some(inventory),
            ..self
        }
    }

    #[must_use]
    pub fn bank(self, bank: &'a BankState) -> FullCharacterBytesBuilder<'a> {
        FullCharacterBytesBuilder {
            bank: Some(bank),
            ..self
        }
    }

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

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

    #[must_use]
    pub fn symbol_chat(self, symbol_chat: &'a [u8; 1248]) -> FullCharacterBytesBuilder<'a> {
        FullCharacterBytesBuilder {
            symbol_chat: Some(symbol_chat),
            ..self
        }
    }

    #[must_use]
    pub fn tech_menu(self, tech_menu: &'a [u8; 40]) -> FullCharacterBytesBuilder<'a> {
        FullCharacterBytesBuilder {
            tech_menu: Some(tech_menu),
            ..self
        }
    }

    #[must_use]
    pub fn option_flags(self, option_flags: u32) -> FullCharacterBytesBuilder<'a> {
        FullCharacterBytesBuilder {
            option_flags: Some(option_flags),
            ..self
        }
    }

    pub fn build(self) -> character::FullCharacter {
        let character = self.character.unwrap();
        let stats = self.stats.unwrap();
        let level = self.level.unwrap();
        let meseta = self.meseta.unwrap();
        let inventory = self.inventory.unwrap();
        let bank = self.bank.unwrap();
        let keyboard_config = self.keyboard_config.unwrap();
        let gamepad_config = self.gamepad_config.unwrap();
        let symbol_chat = self.symbol_chat.unwrap();
        let tech_menu = self.tech_menu.unwrap();
        let option_flags = self.option_flags.unwrap();

        let mut inventory_items = inventory.as_client_inventory_items();
        inventory_items[7].material_count = character.materials.power as u8;
        inventory_items[8].material_count = character.materials.mind as u8;
        inventory_items[9].material_count = character.materials.evade as u8;
        inventory_items[10].material_count = character.materials.def as u8;
        inventory_items[11].material_count = character.materials.luck as u8;

        character::FullCharacter {
            character: CharacterBytesBuilder::default()
                .character(character)
                .stats(stats)
                .level(level - 1)
                .meseta(meseta)
                .build(),
            inventory: character::Inventory {
                item_count: inventory.count() as u8,
                items: inventory_items,
                hp_mats_used: character.materials.hp as u8 * 2,
                tp_mats_used: character.materials.tp as u8 * 2,
                ..character::Inventory::default()
            },
            key_team_config: character::KeyTeamConfig {
                keyboard_config: *keyboard_config,
                gamepad_config: *gamepad_config,
                ..character::KeyTeamConfig::default()
            },
            info_board: character.info_board.as_bytes(),
            symbol_chats: *symbol_chat,
            tech_menu: *tech_menu,
            bank: bank.as_client_bank_items(),
            option_flags,
            ..character::FullCharacter::default()
        }
    }
}