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 DirectedMessage {
    flag: u32,
    msg: GameMessage
}

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



#[derive(PSOPacketData, Clone)]
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,
}