use psopacket::{pso_packet, PSOPacketData};
use crate::{PSOPacket, PacketParseError, PSOPacketData};
use crate::utf8_to_utf16_array;
use crate::packet::messages::GameMessage;
//use character::character::FullCharacter;
use crate::character::character as character;

use std::io::Read;

const BLOCK_MENU_ID: u32 = 1;

#[pso_packet(0x03)]
pub struct ShipWelcome {
    #[utf8]
    copyright: [u8; 0x60],
    server_key: [u8; 48],
    client_key: [u8; 48],
}

impl ShipWelcome {
    pub fn new(server_key: [u8; 48], client_key: [u8; 48]) -> ShipWelcome {
        let mut copyright = [0u8; 0x60];
        copyright[..0x4B].clone_from_slice(b"Phantasy Star Online Blue Burst Game Server. Copyright 1999-2004 SONICTEAM.");
        ShipWelcome {
            copyright: copyright,
            server_key: server_key,
            client_key: client_key,
        }
    }
}


#[derive(Debug, PartialEq, Clone)]
pub struct BlockEntry {
    menu: u32,
    item: u32,
    flags: u16,
    name: [u16; 0x11],
}

impl PSOPacketData for BlockEntry {
    fn from_bytes<R: Read>(_cursor: &mut R) -> Result<Self, PacketParseError> {
        unimplemented!();
    }

    fn as_bytes(&self) -> Vec<u8> {
        let mut bytes = Vec::new();
        bytes.extend_from_slice(&u32::to_le_bytes(self.menu));
        bytes.extend_from_slice(&u32::to_le_bytes(self.item));
        bytes.extend_from_slice(&u16::to_le_bytes(self.flags));
        bytes.extend_from_slice(&unsafe { std::mem::transmute::<[u16; 0x11], [u8; 0x11*2]>(self.name) });
        bytes
    }
}

#[pso_packet(0xA1)]
pub struct ShipBlockList {
    shipblock: BlockEntry,
    blocks: Vec<BlockEntry>
}

impl ShipBlockList {
    pub fn new(shipname: &str, num_blocks: usize) -> ShipBlockList {
        ShipBlockList {
            shipblock: BlockEntry {
                menu: BLOCK_MENU_ID,
                item: 0,
                flags: 0,
                name: utf8_to_utf16_array!(shipname, 0x11)
            },
            blocks: (0..num_blocks).map(|i| BlockEntry {
                menu: BLOCK_MENU_ID,
                item: i as u32 + 1,
                flags: 0,
                name: utf8_to_utf16_array!(format!("Block {}", i+1), 0x11)
            }).collect()
        }
    }
}

// TODO: menu should be an enum
// TODO: or perhaps MenuSelect should be broken up into different structs based on menu
// TODO: i.e. ShipMenuSelect, BlockMenuSelect, etc
#[pso_packet(0x10)]
pub struct MenuSelect {
    pub menu: u32,
    pub item: u32,
}

#[pso_packet(0xE7)]
pub struct FullCharacter {
    #[no_debug]
    character: character::FullCharacter,
}


#[pso_packet(0x95)]
pub struct CharDataRequest {
}


// TODO: what does this even do?
#[pso_packet(0x61)]
pub struct CharData {
    _unknown: [u8; 0x828]
}



#[pso_packet(0x60)]
pub struct Message {
    msg: GameMessage,
}

impl Message {
    pub fn new(msg: GameMessage) -> Message {
        Message {
            msg: msg,
        }
    }
}

#[pso_packet(0x62, manual_flag)]
pub struct DirectMessage {
    flag: u32,
    msg: GameMessage
}

impl DirectMessage {
    pub fn new(target: u32, msg: GameMessage) -> DirectMessage {
        DirectMessage {
            flag: target,
            msg: msg,
        }
    }
}



#[derive(PSOPacketData, Clone, Copy, Default)]
pub struct PlayerHeader {
    pub tag: u32,
    pub guildcard: u32,
    pub _unknown1: [u32; 5],
    pub client_id: u32,
    pub name: [u16; 16],
    pub _unknown2: u32,
}

#[derive(PSOPacketData, Clone)]
pub struct PlayerInfo {
    pub header: PlayerHeader,
    pub inventory: character::Inventory,
    pub character: character::Character,
}

#[pso_packet(0x67)]
pub struct JoinLobby {
    pub client: u8,
    pub leader: u8,
    pub one: u8,
    pub lobby: u8,
    pub block: u16,
    pub event: u16,
    pub padding: u32,
    pub playerinfo: Vec<PlayerInfo>,
}

#[pso_packet(0x68, manual_flag)]
pub struct AddToLobby {
    flag: u32,
    pub client: u8,
    pub leader: u8,
    pub one: u8,
    pub lobby: u8,
    pub block: u16,
    pub event: u16,
    pub padding: u32,
    pub playerinfo: PlayerInfo,
}



#[pso_packet(0xC1)]
pub struct CreateRoom {
    unknown: [u32; 2],
    name: [u16; 16],
    password: [u16; 16],
    difficulty: u8,
    battle: u8,
    challenge: u8,
    episode: u8,
    single_player: u8,
    padding: [u8; 3],
}


#[pso_packet(0x01)]
pub struct SmallDialog {
    padding: [u32; 0x02],
    msg: String,
}

impl SmallDialog {
    pub fn new(msg: String) -> SmallDialog {
        SmallDialog {
            padding: [0; 0x02],
            msg: msg,
        }
    }
}

#[pso_packet(0x64, manual_flag)]
pub struct JoinRoom {
    flag: u32, // # of elements in players
    maps: [u32; 0x20],
    players: [PlayerHeader; 4],
    client_id: u8,
    leader_id: u8,
    one: u8,
    difficulty: u8, // TODO: enum
    battle: u8,
    event: u8,
    section: u8,
    challenge: u8,
    random_seed: u32,
    episode: u8,
    one2: u8,
    single_player: u8,
    unknown: u8,
}

impl JoinRoom {
    /*fn new() -> JoinRoom {
        JoinRoom {

        }
    }*/
}

#[pso_packet(0x65, manual_flag)]
pub struct AddToRoom {
    flag: u32,
    pub client: u8,
    pub leader: u8,
    pub one: u8,
    pub lobby: u8,
    pub block: u16,
    pub event: u16,
    pub padding: u32,
    pub playerinfo: PlayerInfo,
}

#[pso_packet(0x69)]
pub struct LeaveLobby {
    client: u8,
    leader: u8,
    _padding: u16,
}

impl LeaveLobby {
    pub fn new(client: u8, leader: u8) -> LeaveLobby {
        LeaveLobby {
            client: client,
            leader: leader,
            _padding: 0,
        }
    }
}

#[pso_packet(0x66)]
pub struct LeaveRoom {
    client: u8,
    leader: u8,
    _padding: u16,
}

impl LeaveRoom {
    pub fn new(client: u8, leader: u8) -> LeaveRoom {
        LeaveRoom {
            client: client,
            leader: leader,
            _padding: 0,
        }
    }
}

#[pso_packet(0x06)]
pub struct PlayerChat {
    pub unknown: u32,
    pub guildcard: u32,
    pub message: String,
}

impl PlayerChat {
    pub fn new(guildcard: u32, message: String) -> PlayerChat {
        PlayerChat {
            unknown: 0x00010000,
            guildcard: guildcard,
            message: message,
        }
    }
}