//! Structures used by Marathon's Map format's terminal definitions.

use crate::durandal::{bin::*, err::*, text::*};
use bitflags::bitflags;

/// Reads an `InterGroup`.
pub fn read_group(b: &[u8]) -> ResultS<(InterGroup, usize)>
{
   read_data! {
      12, BE in b =>
      flags = u16[0];
      ttype = u16[2];
      pdata = u16[4];
      beg   = u16[6] usize;
      len   = u16[8] usize;
      lines = u16[10];
   }

   let flags = flag_ok!(GroupFlags, flags)?;
   let ttype = match ttype {
      0  => GroupType::Logon(pdata),
      1  => GroupType::Unfinished,
      2  => GroupType::Success,
      3  => GroupType::Failure,
      4  => GroupType::Info,
      5  => GroupType::End,
      6  => GroupType::TeleInter(pdata),
      7  => GroupType::TeleIntra(pdata),
      8  => GroupType::Checkpoint(pdata),
      9  => GroupType::Sound(pdata),
      10 => GroupType::Movie(pdata),
      11 => GroupType::Track(pdata),
      12 => GroupType::Pict(pdata),
      13 => GroupType::Logoff(pdata),
      14 => GroupType::Camera(pdata),
      15 => GroupType::Static(pdata),
      16 => GroupType::Tag(pdata),
      n  => return Err(ReprError::new(n).into()),
   };

   Ok((InterGroup{flags, ttype, lines, beg, len}, 12))
}

/// Reads a `Face`.
pub fn read_face(b: &[u8]) -> ResultS<(Face, usize)>
{
   read_data! {
      6, BE in b =>
      start = u16[0] usize;
      face  = u16[2];
      color = u16[4];
   }

   Ok((Face{start, face, color}, 6))
}

/// Reads a `term` chunk.
pub fn read_term(b: &[u8]) -> ResultS<(Terminal, usize)>
{
   read_data! {
      10, BE in b =>
      end     = u16[0] usize;
      encoded = u16[2];
      lines   = u16[4];
      group_n = u16[6] usize;
      face_n  = u16[8] usize;
   }

   let encoded = encoded != 0;

   let (i_grp, x) = rd_array_num(&b[10..],    group_n, read_group)?;
   let (faces, y) = rd_array_num(&b[10 + x..], face_n, read_face)?;

   let text = ok!(b.get(10 + x + y..end), "not enough data")?;
   let text = if encoded {fuck_string(text)} else {text.to_vec()};

   let mut groups = Vec::with_capacity(group_n);

   for grp in &i_grp {
      let flags = grp.flags;
      let ttype = grp.ttype;
      let lines = grp.lines;
      let beg   = grp.beg;
      let len   = grp.len;
      let text = ok!(text.get(beg..beg + len), "bad offset")?;
      let text = mac_roman_cstr(text);

      groups.push(Group{flags, ttype, lines, text});
   }

   Ok((Terminal{lines, groups, faces}, end))
}

impl Default for GroupType
{
   fn default() -> Self {GroupType::Unfinished}
}

/// A terminal definition, with collections of groups and faces.
#[cfg_attr(feature = "serde_obj", derive(serde::Serialize))]
#[derive(Debug, Eq, PartialEq)]
pub struct Terminal
{
   pub lines:  u16,
   pub groups: Vec<Group>,
   pub faces:  Vec<Face>,
}

/// A text face.
#[cfg_attr(feature = "serde_obj", derive(serde::Serialize))]
#[derive(Debug, Eq, PartialEq)]
pub struct Face
{
   pub start: usize,
   pub face:  u16,
   pub color: u16,
}

/// A terminal command grouping.
#[cfg_attr(feature = "serde_obj", derive(serde::Serialize))]
#[derive(Debug, Eq, PartialEq)]
pub struct Group
{
   pub flags: GroupFlags,
   pub ttype: GroupType,
   pub lines: u16,
   pub text:  String,
}

/// Interim structure.
#[derive(Debug, Eq, PartialEq)]
pub struct InterGroup
{
   pub flags: GroupFlags,
   pub ttype: GroupType,
   pub lines: u16,
   pub beg:   usize,
   pub len:   usize,
}

/// The command of a `Group`.
#[cfg_attr(feature = "serde_obj", derive(serde::Serialize))]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum GroupType
{
   Logon(u16),
   Unfinished,
   Success,
   Failure,
   Info,
   End,
   TeleInter(u16),
   TeleIntra(u16),
   Checkpoint(u16),
   Sound(u16),
   Movie(u16),
   Track(u16),
   Pict(u16),
   Logoff(u16),
   Camera(u16),
   Static(u16),
   Tag(u16),
}

bitflags! {
   /// Flags for `Group`.
   #[cfg_attr(feature = "serde_obj", derive(serde::Serialize))]
   pub struct GroupFlags: u16
   {
      /// Draws the picture on the right.
      const DRAW_ON_RIGHT = 1;
      /// Draws the picture in the center.
      const DRAW_CENTER   = 1 << 1;
   }
}

// EOF
