pub mod rw;

use newtype_enum::newtype_enum;
use zerocopy::{FromZeros, Immutable, IntoBytes, KnownLayout};

use crate::protocol::{Bool, String};

#[derive(Debug, Clone, Copy, FromZeros, IntoBytes, KnownLayout, Immutable)]
#[repr(C, packed)]
pub struct PacketHeader {
  pub user_id: u128,
  pub kind: PacketKind,
  pub size: u16,
}

#[derive(Debug, Clone, Copy, FromZeros, IntoBytes, KnownLayout, Immutable)]
#[repr(u16)]
pub enum PacketKind {
  Unknown = 0,
  Init = 1,
  Player = 2,
  Cap = 3,
  Game = 4,
  Tag = 5,
  Connect = 6,
  Disconnect = 7,
  Costume = 8,
  Shine = 9,
  Capture = 10,
  ChangeStage = 11,
  Command = 12,
  UdpInit = 13,
  HolePunch = 14,
}

const COSTUME_NAME_SIZE: usize = 0x20;
const CAP_ANIM_SIZE: usize = 0x30;
pub const STAGE_GAME_NAME_SIZE: usize = 0x40;
const STAGE_CHANGE_NAME_SIZE: usize = 0x30;
const STAGE_ID_SIZE: usize = 0x10;
pub const CLIENT_NAME_SIZE: usize = COSTUME_NAME_SIZE;

#[derive(Clone, Debug)]
pub struct Packet {
  pub user_id: u128,
  pub udp: bool,
  pub data: PacketData,
}

#[newtype_enum]
#[derive(Clone, Debug)]
pub enum PacketData {
  Unknown(Vec<u8>),
  #[derive(FromZeros, IntoBytes, KnownLayout, Immutable)]
  #[repr(C, packed)]
  Init {
    pub max_players: u16,
  },
  #[derive(FromZeros, IntoBytes, KnownLayout, Immutable)]
  #[repr(C, packed)]
  Player {
    position: [f32; 3],
    rotation: [f32; 4],
    weights: [f32; 6],
    action: u16,
    subaction: u16,
  },
  #[derive(FromZeros, IntoBytes, KnownLayout, Immutable)]
  #[repr(C, packed)]
  Cap {
    position: [f32; 3],
    rotation: [f32; 4],
    out: Bool,
    anim: String<CAP_ANIM_SIZE>,
  },
  #[derive(FromZeros, IntoBytes, KnownLayout, Immutable)]
  #[repr(C, packed)]
  Game {
    is_2d: u8,
    scenario_num: u8,
    stage: String<STAGE_GAME_NAME_SIZE>,
  },
  #[derive(FromZeros, IntoBytes, KnownLayout, Immutable)]
  #[repr(C, packed)]
  Tag {
    update_type: u8,
    is_it: Bool,
    seconds: u8,
    minutes: u16,
  },
  #[derive(FromZeros, IntoBytes, KnownLayout, Immutable)]
  #[repr(C, packed)]
  Connect {
    kind: ConnectionKind,
    max_player: u16,
    client_name: String<CLIENT_NAME_SIZE>,
  },
  Disconnect,
  #[derive(FromZeros, IntoBytes, KnownLayout, Immutable)]
  #[repr(C, packed)]
  Costume {
    body_name: String<COSTUME_NAME_SIZE>,
    cap_name: String<COSTUME_NAME_SIZE>,
  },
  #[derive(FromZeros, IntoBytes, KnownLayout, Immutable)]
  #[repr(C, packed)]
  Shine {
    shine_id: i32,
    is_grand: Bool,
  },
  #[derive(FromZeros, IntoBytes, KnownLayout, Immutable)]
  #[repr(C, packed)]
  Capture {
    model: String<COSTUME_NAME_SIZE>,
  },
  #[derive(FromZeros, IntoBytes, KnownLayout, Immutable)]
  #[repr(C, packed)]
  ChangeStage {
    stage: String<STAGE_CHANGE_NAME_SIZE>,
    id: String<STAGE_ID_SIZE>,
    scenario: i8,
    sub_scenario: u8,
  },
  Command,
  #[derive(FromZeros, IntoBytes, KnownLayout, Immutable)]
  #[repr(C, packed)]
  UdpInit {
    port: u16,
  },
  HolePunch,
}

pub enum TagUpdateBit {
  Time = 0,
  State = 1,
}

#[derive(Clone, Copy, Debug, PartialEq, Eq, FromZeros, IntoBytes, KnownLayout, Immutable)]
#[repr(u32)]
pub enum ConnectionKind {
  New = 0,
  Old = 1,
}
