mod drop_table;
mod rare_drop_table;
mod generic_weapon;
mod generic_armor;
mod generic_shield;
mod generic_unit;


use std::collections::HashMap;
use std::fs::File;
use std::path::PathBuf;
use std::io::Read;
use serde::{Serialize, Deserialize};
use rand::{Rng, SeedableRng};
use rand::distributions::{WeightedIndex, Distribution};

use crate::ship::monster::MonsterType;
use crate::ship::room::{Difficulty, Episode};
use crate::entity::item::ItemDetail;
use crate::entity::item::weapon::{WeaponType, Attribute, WeaponAttribute, WeaponSpecial};
use crate::entity::item::armor::ArmorType;
use crate::entity::item::shield::ShieldType;
use crate::entity::item::unit::UnitType;
use crate::entity::item::tool::ToolType;
use crate::ship::map::MapVariantType;
use crate::entity::character::SectionID;
use crate::ship::drops::generic_weapon::GenericWeaponTable;
use crate::ship::drops::generic_armor::GenericArmorTable;
use crate::ship::drops::generic_shield::GenericShieldTable;


fn data_file_path(episode: Episode, difficulty: Difficulty, section_id: SectionID, filename: &str) -> PathBuf {
    let mut path = PathBuf::from("data/drops/");
    path.push(episode.to_string());
    path.push(difficulty.to_string().to_lowercase());
    path.push(section_id.to_string().to_lowercase());
    path.push(filename);
    path
}

pub fn load_data_file<T: serde::de::DeserializeOwned>(episode: Episode, difficulty: Difficulty, section_id: SectionID, filename: &str) -> T {
    let path = data_file_path(episode, difficulty, section_id, filename);
    let mut f = File::open(path).unwrap();
    let mut s = String::new();
    f.read_to_string(&mut s);

    toml::from_str::<T>(s.as_str()).unwrap()
}


#[derive(Debug, Serialize, Deserialize, Copy, Clone)]
pub enum MonsterDropType {
    #[serde(rename = "weapon")]
    Weapon,
    #[serde(rename = "armor")]
    Armor,
    #[serde(rename = "shield")]
    Shield,
    #[serde(rename = "unit")]
    Unit,
    #[serde(rename = "none")]
    None,
}

#[derive(Debug, Serialize, Deserialize, Copy, Clone)]
pub struct MonsterDropStats {
    pub dar: u32,
    pub drop_type: MonsterDropType,
    pub min_meseta: u32,
    pub max_meseta: u32,
}

enum RareDropItem {
    Weapon(WeaponType),
    Armor(ArmorType),
    Shield(ShieldType),
    Unit(UnitType),
    Tool(ToolType),
}


struct RareDrop {
    rate: f32,
    item: RareDropItem
}



#[derive(Debug, Serialize, Deserialize)]
pub struct RareDropConfigEntity {
    pub rate: f32,
    pub item: String,
}



/*#[derive(Serialize, Deserialize)]
pub struct MonsterDar(pub HashMap<MonsterType, MonsterDropStats>);

/*impl MonsterDar {
    fn from_f


}*/*/


struct RareDropTable {

}

impl RareDropTable {
    fn new(episode: Episode, difficulty: Difficulty, section_id: SectionID) -> RareDropTable {
        RareDropTable {

        }
    }

    fn get_drop(&self, monster: &MonsterType) -> Option<ItemDetail> {
        None
    }
}


struct DropTable<R: Rng + SeedableRng> {
    rare_table: RareDropTable,
    monster_stats: HashMap<MonsterType, MonsterDropStats>,
    weapon_table: GenericWeaponTable,
    armor_table: GenericArmorTable,
    shield_table: GenericShieldTable,
    rng: R,
}


impl<R: Rng + SeedableRng> DropTable<R> {
    fn new(episode: Episode, difficulty: Difficulty, section_id: SectionID) -> DropTable<R> {
        let mut path = PathBuf::from("data/drops/");
        path.push(episode.to_string());
        path.push(difficulty.to_string().to_lowercase());
        path.push(section_id.to_string().to_lowercase());
        path.push("grind_rate.toml");
        let mut f = File::open(path).unwrap();
        let mut s = String::new();
        f.read_to_string(&mut s);
        let monster_stats = toml::from_str(&s).unwrap();
        
        DropTable {
            rare_table: RareDropTable::new(episode, difficulty, section_id),
            monster_stats: monster_stats,
            weapon_table: GenericWeaponTable::new(episode, difficulty, section_id),
            armor_table: GenericArmorTable::new(episode, difficulty, section_id),
            shield_table: GenericShieldTable::new(episode, difficulty, section_id),
            rng: R::from_entropy(),
        }
    }

    fn generate_meseta(&self, monster: &MonsterDropStats) -> Option<ItemDetail> {
        None
    }

    fn generate_tool(&self, map_area: &MapVariantType, monster: &MonsterDropStats) -> Option<ItemDetail> {
        None
    }

    fn generate_armor(&self) -> Option<ItemDetail> {
        None
    }

    fn generate_shield(&self) -> Option<ItemDetail> {
        None
    }

    fn generate_unit(&self) -> Option<ItemDetail> {
        None
    }
    
    fn generate_typed_drop(&mut self, map_area: &MapVariantType, monster: &MonsterDropStats) -> Option<ItemDetail> {
        match monster.drop_type {
            MonsterDropType::Weapon => self.weapon_table.get_drop(map_area, &mut self.rng),
            MonsterDropType::Armor => self.armor_table.get_drop(map_area, &mut self.rng),
            MonsterDropType::Shield => self.shield_table.get_drop(map_area, &mut self.rng),
            MonsterDropType::Unit => self.generate_unit(),
            MonsterDropType::None => None,
        }
    }

    fn get_drop(&mut self, map_area: &MapVariantType, monster: &MonsterType) -> Option<ItemDetail> {
        //let mut rng = rand::thread_rng();
        let monster_stat = *self.monster_stats.get(monster)?;

        let drop_anything = self.rng.gen_range(0, 100);
        if drop_anything > monster_stat.dar {
            return None;
        }

        if let Some(item) = self.rare_table.get_drop(&monster) {
            return Some(item);
        }

        let drop_type = self.rng.gen_range(0, 3);
        
        match drop_type {
            0 => {
                self.generate_meseta(&monster_stat)
            },
            1 => {
                self.generate_tool(map_area, &monster_stat)
            },
            2 => {
                self.generate_typed_drop(map_area, &monster_stat)
            },
            _ => panic!()
        }
    }
}