use std::io::{Seek, SeekFrom};

use psopacket::pso_message;
use crate::{PSOPacketData, PacketParseError};


pub trait PSOMessage {
    const CMD: u8;
    fn from_bytes<R: std::io::Read + std::io::Seek>(cur: &mut R) -> Result<Self, PacketParseError> where Self: Sized;
    fn as_bytes(&self) -> Vec<u8>;
}


#[pso_message(0x40)]
pub struct PlayerWalking {
    x: f32,
    y: f32,
    z: f32,
}

#[pso_message(0x42)]
pub struct PlayerRunning {
    x: f32,
    z: f32,
}

#[pso_message(0x3E)]
pub struct PlayerStopped {
    unknown1: u32,
    unknown2: u32,
    x: f32,
    y: f32,
    z: f32,
}

#[pso_message(0x1F)]
pub struct PlayerChangedMap {
    unknown1: u32,
    unknown2: u32,
    x: f32,
    y: f32,
    z: f32,
}

#[pso_message(0x1F)]
pub struct PlayerChangedMap2 {
    unknown1: u32,
}

#[pso_message(0x20)]
pub struct TellOtherPlayerMyLocation {
    unknown1: u32,
    unknown2: u32,
    x: f32,
    y: f32,
    z: f32,
}

#[pso_message(0x3F)]
pub struct PlayerLoadedIn {
    unknown1: u32,
    unknown2: u32,
    x: f32,
    y: f32,
    z: f32,
}


#[pso_message(0x23)]
pub struct PlayerDoneChangingMap {
}

#[pso_message(0x52)]
pub struct TalkToNpc {
    unknown1: u32,
    unknown2: u32,
}

#[pso_message(0x58)]
pub struct LobbyEmote {
    emote: u32,
}

#[pso_message(0xAE)]
pub struct UnknownAE {
    unknown1: u32,
    unknown2: u32,
    unknown3: u32,
}



#[derive(Debug, Clone, PartialEq)]
pub enum GameMessage {
    PlayerWalking(PlayerWalking),
    PlayerRunning(PlayerRunning),
    PlayerStopped(PlayerStopped),
    PlayerLoadedIn(PlayerLoadedIn),
    PlayerChangedMap(PlayerChangedMap),
    PlayerChangedMap2(PlayerChangedMap2),
    PlayerDoneChangingMap(PlayerDoneChangingMap),
    TellOtherPlayerMyLocation(TellOtherPlayerMyLocation),
    LobbyEmote(LobbyEmote),
    TalkToNpc(TalkToNpc),
    UnknownAE(UnknownAE),
}

impl PSOPacketData for GameMessage {
    fn from_bytes<R: std::io::Read + std::io::Seek>(mut cur: &mut R) -> Result<Self, PacketParseError> {
        let mut byte = [0u8; 1];
        let mut len = [0u8; 1];
        cur.read(&mut byte);
        cur.read(&mut len);
        cur.seek(SeekFrom::Current(-2)); // Cursor doesn't implement Peek?
        match byte[0] {
            PlayerWalking::CMD => Ok(GameMessage::PlayerWalking(PlayerWalking::from_bytes(&mut cur)?)),
            PlayerRunning::CMD => Ok(GameMessage::PlayerRunning(PlayerRunning::from_bytes(&mut cur)?)),
            PlayerStopped::CMD => Ok(GameMessage::PlayerStopped(PlayerStopped::from_bytes(&mut cur)?)),
            PlayerLoadedIn::CMD => Ok(GameMessage::PlayerLoadedIn(PlayerLoadedIn::from_bytes(&mut cur)?)),
            PlayerChangedMap::CMD if len[0] == 6 => Ok(GameMessage::PlayerChangedMap(PlayerChangedMap::from_bytes(&mut cur)?)),
            PlayerChangedMap2::CMD if len[0] == 2 => Ok(GameMessage::PlayerChangedMap2(PlayerChangedMap2::from_bytes(&mut cur)?)),
            PlayerDoneChangingMap::CMD => Ok(GameMessage::PlayerDoneChangingMap(PlayerDoneChangingMap::from_bytes(&mut cur)?)),
            TellOtherPlayerMyLocation::CMD => Ok(GameMessage::TellOtherPlayerMyLocation(TellOtherPlayerMyLocation::from_bytes(&mut cur)?)),
            LobbyEmote::CMD => Ok(GameMessage::LobbyEmote(LobbyEmote::from_bytes(&mut cur)?)),
            TalkToNpc::CMD => Ok(GameMessage::TalkToNpc(TalkToNpc::from_bytes(&mut cur)?)),
            UnknownAE::CMD => Ok(GameMessage::UnknownAE(UnknownAE::from_bytes(&mut cur)?)),
            _ => Err(PacketParseError::UnknownMessage(byte[0],
                                                      {
                                                          let mut b = vec![0; len[0] as usize * 4];
                                                          cur.read(&mut b).unwrap();
                                                          b.to_vec()
                                                      }
            )),
        }
    }
    fn as_bytes(&self) -> Vec<u8> {
        match self {
            GameMessage::PlayerWalking(data) => data.as_bytes(),
            GameMessage::PlayerRunning(data) => data.as_bytes(),
            GameMessage::PlayerStopped(data) => data.as_bytes(),
            GameMessage::PlayerLoadedIn(data) => data.as_bytes(),
            GameMessage::PlayerChangedMap(data) => data.as_bytes(),
            GameMessage::PlayerChangedMap2(data) => data.as_bytes(),
            GameMessage::PlayerDoneChangingMap(data) => data.as_bytes(),
            GameMessage::TellOtherPlayerMyLocation(data) => data.as_bytes(),
            GameMessage::LobbyEmote(data) => data.as_bytes(),
            GameMessage::TalkToNpc(data) => data.as_bytes(),
            GameMessage::UnknownAE(data) => data.as_bytes(),
        }
    }
}