use std::convert::{From, Into, TryFrom, TryInto};

#[derive(Debug)]
pub enum RoomCreationError {
    InvalidMode,
    InvalidEpisode(u8),
    InvalidDifficulty(u8),

}

#[derive(Debug, Copy, Clone)]
pub enum Episode {
    One,
    Two,
    Four,
}

impl TryFrom<u8> for Episode {
    type Error = RoomCreationError;

    fn try_from(value: u8) -> Result<Episode, RoomCreationError> {
        match value {
            1 => Ok(Episode::One),
            2 => Ok(Episode::Two),
            3 => Ok(Episode::Four),
            _ => Err(RoomCreationError::InvalidEpisode(value))
        }
    }
}

impl Into<u8> for Episode {
    fn into(self) -> u8 {
        match self {
            Episode::One => 1,
            Episode::Two => 2,
            Episode::Four => 3,
        }
    }
}

#[derive(Debug, Copy, Clone)]
pub enum Difficulty {
    Normal,
    Hard,
    VeryHard,
    Ultimate,
}

impl TryFrom<u8> for Difficulty {
    type Error = RoomCreationError;

    fn try_from(value: u8) -> Result<Difficulty, RoomCreationError> {
        match value {
            0 => Ok(Difficulty::Normal),
            1 => Ok(Difficulty::Hard),
            2 => Ok(Difficulty::VeryHard),
            3 => Ok(Difficulty::Ultimate),
            _ => Err(RoomCreationError::InvalidDifficulty(value))
        }
    }
}

impl Into<u8> for Difficulty {
    fn into(self) -> u8 {
        match self {
            Difficulty::Normal => 0,
            Difficulty::Hard => 1,
            Difficulty::VeryHard => 2,
            Difficulty::Ultimate => 3,
        }
    }
}

#[derive(Debug)]
pub enum RoomMode {
    Single {
        episode: Episode,
        difficulty: Difficulty,
    },
    Multi {
        episode: Episode,
        difficulty: Difficulty,
    },
    Challenge {
        episode: Episode,
    },
    Battle {
        episode: Episode,
        difficulty: Difficulty,
    }
}


impl RoomMode {
    fn difficulty(&self) -> Difficulty {
        match self {
            RoomMode::Single {difficulty, ..} => *difficulty,
            RoomMode::Multi {difficulty, ..} => *difficulty,
            RoomMode::Battle {difficulty, ..} => *difficulty,
            RoomMode::Challenge {..} => Difficulty::Normal,
        }
    }

    fn episode(&self) -> Episode {
        match self {
            RoomMode::Single {episode, ..} => *episode,
            RoomMode::Multi {episode, ..} => *episode,
            RoomMode::Battle {episode, ..} => *episode,
            RoomMode::Challenge {episode, ..} => *episode,
        }
    }
}


#[derive(Debug)]
pub struct RoomState {
    mode: RoomMode,
    pub name: String,
    password: [u16; 16],
    pub maps: [u32; 0x20],
    // drop_table
    // items on ground
    // enemy info
}

impl RoomState {
    /*fn new(mode: RoomMode) -> Room {
        Room {
            mode: mode,
        }
    }*/

    pub fn from_create_room(create_room: &libpso::packet::ship::CreateRoom) -> Result<RoomState, RoomCreationError> {
        if [create_room.battle, create_room.challenge, create_room.single_player].iter().sum::<u8>() > 1 {
            return Err(RoomCreationError::InvalidMode)
        }
        
        let room_mode = if create_room.battle == 1 {
            RoomMode::Battle {
                episode: create_room.episode.try_into()?,
                difficulty: create_room.difficulty.try_into()?,
            }
        }
        else if create_room.challenge == 1 {
            RoomMode::Challenge {
                episode: create_room.episode.try_into()?,
            }
        }
        else if create_room.single_player == 1 {
            RoomMode::Single {
                episode: create_room.episode.try_into()?,
                difficulty: create_room.difficulty.try_into()?,
            }
        }
        else { // normal multimode
            RoomMode::Multi {
                episode: create_room.episode.try_into()?,
                difficulty: create_room.difficulty.try_into()?,
            }
        };
        
        Ok(RoomState {
            mode: room_mode,
            name: String::from_utf16_lossy(&create_room.name).trim_matches(char::from(0)).into(),
            password: create_room.password,
            maps: [0; 0x20],
        })
    }
}