// TOOD: `pub(super) for most of these?`
use std::convert::Into;
use std::path::PathBuf;
use std::io::{Read};
use std::fs::File;

use byteorder::{LittleEndian, ReadBytesExt};
use rand::Rng;
use thiserror::Error;

use crate::ship::monster::MonsterType;
use crate::ship::room::{Episode, RoomMode};

use crate::ship::map::*;

#[derive(Debug, Copy, Clone)]
pub struct RawMapEnemy {
    id: u32,
    _unknown1: u16,
    pub children: u16,
    map_area: u16,
    _unknown4: u16,
    section: u16,
    wave_idd: u16,
    wave_id: u32,
    x: f32,
    y: f32,
    z: f32,
    xrot: u32,
    yrot: u32,
    zrot: u32,
    _field1: u32,
    field2: u32,
    _field3: u32,
    _field4: u32,
    _field5: u32,
    skin: u32,
    _field6: u32
}

impl RawMapEnemy {
    pub fn from_byte_stream<R: Read>(cursor: &mut R) -> Result<RawMapEnemy, std::io::Error> {
        Ok(RawMapEnemy {
            id: cursor.read_u32::<LittleEndian>()?,
            _unknown1: cursor.read_u16::<LittleEndian>()?,
            children: cursor.read_u16::<LittleEndian>()?,
            map_area: cursor.read_u16::<LittleEndian>()?,
            _unknown4: cursor.read_u16::<LittleEndian>()?,
            section: cursor.read_u16::<LittleEndian>()?,
            wave_idd: cursor.read_u16::<LittleEndian>()?,
            wave_id: cursor.read_u32::<LittleEndian>()?,
            x: cursor.read_f32::<LittleEndian>()?,
            y: cursor.read_f32::<LittleEndian>()?,
            z: cursor.read_f32::<LittleEndian>()?,
            xrot: cursor.read_u32::<LittleEndian>()?,
            yrot: cursor.read_u32::<LittleEndian>()?,
            zrot: cursor.read_u32::<LittleEndian>()?,
            _field1: cursor.read_u32::<LittleEndian>()?,
            field2: cursor.read_u32::<LittleEndian>()?,
            _field3: cursor.read_u32::<LittleEndian>()?,
            _field4: cursor.read_u32::<LittleEndian>()?,
            _field5: cursor.read_u32::<LittleEndian>()?,
            skin: cursor.read_u32::<LittleEndian>()?,
            _field6: cursor.read_u32::<LittleEndian>()?,
        })
    }
}


#[derive(Error, Debug)]
#[error("")]
pub enum MapEnemyError {
    UnknownEnemyId(u32),
    MapAreaError(#[from] MapAreaError),
}


#[derive(Debug, Copy, Clone)]
pub struct MapEnemy {
    pub monster: MonsterType,
    pub map_area: MapArea,
    hp: u32,
    // TODO: other stats from battleparam
    pub player_hit: [bool; 4],
    pub dropped_item: bool,
    pub gave_exp: bool,
}

impl MapEnemy {
    pub fn from_raw(enemy: RawMapEnemy, episode: &Episode, map_area: &MapArea /*, battleparam */) -> Result<MapEnemy, MapEnemyError> {
        // TODO: rare enemies ep1-4, tower lilys, event rappies, ult variants?
        let monster = match map_area {
            MapArea::Forest1 | MapArea::Forest2 | MapArea::Dragon | 
            MapArea::Caves1  | MapArea::Caves2  | MapArea::Caves3 | MapArea::DeRolLe | 
            MapArea::Mines1  | MapArea::Mines2  | MapArea::VolOpt | 
            MapArea::Ruins1  | MapArea::Ruins2  | MapArea::Ruins3 | MapArea::DarkFalz => {
                match (enemy, episode) {
                    (RawMapEnemy {id: 64, ..}, _) => MonsterType::Hildebear,
                    // (RawMapEnemy {id: 64, ..}, _) => MonsterType::Hildeblue,
                    (RawMapEnemy {id: 65, ..}, _) => MonsterType::RagRappy,
                    // (RawMapEnemy {id: 65, ..}, _) => MonsterType::AlRappy,
                    (RawMapEnemy {id: 66, ..}, _) => MonsterType::Monest,
                    (RawMapEnemy {id: 67, field2: 0, ..}, _) => MonsterType::SavageWolf,
                    (RawMapEnemy {id: 67, ..}, _) => MonsterType::BarbarousWolf,
                    (RawMapEnemy {id: 68, skin: 0, ..}, _) => MonsterType::Booma,
                    (RawMapEnemy {id: 68, skin: 1, ..}, _) => MonsterType::Gobooma,
                    (RawMapEnemy {id: 68, skin: 2, ..}, _) => MonsterType::Gigobooma,
                    (RawMapEnemy {id: 96, ..}, _) => MonsterType::GrassAssassin,
                    (RawMapEnemy {id: 97, ..}, _) => MonsterType::PoisonLily,
                    // (RawMapEnemy {id: 97, ..}, _) => MonsterType::NarLily,
                    (RawMapEnemy {id: 98, ..}, _) => MonsterType::NanoDragon,
                    (RawMapEnemy {id: 99, skin: 0, ..}, _) => MonsterType::EvilShark,
                    (RawMapEnemy {id: 99, skin: 1, ..}, _) => MonsterType::PalShark,
                    (RawMapEnemy {id: 99, skin: 2, ..}, _) => MonsterType::GuilShark,
                    (RawMapEnemy {id: 100, ..}, _) => MonsterType::PofuillySlime,
                    // (RawMapEnemy {id: 100, ..}, _) => MonsterType::PouillySlime,
                    (RawMapEnemy {id: 101, ..}, _) => MonsterType::PanArms,
                    (RawMapEnemy {id: 128, skin: 0, ..}, _) => MonsterType::Dubchic,
                    (RawMapEnemy {id: 128, skin: 1, ..}, _) => MonsterType::Gillchic,
                    (RawMapEnemy {id: 129, ..}, _) => MonsterType::Garanz,
                    (RawMapEnemy {id: 130, field2: 0, ..}, _) => MonsterType::SinowBeat,
                    (RawMapEnemy {id: 130, ..}, _) => MonsterType::SinowGold,
                    (RawMapEnemy {id: 131, ..}, _) => MonsterType::Canadine,
                    (RawMapEnemy {id: 132, ..}, _) => MonsterType::Canane,
                    (RawMapEnemy {id: 133, ..}, _) => MonsterType::Dubwitch,
                    (RawMapEnemy {id: 160, ..}, _) => MonsterType::Delsaber,
                    (RawMapEnemy {id: 161, ..}, _) => MonsterType::ChaosSorcerer,
                    (RawMapEnemy {id: 162, ..}, _) => MonsterType::DarkGunner,
                    (RawMapEnemy {id: 164, ..}, _) => MonsterType::ChaosBringer,
                    (RawMapEnemy {id: 165, ..}, _) => MonsterType::DarkBelra,
                    (RawMapEnemy {id: 166, skin: 0, ..}, _) => MonsterType::Dimenian,
                    (RawMapEnemy {id: 166, skin: 1, ..}, _) => MonsterType::LaDimenian,
                    (RawMapEnemy {id: 166, skin: 2, ..}, _) => MonsterType::SoDimenian,
                    (RawMapEnemy {id: 167, ..}, _) => MonsterType::Bulclaw,
                    (RawMapEnemy {id: 168, ..}, _) => MonsterType::Claw,
                    (RawMapEnemy {id: 192, ..}, Episode::One) => MonsterType::Dragon,
                    (RawMapEnemy {id: 193, ..}, _) => MonsterType::DeRolLe,
                    (RawMapEnemy {id: 194, ..}, _) => MonsterType::VolOptPartA,
                    (RawMapEnemy {id: 197, ..}, _) => MonsterType::VolOpt,
                    (RawMapEnemy {id: 200, ..}, _) => MonsterType::DarkFalz,
                    _ => return Err(MapEnemyError::UnknownEnemyId(enemy.id))
                }
            },

            MapArea::VrTempleAlpha | MapArea::VrTempleBeta | MapArea::BarbaRay | 
            MapArea::VrSpaceshipAlpha | MapArea::VrSpaceshipBeta | MapArea::GolDragon | 
            MapArea::JungleAreaNorth | MapArea::JungleAreaEast | MapArea::Mountain | MapArea::Seaside | MapArea::SeasideNight | MapArea::Cca | MapArea::GalGryphon |
            MapArea::SeabedUpper | MapArea::SeabedLower | MapArea::OlgaFlow => {
                match (enemy, episode) {
                    (RawMapEnemy {id: 64, ..}, _) => MonsterType::Hildebear,
                    // (RawMapEnemy {id: 64, ..}, _) => MonsterType::Hildeblue,
                    (RawMapEnemy {id: 65, ..}, _) => MonsterType::RagRappy,
                    // (RawMapEnemy {id: 65, ..}, _) => MonsterType::EventRappy,
                    (RawMapEnemy {id: 66, ..}, _) => MonsterType::Monest,
                    (RawMapEnemy {id: 67, field2: 0, ..}, _) => MonsterType::SavageWolf,
                    (RawMapEnemy {id: 67, ..}, _) => MonsterType::BarbarousWolf,
                    (RawMapEnemy {id: 96, ..}, _) => MonsterType::GrassAssassin,
                    (RawMapEnemy {id: 97, ..}, _) => MonsterType::PoisonLily,
                    // (RawMapEnemy {id: 97, ..}, _) => MonsterType::NarLily,
                    // (RawMapEnemy {id: 97, ..}, _) => MonsterType::DelLily,
                    (RawMapEnemy {id: 101, ..}, _) => MonsterType::PanArms,
                    (RawMapEnemy {id: 128, skin: 0, ..}, _) => MonsterType::Dubchic,
                    (RawMapEnemy {id: 128, skin: 1, ..}, _) => MonsterType::Gillchic,
                    (RawMapEnemy {id: 129, ..}, _) => MonsterType::Garanz,
                    (RawMapEnemy {id: 133, ..}, _) => MonsterType::Dubwitch,
                    (RawMapEnemy {id: 160, ..}, _) => MonsterType::Delsaber,
                    (RawMapEnemy {id: 161, ..}, _) => MonsterType::ChaosSorcerer,
                    (RawMapEnemy {id: 165, ..}, _) => MonsterType::DarkBelra,
                    (RawMapEnemy {id: 166, skin: 0, ..}, _) => MonsterType::Dimenian,
                    (RawMapEnemy {id: 166, skin: 1, ..}, _) => MonsterType::LaDimenian,
                    (RawMapEnemy {id: 166, skin: 2, ..}, _) => MonsterType::SoDimenian,
                    (RawMapEnemy {id: 192, ..}, Episode::Two) => MonsterType::GalGryphon,
                    (RawMapEnemy {id: 202, ..}, _) => MonsterType::OlgaFlow,
                    (RawMapEnemy {id: 203, ..}, _) => MonsterType::BarbaRay,
                    (RawMapEnemy {id: 204, ..}, _) => MonsterType::GolDragon,
                    (RawMapEnemy {id: 212, skin: 0, ..}, _) => MonsterType::SinowBerill,
                    (RawMapEnemy {id: 212, skin: 1, ..}, _) => MonsterType::SinowSpigell,
                    (RawMapEnemy {id: 213, skin: 0, ..}, _) => MonsterType::Merillia,
                    (RawMapEnemy {id: 213, skin: 1, ..}, _) => MonsterType::Meriltas,
                    (RawMapEnemy {id: 214, skin: 0, ..}, _) => MonsterType::Mericarol,
                    (RawMapEnemy {id: 214, skin: 1, ..}, _) => MonsterType::Merikle,
                    (RawMapEnemy {id: 214, skin: 2, ..}, _) => MonsterType::Mericus,
                    (RawMapEnemy {id: 215, skin: 0, ..}, _) => MonsterType::UlGibbon,
                    (RawMapEnemy {id: 215, skin: 1, ..}, _) => MonsterType::ZolGibbon,
                    (RawMapEnemy {id: 216, ..}, _) => MonsterType::Gibbles,
                    (RawMapEnemy {id: 217, ..}, _) => MonsterType::Gee,
                    (RawMapEnemy {id: 218, ..}, _) => MonsterType::GiGue,
                    (RawMapEnemy {id: 219, ..}, _) => MonsterType::Deldepth,
                    (RawMapEnemy {id: 220, ..}, _) => MonsterType::Delbiter,
                    (RawMapEnemy {id: 221, skin: 0, ..}, _) => MonsterType::Dolmolm,
                    (RawMapEnemy {id: 221, skin: 1, ..}, _) => MonsterType::Dolmdarl,
                    (RawMapEnemy {id: 222, ..}, _) => MonsterType::Morfos,
                    (RawMapEnemy {id: 223, ..}, _) => MonsterType::Recobox,
                    (RawMapEnemy {id: 224, skin: 0, ..}, _) => MonsterType::SinowZoa,
                    (RawMapEnemy {id: 224, skin: 1, ..}, _) => MonsterType::SinowZele,
                    (RawMapEnemy {id: 224, ..}, _) => MonsterType::Epsilon,
                    (RawMapEnemy {id: 225, ..}, _) => MonsterType::IllGill,
                    _ => return Err(MapEnemyError::UnknownEnemyId(enemy.id))
                }
            },

            MapArea::Tower => {
                match (enemy, episode) {
                    (RawMapEnemy {id: 97, ..}, _) => MonsterType::DelLily,
                    (RawMapEnemy {id: 214, skin: 0, ..}, _) => MonsterType::Mericarol,
                    (RawMapEnemy {id: 214, skin: 1, ..}, _) => MonsterType::Merikle,
                    (RawMapEnemy {id: 214, skin: 2, ..}, _) => MonsterType::Mericus,
                    (RawMapEnemy {id: 216, ..}, _) => MonsterType::Gibbles,
                    (RawMapEnemy {id: 218, ..}, _) => MonsterType::GiGue,
                    (RawMapEnemy {id: 220, ..}, _) => MonsterType::Delbiter,
                    (RawMapEnemy {id: 223, ..}, _) => MonsterType::Recobox,
                    (RawMapEnemy {id: 224, ..}, _) => MonsterType::Epsilon,
                    (RawMapEnemy {id: 225, ..}, _) => MonsterType::IllGill,
                    _ => return Err(MapEnemyError::UnknownEnemyId(enemy.id))
                }
            },

            MapArea::CraterEast | MapArea::CraterWest | MapArea::CraterSouth | MapArea::CraterNorth | MapArea::CraterInterior => {
                match (enemy, episode) {
                    (RawMapEnemy {id: 65, ..}, Episode::Four) => MonsterType::SandRappyCrater,
                    // (RawMapEnemy {id: 65, ..}, Episode::Four) => MonsterType::DelRappyCrater,
                    (RawMapEnemy {id: 272, ..}, _) => MonsterType::Astark,
                    (RawMapEnemy {id: 273, field2: 0, ..}, _) => MonsterType::SatelliteLizardCrater,
                    (RawMapEnemy {id: 273, ..}, _) => MonsterType::YowieCrater,
                    (RawMapEnemy {id: 276, ..}, _) => MonsterType::ZuCrater,
                    // (RawMapEnemy {id: 276, ..}, _) => MonsterType::PazuzuCrater,
                    (RawMapEnemy {id: 277, skin: 0, ..}, _) => MonsterType::Boota,
                    (RawMapEnemy {id: 277, skin: 1, ..}, _) => MonsterType::ZeBoota,
                    (RawMapEnemy {id: 277, skin: 2, ..}, _) => MonsterType::BaBoota,
                    (RawMapEnemy {id: 278, ..}, _) => MonsterType::Dorphon,
                    // (RawMapEnemy {id: 278, ..}, _) => MonsterType::DorphonEclair,
                    _ => return Err(MapEnemyError::UnknownEnemyId(enemy.id))
                }
            },
            MapArea::SubDesert1 | MapArea::SubDesert2 | MapArea::SubDesert3 | MapArea::SaintMillion => {
                match (enemy, episode) {
                    (RawMapEnemy {id: 65, ..}, Episode::Four) => MonsterType::SandRappyDesert,
                    // (RawMapEnemy {id: 65, ..}, Episode::Four) => MonsterType::DelRappyDesert,
                    (RawMapEnemy {id: 273, field2: 0, ..}, _) => MonsterType::SatelliteLizardDesert,
                    (RawMapEnemy {id: 273, ..}, _) => MonsterType::YowieDesert,
                    (RawMapEnemy {id: 274, ..}, _) => MonsterType::MerissaA,
                    // (RawMapEnemy {id: 274, ..}, _) => MonsterType::MerissaAA,
                    (RawMapEnemy {id: 275, ..}, _) => MonsterType::Girtablulu,
                    (RawMapEnemy {id: 276, ..}, _) => MonsterType::ZuDesert,
                    // (RawMapEnemy {id: 276, ..}, _) => MonsterType::PazuzuDesert,
                    (RawMapEnemy {id: 279, skin: 0, ..}, _) => MonsterType::Goran,
                    (RawMapEnemy {id: 279, skin: 1, ..}, _) => MonsterType::PyroGoran,
                    (RawMapEnemy {id: 279, skin: 2, ..}, _) => MonsterType::GoranDetonator,
                    (RawMapEnemy {id: 281, skin: 0, ..}, _) => MonsterType::SaintMillion,
                    (RawMapEnemy {id: 281, skin: 1, ..}, _) => MonsterType::Shambertin,
                    // (RawMapEnemy {id: 281, skin: 1, ..}, _) => MonsterType::Kondrieu,
                    _ => return Err(MapEnemyError::UnknownEnemyId(enemy.id))
                }
            },
            _ => return Err(MapEnemyError::UnknownEnemyId(enemy.id))
        };

        Ok(MapEnemy {
            monster: monster,
            map_area: map_area.clone(),
            hp: 0,
            dropped_item: false,
            gave_exp: false,
            player_hit: [false; 4],
        })
    }

    pub fn new(monster: MonsterType, map_area: MapArea) -> MapEnemy {
        MapEnemy {
            monster: monster,
            map_area: map_area,
            hp: 0,
            dropped_item: false,
            gave_exp: false,
            player_hit: [false; 4],
        }
    }
}