#![allow(dead_code)]
pub mod weapon; 
pub mod armor; 
pub mod shield;
pub mod tool;
pub mod tech;
pub mod unit;
pub mod mag;
pub mod esweapon;

use serde::{Serialize, Deserialize};
use crate::entity::character::CharacterEntityId;
use crate::ship::map::MapArea;
use crate::ship::drops::ItemDropType;

#[derive(PartialEq, Copy, Clone, Debug, Hash, Eq, PartialOrd, Ord, Serialize, Deserialize)]
pub struct ItemEntityId(pub u32);
#[derive(Hash, PartialEq, Eq, Debug, Clone)]
pub struct ItemId(u32);
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
pub struct BankName(pub String);

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub enum ItemLocation {
    Inventory {
        character_id: CharacterEntityId,
    },
    Bank {
        character_id: CharacterEntityId,
        name: BankName,
    },
    LocalFloor {
        character_id: CharacterEntityId,
        map_area: MapArea,
        x: f32,
        y: f32,
        z: f32,
    },
    SharedFloor {
        map_area: MapArea,
        x: f32,
        y: f32,
        z: f32,
    },
    Consumed,
    FedToMag {
        mag: ItemEntityId,
    },
    Shop,
    Trade {
        //id: TradeId,
        character_to: CharacterEntityId,
        character_from: CharacterEntityId,
    },
    /*Destroyed {
        // marks an item that has been consumed in some way
    },
    Transformed {
        item_id,
        change_event
    }
*/
}

#[derive(Debug, Clone, PartialEq)]
pub struct Meseta(pub u32);

impl Meseta {
    pub fn as_bytes(&self) -> [u8; 16] {
        let mut result = [0; 16];
        result[0] = 4;
        result[12..16].copy_from_slice(&u32::to_le_bytes(self.0));
        result
    }
}


#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum ItemType {
    Weapon(weapon::WeaponType),
    Armor(armor::ArmorType),
    Shield(shield::ShieldType),
    Unit(unit::UnitType),
    Tool(tool::ToolType),
    TechniqueDisk(tech::Technique),
    Mag(mag::MagType),
    ESWeapon(esweapon::ESWeaponType),
}

#[derive(Clone, Debug, PartialEq)]
pub enum ItemParseError {
    InvalidBytes
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub enum ItemDetail {
    Weapon(weapon::Weapon),
    Armor(armor::Armor),
    Shield(shield::Shield),
    Unit(unit::Unit),
    Tool(tool::Tool),
    TechniqueDisk(tech::TechniqueDisk),
    Mag(mag::Mag),
    ESWeapon(esweapon::ESWeapon),
}

impl ItemDetail {
    pub fn is_stackable(&self) -> bool {
        match self {
            ItemDetail::Tool(tool) => tool.tool.is_stackable(),
            _ => false,
        }
    }

    pub fn item_type(&self) -> ItemType {
        match self {
            ItemDetail::Weapon(w) => ItemType::Weapon(w.weapon),
            ItemDetail::Armor(a) => ItemType::Armor(a.armor),
            ItemDetail::Shield(s) => ItemType::Shield(s.shield),
            ItemDetail::Unit(u) => ItemType::Unit(u.unit),
            ItemDetail::Tool(t) => ItemType::Tool(t.tool),
            ItemDetail::TechniqueDisk(d) => ItemType::TechniqueDisk(d.tech),
            ItemDetail::Mag(m) => ItemType::Mag(m.mag),
            ItemDetail::ESWeapon(e) => ItemType::ESWeapon(e.esweapon),
        }
    }

    pub fn parse_item_from_bytes(data: [u8; 16]) -> Option<ItemDropType> {
        let item_type = weapon::WeaponType::parse_type([data[0],data[1],data[2]]).map(ItemType::Weapon)
            .or_else(|_| armor::ArmorType::parse_type([data[0],data[1],data[2]]).map(ItemType::Armor))
            .or_else(|_| shield::ShieldType::parse_type([data[0],data[1],data[2]]).map(ItemType::Shield))
            .or_else(|_| unit::UnitType::parse_type([data[0],data[1],data[2]]).map(ItemType::Unit))
            .or_else(|_| mag::MagType::parse_type([data[0],data[1],data[2]]).map(ItemType::Mag))
            .or_else(|_| tool::ToolType::parse_type([data[0],data[1],data[2]]).map(ItemType::Tool))
            .or_else(|_| esweapon::ESWeaponType::parse_type([data[0],data[1],data[2]]).map(ItemType::ESWeapon)).ok()?;
            
        match item_type {
            ItemType::Weapon(_w) => Some(ItemDropType::Weapon(weapon::Weapon::from_bytes(data).ok()?)),
            ItemType::Armor(_a) => Some(ItemDropType::Armor(armor::Armor::from_bytes(data).ok()?)),
            ItemType::Shield(_s) => Some(ItemDropType::Shield(shield::Shield::from_bytes(data).ok()?)),
            ItemType::Unit(_u) => Some(ItemDropType::Unit(unit::Unit::from_bytes(data).ok()?)),
            ItemType::Mag(_m) => Some(ItemDropType::Mag(mag::Mag::from_bytes(data).ok()?)),
            ItemType::Tool(_t) => Some(ItemDropType::Tool(tool::Tool::from_bytes(data).ok()?)),
            _ => None,
        }
    }

    pub fn as_client_bytes(&self) -> [u8; 16] {
        match self {
            ItemDetail::Weapon(w) => w.as_bytes(),
            ItemDetail::Armor(a) => a.as_bytes(),
            ItemDetail::Shield(s) => s.as_bytes(),
            ItemDetail::Unit(u) => u.as_bytes(),
            ItemDetail::Tool(t) => t.as_individual_bytes(),
            ItemDetail::TechniqueDisk(d) => d.as_bytes(),
            ItemDetail::Mag(m) => m.as_bytes(),
            ItemDetail::ESWeapon(e) => e.as_bytes(),
        }
    }

    pub fn as_tool(self) -> Option<tool::Tool> {
        match self {
            ItemDetail::Tool(tool) => Some(tool),
            _ => None,
        }
    }
}

#[derive(Clone, Debug)]
pub struct NewItemEntity {
    pub location: ItemLocation,
    pub item: ItemDetail,
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct ItemEntity {
    pub id: ItemEntityId,
    pub location: ItemLocation,
    pub item: ItemDetail,
}


#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum InventoryItemEntity {
    Individual(ItemEntity),
    Stacked(Vec<ItemEntity>),
}

impl std::convert::From<ItemEntity> for InventoryItemEntity {
    fn from(item: ItemEntity) -> InventoryItemEntity {
        InventoryItemEntity::Individual(item)
    }
}

impl std::convert::From<Vec<ItemEntity>> for InventoryItemEntity {
    fn from(items: Vec<ItemEntity>) -> InventoryItemEntity {
        InventoryItemEntity::Stacked(items)
    }
}

impl InventoryItemEntity {
    pub fn map_individual<F: Fn(ItemEntity) -> ItemEntity>(self, func: F) -> InventoryItemEntity {
        match self {
            InventoryItemEntity::Individual(item) => InventoryItemEntity::Individual(func(item)),
            _ => self,
        }
    }
    //pub fn with_individual<T>(&self, func: fn(&ItemEntity) -> T) -> Option<T> {
    pub fn with_individual<F: Fn(&ItemEntity) -> T, T>(&self, func: F) -> Option<T> {
        match self {
            InventoryItemEntity::Individual(item) => Some(func(item)),
            _ => None,
        }
    }

    //pub fn with_stacked<T>(&self, func: fn(&Vec<ItemEntity>) -> T) -> Option<T> {
    pub fn with_stacked<F: Fn(&Vec<ItemEntity>) -> T, T>(&self, func: F) -> Option<T> {
        match self {
            InventoryItemEntity::Stacked(items) => Some(func(items)),
            _ => None,
        }
    }
}

#[derive(Clone, Debug, Default)]
pub struct EquippedEntity {
    pub weapon: Option<ItemEntityId>,
    pub armor: Option<ItemEntityId>,
    pub shield: Option<ItemEntityId>,
    pub unit: [Option<ItemEntityId>; 4],
    pub mag: Option<ItemEntityId>,
}

impl EquippedEntity {
    pub fn is_equipped(&self, item: &ItemEntityId) -> bool {
        self.weapon == Some(*item)
            || self.armor == Some(*item)
            || self.shield == Some(*item)
            || self.unit[0] == Some(*item)
            || self.unit[1] == Some(*item)
            || self.unit[2] == Some(*item)
            || self.unit[3] == Some(*item)
            || self.mag == Some(*item)
    }
}

#[derive(Clone, Debug, Default)]
pub struct InventoryEntity {
    pub items: Vec<InventoryItemEntity>,
}

impl InventoryEntity {
    pub fn new<T: Into<InventoryItemEntity>>(items: Vec<T>) -> InventoryEntity {
        InventoryEntity {
            items: items.into_iter().map(|i| i.into()).collect(),
        }
    }
}


#[derive(Clone, Debug)]
pub enum BankItemEntity {
    Individual(ItemEntity),
    Stacked(Vec<ItemEntity>),
}

impl std::convert::From<ItemEntity> for BankItemEntity {
    fn from(item: ItemEntity) -> BankItemEntity {
        BankItemEntity::Individual(item)
    }
}

impl std::convert::From<Vec<ItemEntity>> for BankItemEntity {
    fn from(items: Vec<ItemEntity>) -> BankItemEntity {
        BankItemEntity::Stacked(items)
    }
}

impl BankItemEntity {
    pub fn with_individual<T>(&self, func: fn(&ItemEntity) -> T) -> Option<T> {
        match self {
            BankItemEntity::Individual(item) => Some(func(item)),
            _ => None,
        }
    }

    pub fn with_stacked<T>(&self, func: fn(&Vec<ItemEntity>) -> T) -> Option<T> {
        match self {
            BankItemEntity::Stacked(items) => Some(func(items)),
            _ => None,
        }
    }
}


#[derive(Clone, Debug, Default)]
pub struct BankEntity {
    //pub items: [Option<CharacterBankItem>; 30],
    pub items: Vec<BankItemEntity>,
}

impl BankEntity {
    pub fn new<T: Into<BankItemEntity>>(items: Vec<T>) -> BankEntity {
        BankEntity {
            items: items.into_iter().map(|i| i.into()).collect(),
        }
    }
}