///
/// Record type and tag definitions
///

use crate::record::{Record, Int2, Int4, Int2Array, Int4Array, Real8, Real8Pair, DateTimePair, BitArray, ASCII, Empty};

//use std::io::Write;

// record tags
pub const RTAG_HEADER:   u16 = 0x0002;
pub const RTAG_BGNLIB:   u16 = 0x0102;
pub const RTAG_LIBNAME:  u16 = 0x0206;
pub const RTAG_UNITS:    u16 = 0x0305;       // (user_units_per_db_unit, db_units_per_meter)
pub const RTAG_ENDLIB:   u16 = 0x0400;
pub const RTAG_BGNSTR:   u16 = 0x0502;
pub const RTAG_STRNAME:  u16 = 0x0606;
pub const RTAG_ENDSTR:   u16 = 0x0700;
pub const RTAG_BOUNDARY: u16 = 0x0800;
pub const RTAG_PATH:     u16 = 0x0900;
pub const RTAG_SREF:     u16 = 0x0a00;
pub const RTAG_AREF:     u16 = 0x0b00;
pub const RTAG_TEXT:     u16 = 0x0c00;
pub const RTAG_LAYER:    u16 = 0x0d02;
pub const RTAG_DATATYPE: u16 = 0x0e02;
pub const RTAG_WIDTH:    u16 = 0x0f03;
pub const RTAG_XY:       u16 = 0x1003;
pub const RTAG_ENDEL:    u16 = 0x1100;
pub const RTAG_SNAME:    u16 = 0x1206;
pub const RTAG_COLROW:   u16 = 0x1302;
pub const RTAG_NODE:     u16 = 0x1500;
pub const RTAG_TEXTTYPE: u16 = 0x1602;
pub const RTAG_PRESENTATION: u16 = 0x1701;
pub const RTAG_SPACING:  u16 = 0x1802;       // unused; not sure about 02
pub const RTAG_STRING:   u16 = 0x1906;
pub const RTAG_STRANS:   u16 = 0x1a01;
pub const RTAG_MAG:      u16 = 0x1b05;
pub const RTAG_ANGLE:    u16 = 0x1c05;
pub const RTAG_UINTEGER: u16 = 0x1d02;       // unused; not sure about 02
pub const RTAG_USTRING:  u16 = 0x1e06;       // unused; not sure about 06
pub const RTAG_REFLIBS:  u16 = 0x1f06;
pub const RTAG_FONTS:    u16 = 0x2006;
pub const RTAG_PATHTYPE: u16 = 0x2102;
pub const RTAG_GENERATIONS:  u16 = 0x2202;
pub const RTAG_ATTRTABLE:    u16 = 0x2306;
pub const RTAG_STYPTABLE:    u16 = 0x2406;   // unused; not sure about 06
pub const RTAG_STRTYPE:  u16 = 0x2502;       // unused
pub const RTAG_ELFLAGS:  u16 = 0x2601;
pub const RTAG_ELKEY:    u16 = 0x2703;       // unused
pub const RTAG_LINKTYPE: u16 = 0x2803;       // unused
pub const RTAG_LINKKEYS: u16 = 0x2903;       // unused
pub const RTAG_NODETYPE: u16 = 0x2a02;
pub const RTAG_PROPATTR: u16 = 0x2b02;
pub const RTAG_PROPVALUE:    u16 = 0x2c06;
pub const RTAG_BOX:      u16 = 0x2d00;
pub const RTAG_BOXTYPE:  u16 = 0x2e02;
pub const RTAG_PLEX:     u16 = 0x2f03;
pub const RTAG_BGNEXTN:  u16 = 0x3003;
pub const RTAG_ENDEXTN:  u16 = 0x3103;
pub const RTAG_TAPENUM:  u16 = 0x3202;
pub const RTAG_TAPECODE: u16 = 0x3302;
pub const RTAG_STRCLASS: u16 = 0x3401;
pub const RTAG_RESERVED: u16 = 0x3503;
pub const RTAG_FORMAT:   u16 = 0x3602;
pub const RTAG_MASK:     u16 = 0x3706;       // list of Layers and dtypes
pub const RTAG_ENDMASKS: u16 = 0x3800;       // end of MASKS records
pub const RTAG_LIBDIRSIZE:   u16 = 0x3902;
pub const RTAG_SRFNAME:  u16 = 0x3a06;
pub const RTAG_LIBSECUR: u16 = 0x3b02;
pub const RTAG_BORDER:   u16 = 0x3c00;
pub const RTAG_SOFTFENCE:    u16 = 0x3d00;
pub const RTAG_HARDFENCE:    u16 = 0x3f00;
pub const RTAG_SOFTWIRE: u16 = 0x3f00;
pub const RTAG_HARDWIRE: u16 = 0x4000;
pub const RTAG_PATHPORT: u16 = 0x4100;
pub const RTAG_NODEPORT: u16 = 0x4200;
pub const RTAG_USERCONSTRAINT: u16 = 0x4300;
pub const RTAG_SPACERERROR:    u16 = 0x4400;
pub const RTAG_CONTACT:  u16 = 0x4500;

/*
// data types
pub const DATA_TYPE_NONE: u16 = 0x00;
pub const DATA_TYPE_BIT: u16 = 0x01;
pub const DATA_TYPE_INT16: u16 = 0x02;
pub const DATA_TYPE_INT32: u16 = 0x03;
pub const DATA_TYPE_REAL32: u16 = 0x04;
pub const DATA_TYPE_REAL64: u16 = 0x05;
pub const DATA_TYPE_STR: u16 = 0x06;

pub const MAX_DATA_SIZE: usize = 8;

/// Returns the size of the given data type in bytes.
pub fn data_size(t: u16) -> Option<u16> {
    match t {
        x if x == DATA_TYPE_NONE => 0,
        x if x == DATA_TYPE_BIT => 2,
        x if x == DATA_TYPE_INT16 => 2,
        x if x == DATA_TYPE_INT32 => 4,
        x if x == DATA_TYPE_REAL32 => 4,
        x if x == DATA_TYPE_REAL64 => 8,
        _ => 0
    }
*/

pub struct HEADER;
impl Record<Int2> for HEADER {
    fn tag() -> u16 { RTAG_HEADER }
    fn expected_size() -> Option<u16> { Some(2) }
}
//impl Record<Int2> for HEADER;

pub struct BGNLIB;
impl Record<DateTimePair> for BGNLIB {
    fn tag() -> u16 { RTAG_BGNLIB }
    fn expected_size() -> Option<u16> { Some(2 * 2 * 6) }
}

pub struct LIBNAME;
impl Record<ASCII> for LIBNAME {
    fn tag() -> u16 { RTAG_LIBNAME }
    fn expected_size() -> Option<u16> { None }
}

pub struct UNITS;
impl Record<Real8Pair> for UNITS {
    // (user_units_per_db_unit, db_units_per_meter)
    fn tag() -> u16 { RTAG_UNITS }
    fn expected_size() -> Option<u16> { Some(2 * 8) }
}

pub struct ENDLIB;
impl Record<Empty> for ENDLIB {
    fn tag() -> u16 { RTAG_ENDLIB }
    fn expected_size() -> Option<u16> { Some(0) }
}

pub struct BGNSTR;
impl Record<DateTimePair> for BGNSTR {
    fn tag() -> u16 { RTAG_BGNSTR }
    fn expected_size() -> Option<u16> { Some(2 * 6) }
}

pub struct STRNAME;
impl Record<ASCII> for STRNAME {
    fn tag() -> u16 { RTAG_STRNAME }
    fn expected_size() -> Option<u16> { None }
}

pub struct ENDSTR;
impl Record<Empty> for ENDSTR {
    fn tag() -> u16 { RTAG_ENDSTR }
    fn expected_size() -> Option<u16> { Some(0) }
}

pub struct BOUNDARY;
impl Record<Empty> for BOUNDARY {
    fn tag() -> u16 { RTAG_BOUNDARY }
    fn expected_size() -> Option<u16> { Some(0) }
}

pub struct PATH;
impl Record<Empty> for PATH {
    fn tag() -> u16 { RTAG_PATH }
    fn expected_size() -> Option<u16> { Some(0) }
}

pub struct SREF;
impl Record<Empty> for SREF {
    fn tag() -> u16 { RTAG_SREF }
    fn expected_size() -> Option<u16> { Some(0) }
}

pub struct AREF;
impl Record<Empty> for AREF {
    fn tag() -> u16 { RTAG_AREF }
    fn expected_size() -> Option<u16> { Some(0) }
}

pub struct TEXT;
impl Record<Empty> for TEXT {
    fn tag() -> u16 { RTAG_TEXT }
    fn expected_size() -> Option<u16> { Some(0) }
}

pub struct LAYER;
impl Record<Int2> for LAYER {
    fn tag() -> u16 { RTAG_LAYER }
    fn expected_size() -> Option<u16> { Some(2) }
}

pub struct DATATYPE;
impl Record<Int2> for DATATYPE {
    fn tag() -> u16 { RTAG_DATATYPE }
    fn expected_size() -> Option<u16> { Some(2) }
}

pub struct WIDTH;
impl Record<Int4> for WIDTH {
    fn tag() -> u16 { RTAG_WIDTH }
    fn expected_size() -> Option<u16> { Some(4) }
}

pub struct XY;
impl Record<Int4Array> for XY {
    fn tag() -> u16 { RTAG_XY }
    fn expected_size() -> Option<u16> { None }
}

pub struct ENDEL;
impl Record<Empty> for ENDEL {
    fn tag() -> u16 { RTAG_ENDEL }
    fn expected_size() -> Option<u16> { Some(0) }
}

pub struct SNAME;
impl Record<ASCII> for SNAME {
    fn tag() -> u16 { RTAG_SNAME }
    fn expected_size() -> Option<u16> { None }
}

pub struct COLROW;
impl Record<Int2Array> for COLROW {
    fn tag() -> u16 { RTAG_COLROW }
    fn expected_size() -> Option<u16> { Some(4) }
}

pub struct NODE;
impl Record<Empty> for NODE {
    fn tag() -> u16 { RTAG_NODE }
    fn expected_size() -> Option<u16> { Some(0) }
}

pub struct TEXTTYPE;
impl Record<Int2> for TEXTTYPE {
    fn tag() -> u16 { RTAG_TEXTTYPE }
    fn expected_size() -> Option<u16> { Some(2) }
}

pub struct PRESENTATION;
impl Record<BitArray> for PRESENTATION {
    fn tag() -> u16 { RTAG_PRESENTATION }
    fn expected_size() -> Option<u16> { Some(2) }
}

pub struct SPACING;
impl Record<Int2> for SPACING {
    fn tag() -> u16 { RTAG_SPACING }
    fn expected_size() -> Option<u16> { Some(2) }
}

pub struct STRING;
impl Record<ASCII> for STRING {
    fn tag() -> u16 { RTAG_STRING }
    fn expected_size() -> Option<u16> { None }
}

pub struct STRANS;
impl Record<BitArray> for STRANS {
    fn tag() -> u16 { RTAG_STRANS }
    fn expected_size() -> Option<u16> { Some(2) }
}

pub struct MAG;
impl Record<Real8> for MAG {
    fn tag() -> u16 { RTAG_MAG }
    fn expected_size() -> Option<u16> { Some(8) }
}

pub struct ANGLE;
impl Record<Real8> for ANGLE {
    fn tag() -> u16 { RTAG_ANGLE }
    fn expected_size() -> Option<u16> { Some(8) }
}

pub struct UINTEGER;
impl Record<Int2> for UINTEGER {
    fn tag() -> u16 { RTAG_UINTEGER }
    fn expected_size() -> Option<u16> { Some(2) }
}

pub struct USTRING;
impl Record<ASCII> for USTRING {
    fn tag() -> u16 { RTAG_USTRING }
    fn expected_size() -> Option<u16> { None }
}

pub struct REFLIBS;
impl Record<ASCII> for REFLIBS {
    fn tag() -> u16 { RTAG_REFLIBS }
    fn expected_size() -> Option<u16> { None }
}
impl REFLIBS {
    pub fn check_size(actual_size: usize) -> Result<(), String> {
        if actual_size % 44 == 0 {
            Ok(())
        } else {
            Err(format!("Expected record size divisible by 44, got {}", actual_size))
        }
    }
}

pub struct FONTS;
impl Record<ASCII> for FONTS {
    fn tag() -> u16 { RTAG_FONTS }
    fn expected_size() -> Option<u16> { None }
}
impl FONTS {
    pub fn check_size(actual_size: usize) -> Result<(), String> {
        if actual_size % 44 == 0 {
            Ok(())
        } else {
            Err(format!("Expected record size divisible by 44, got {}", actual_size))
        }
    }
}

pub struct PATHTYPE;
impl Record<Int2> for PATHTYPE {
    fn tag() -> u16 { RTAG_PATHTYPE }
    fn expected_size() -> Option<u16> { Some(2) }
}

pub struct GENERATIONS;
impl Record<Int2> for GENERATIONS {
    fn tag() -> u16 { RTAG_GENERATIONS }
    fn expected_size() -> Option<u16> { Some(2) }
}

pub struct ATTRTABLE;
impl Record<ASCII> for ATTRTABLE {
    fn tag() -> u16 { RTAG_ATTRTABLE }
    fn expected_size() -> Option<u16> { None }
}
impl ATTRTABLE {
    pub fn check_size(actual_size: usize) -> Result<(), String> {
        if actual_size % 44 == 0 {
            Ok(())
        } else {
            Err(format!("Expected record size divisible by 44, got {}", actual_size))
        }
    }
}

pub struct STYPTABLE;
impl Record<ASCII> for STYPTABLE {
    fn tag() -> u16 { RTAG_STYPTABLE }
    fn expected_size() -> Option<u16> { None }
}

pub struct STRTYPE;
impl Record<Int2> for STRTYPE {
    fn tag() -> u16 { RTAG_STRTYPE }
    fn expected_size() -> Option<u16> { None }
}

pub struct ELFLAGS;
impl Record<BitArray> for ELFLAGS {
    fn tag() -> u16 { RTAG_ELFLAGS }
    fn expected_size() -> Option<u16> { Some(2) }
}

pub struct ELKEY;
impl Record<Int2> for ELKEY {
    fn tag() -> u16 { RTAG_ELKEY }
    fn expected_size() -> Option<u16> { Some(2) }
}

pub struct LINKTYPE;
impl Record<Int2> for LINKTYPE {
    fn tag() -> u16 { RTAG_LINKTYPE }
    fn expected_size() -> Option<u16> { Some(2) }
}

pub struct LINKKEYS;
impl Record<Int2> for LINKKEYS {
    fn tag() -> u16 { RTAG_LINKKEYS }
    fn expected_size() -> Option<u16> { Some(2) }
}

pub struct NODETYPE;
impl Record<Int2> for NODETYPE {
    fn tag() -> u16 { RTAG_NODETYPE }
    fn expected_size() -> Option<u16> { Some(2) }
}

pub struct PROPATTR;
impl Record<Int2> for PROPATTR {
    fn tag() -> u16 { RTAG_PROPATTR }
    fn expected_size() -> Option<u16> { Some(2) }
}

pub struct PROPVALUE;
impl Record<ASCII> for PROPVALUE {
    fn tag() -> u16 { RTAG_PROPVALUE }
    fn expected_size() -> Option<u16> { Some(2) }
}

pub struct BOX;
impl Record<Empty> for BOX {
    fn tag() -> u16 { RTAG_BOX }
    fn expected_size() -> Option<u16> { Some(0) }
}

pub struct BOXTYPE;
impl Record<Int2> for BOXTYPE {
    fn tag() -> u16 { RTAG_BOXTYPE }
    fn expected_size() -> Option<u16> { Some(2) }
}

pub struct PLEX;
impl Record<Int4> for PLEX {
    fn tag() -> u16 { RTAG_PLEX }
    fn expected_size() -> Option<u16> { Some(4) }
}

pub struct BGNEXTN;
impl Record<Int4> for BGNEXTN {
    fn tag() -> u16 { RTAG_BGNEXTN }
    fn expected_size() -> Option<u16> { Some(4) }
}

pub struct ENDEXTN;
impl Record<Int4> for ENDEXTN {
    fn tag() -> u16 { RTAG_ENDEXTN }
    fn expected_size() -> Option<u16> { Some(4) }
}

pub struct TAPENUM;
impl Record<Int2> for TAPENUM {
    fn tag() -> u16 { RTAG_TAPENUM }
    fn expected_size() -> Option<u16> { Some(2) }
}

pub struct TAPECODE;
impl Record<Int2Array> for TAPECODE {
    fn tag() -> u16 { RTAG_TAPECODE }
    fn expected_size() -> Option<u16> { Some(2 * 6) }
}

pub struct STRCLASS;
impl Record<Int2> for STRCLASS {
    fn tag() -> u16 { RTAG_STRCLASS }
    fn expected_size() -> Option<u16> { Some(2) }
}

pub struct RESERVED;
impl Record<Int2Array> for RESERVED {
    fn tag() -> u16 { RTAG_RESERVED }
    fn expected_size() -> Option<u16> { Some(2) }
}

pub struct FORMAT;
impl Record<Int2> for FORMAT {
    fn tag() -> u16 { RTAG_FORMAT }
    fn expected_size() -> Option<u16> { Some(2) }
}

pub struct MASK;
impl Record<ASCII> for MASK {
    fn tag() -> u16 { RTAG_MASK }
    fn expected_size() -> Option<u16> { None }
}

/// End of MASKS records
pub struct ENDMASKS;
impl Record<Empty> for ENDMASKS {
    fn tag() -> u16 { RTAG_ENDMASKS }
    fn expected_size() -> Option<u16> { Some(0) }
}

pub struct LIBDIRSIZE;
impl Record<Int2> for LIBDIRSIZE {
    fn tag() -> u16 { RTAG_LIBDIRSIZE }
    fn expected_size() -> Option<u16> { Some(2) }
}

pub struct SRFNAME;
impl Record<ASCII> for SRFNAME {
    fn tag() -> u16 { RTAG_SRFNAME }
    fn expected_size() -> Option<u16> { None }
}

pub struct LIBSECUR;
impl Record<Int2> for LIBSECUR {
    fn tag() -> u16 { RTAG_LIBSECUR }
    fn expected_size() -> Option<u16> { Some(2) }
}

pub struct BORDER;
impl Record<Empty> for BORDER {
    fn tag() -> u16 { RTAG_BORDER }
    fn expected_size() -> Option<u16> { Some(0) }
}

pub struct SOFTFENCE;
impl Record<Empty> for SOFTFENCE {
    fn tag() -> u16 { RTAG_SOFTFENCE }
    fn expected_size() -> Option<u16> { Some(0) }
}

pub struct HARDFENCE;
impl Record<Empty> for HARDFENCE {
    fn tag() -> u16 { RTAG_HARDFENCE }
    fn expected_size() -> Option<u16> { Some(0) }
}

pub struct SOFTWIRE;
impl Record<Empty> for SOFTWIRE {
    fn tag() -> u16 { RTAG_SOFTWIRE }
    fn expected_size() -> Option<u16> { Some(0) }
}

pub struct HARDWIRE;
impl Record<Empty> for HARDWIRE {
    fn tag() -> u16 { RTAG_HARDWIRE }
    fn expected_size() -> Option<u16> { Some(0) }
}

pub struct PATHPORT;
impl Record<Empty> for PATHPORT {
    fn tag() -> u16 { RTAG_PATHPORT }
    fn expected_size() -> Option<u16> { Some(0) }
}

pub struct NODEPORT;
impl Record<Empty> for NODEPORT {
    fn tag() -> u16 { RTAG_NODEPORT }
    fn expected_size() -> Option<u16> { Some(0) }
}

pub struct USERCONSTRAINT;
impl Record<Empty> for USERCONSTRAINT {
    fn tag() -> u16 { RTAG_USERCONSTRAINT }
    fn expected_size() -> Option<u16> { Some(0) }
}

pub struct SPACERERROR;
impl Record<Empty> for SPACERERROR {
    fn tag() -> u16 { RTAG_SPACERERROR }
    fn expected_size() -> Option<u16> { Some(0) }
}

pub struct CONTACT;
impl Record<Empty> for CONTACT {
    fn tag() -> u16 { RTAG_CONTACT }
    fn expected_size() -> Option<u16> { Some(0) }
}
