//! `Light` type.

use super::{ltfn, TICKS_PER_SECOND};
use crate::{err::*, fixed::Fixed};

/// Reads a `LITE` chunk.
pub fn read(b: &[u8]) -> ResultS<(Light, usize)>
{
	read_data! {
		endian: BIG, buf: b, size: 100, start: 0, data {
			let ltype   = u16[0] enum LightType;
			let flags   = u16[2] flag LightFlags;
			let phase   = i16[4];
			let act_pri = ltfn::read[6;  14];
			let act_sec = ltfn::read[20; 14];
			let act_mid = ltfn::read[34; 14];
			let ina_pri = ltfn::read[48; 14];
			let ina_sec = ltfn::read[62; 14];
			let ina_mid = ltfn::read[76; 14];
			let tag     = u16[90];
		}
	}

	Ok((Light{ltype, flags, phase, act_pri, act_sec, act_mid, ina_pri, ina_sec,
	          ina_mid, tag}, 100))
}

/// Reads an old `LITE` chunk.
pub fn read_old(b: &[u8]) -> ResultS<(Light, usize)>
{
	read_data! {
		endian: BIG, buf: b, size: 32, start: 0, data {
			let ltype = u16[2] usize;
			let mode  = u16[4];
			let phase = i16[6];
			let min   = Fixed[8];
			let max   = Fixed[12];
			let prd   = u16[16];
		}
	}

	if OLD_LIGHT_DEFINITIONS.len() < ltype {
		bail!("bad old light type");
	}

	let lite   = &OLD_LIGHT_DEFINITIONS[ltype];
	let on     = mode == 0 || mode == 1;
	let strobe = ltype == 3;
	let flags  = if on {lite.flags | LightFlags::INIT_ACTIVE} else {lite.flags};

	// modify each old light function accordingly
	let old_lfun = move |func: &ltfn::LightFunc| -> ltfn::LightFunc {
		ltfn::LightFunc{ftype:   func.ftype,
		                prd_nrm: if strobe {prd / 4 + 1} else {func.prd_nrm},
		                prd_dta: func.prd_dta,
		                val_nrm: if func.val_nrm > 0.into() {max} else {min},
		                val_dta: func.val_dta}
	};

	Ok((Light{flags,
	          phase,
	          act_pri: old_lfun(&lite.act_pri),
	          act_sec: old_lfun(&lite.act_sec),
	          act_mid: old_lfun(&lite.act_mid),
	          ina_pri: old_lfun(&lite.ina_pri),
	          ina_sec: old_lfun(&lite.ina_sec),
	          ina_mid: old_lfun(&lite.ina_mid),
	          tag: 0,
	          ..*lite}, 32))
}

/// A dynamic polygon light.
#[cfg_attr(feature = "serde_obj", derive(serde::Serialize))]
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Light {
	pub ltype:   LightType,
	pub flags:   LightFlags,
	pub phase:   i16,
	pub act_pri: ltfn::LightFunc,
	pub act_sec: ltfn::LightFunc,
	pub act_mid: ltfn::LightFunc,
	pub ina_pri: ltfn::LightFunc,
	pub ina_sec: ltfn::LightFunc,
	pub ina_mid: ltfn::LightFunc,
	pub tag:     u16,
}

c_bitfield! {
	/// Flags for `Light`.
	#[cfg_attr(feature = "serde_obj", derive(serde::Serialize))]
	pub struct LightFlags: u16 {
		INIT_ACTIVE = 0,
		SLAVE_VALUE = 1,
		STATELESS   = 2,
	}
}

c_enum! {
	/// The type of a `Light`.
	#[cfg_attr(feature = "serde_obj", derive(serde::Serialize))]
	pub enum LightType: u16 {
		Normal = 0,
		Strobe = 1,
		Media  = 2,
	}
}

const OLD_LIGHT_DEFINITIONS: [Light; 8] = [
	// Normal
	Light{ltype: LightType::Normal,
	      flags: LightFlags::SLAVE_VALUE,
	      phase: 0,
	      act_pri: ltfn::LightFunc{ftype:   ltfn::LightFuncType::Constant,
	      prd_nrm: TICKS_PER_SECOND,
	      prd_dta: 0,
	      val_nrm: Fixed::from_int(1),
	      val_dta: Fixed::from_int(0)},
	      act_sec: ltfn::LightFunc{ftype:   ltfn::LightFuncType::Constant,
	      prd_nrm: TICKS_PER_SECOND,
	      prd_dta: 0,
	      val_nrm: Fixed::from_int(1),
	      val_dta: Fixed::from_int(0)},
	      act_mid: ltfn::LightFunc{ftype:   ltfn::LightFuncType::Constant,
	      prd_nrm: 1,
	      prd_dta: 0,
	      val_nrm: Fixed::from_int(1),
	      val_dta: Fixed::from_int(0)},
	      ina_pri: ltfn::LightFunc{ftype:   ltfn::LightFuncType::Constant,
	      prd_nrm: TICKS_PER_SECOND,
	      prd_dta: 0,
	      val_nrm: Fixed::from_int(0),
	      val_dta: Fixed::from_int(0)},
	      ina_sec: ltfn::LightFunc{ftype:   ltfn::LightFuncType::Constant,
	      prd_nrm: TICKS_PER_SECOND,
	      prd_dta: 0,
	      val_nrm: Fixed::from_int(0),
	      val_dta: Fixed::from_int(0)},
	      ina_mid: ltfn::LightFunc{ftype:   ltfn::LightFuncType::Constant,
	      prd_nrm: 1,
	      prd_dta: 0,
	      val_nrm: Fixed::from_int(1),
	      val_dta: Fixed::from_int(0)},
	      tag: 0},

	// Rheostat
	Light{ltype: LightType::Normal,
	      flags: LightFlags::SLAVE_VALUE,
	      phase: 0,
	      act_pri: ltfn::LightFunc{ftype:   ltfn::LightFuncType::Constant,
	      prd_nrm: TICKS_PER_SECOND,
	      prd_dta: 0,
	      val_nrm: Fixed::from_int(1),
	      val_dta: Fixed::from_int(0)},
	      act_sec: ltfn::LightFunc{ftype:   ltfn::LightFuncType::Constant,
	      prd_nrm: TICKS_PER_SECOND,
	      prd_dta: 0,
	      val_nrm: Fixed::from_int(1),
	      val_dta: Fixed::from_int(0)},
	      act_mid: ltfn::LightFunc{ftype:   ltfn::LightFuncType::Smooth,
	      prd_nrm: TICKS_PER_SECOND * 3,
	      prd_dta: 0,
	      val_nrm: Fixed::from_int(1),
	      val_dta: Fixed::from_int(0)},
	      ina_pri: ltfn::LightFunc{ftype:   ltfn::LightFuncType::Constant,
	      prd_nrm: TICKS_PER_SECOND,
	      prd_dta: 0,
	      val_nrm: Fixed::from_int(0),
	      val_dta: Fixed::from_int(0)},
	      ina_sec: ltfn::LightFunc{ftype:   ltfn::LightFuncType::Constant,
	      prd_nrm: TICKS_PER_SECOND,
	      prd_dta: 0,
	      val_nrm: Fixed::from_int(0),
	      val_dta: Fixed::from_int(0)},
	      ina_mid: ltfn::LightFunc{ftype:   ltfn::LightFuncType::Smooth,
	      prd_nrm: TICKS_PER_SECOND * 3,
	      prd_dta: 0,
	      val_nrm: Fixed::from_int(0),
	      val_dta: Fixed::from_int(0)},
	      tag: 0},

	// Flourescent
	Light{ltype: LightType::Normal,
	      flags: LightFlags::SLAVE_VALUE,
	      phase: 0,
	      act_pri: ltfn::LightFunc{ftype:   ltfn::LightFuncType::Constant,
	      prd_nrm: TICKS_PER_SECOND,
	      prd_dta: 0,
	      val_nrm: Fixed::from_int(1),
	      val_dta: Fixed::from_int(0)},
	      act_sec: ltfn::LightFunc{ftype:   ltfn::LightFuncType::Constant,
	      prd_nrm: TICKS_PER_SECOND,
	      prd_dta: 0,
	      val_nrm: Fixed::from_int(1),
	      val_dta: Fixed::from_int(0)},
	      act_mid: ltfn::LightFunc{ftype:   ltfn::LightFuncType::Fluorescent,
	      prd_nrm: TICKS_PER_SECOND * 3,
	      prd_dta: 0,
	      val_nrm: Fixed::from_int(1),
	      val_dta: Fixed::from_int(0)},
	      ina_pri: ltfn::LightFunc{ftype:   ltfn::LightFuncType::Constant,
	      prd_nrm: TICKS_PER_SECOND,
	      prd_dta: 0,
	      val_nrm: Fixed::from_int(0),
	      val_dta: Fixed::from_int(0)},
	      ina_sec: ltfn::LightFunc{ftype:   ltfn::LightFuncType::Constant,
	      prd_nrm: TICKS_PER_SECOND,
	      prd_dta: 0,
	      val_nrm: Fixed::from_int(0),
	      val_dta: Fixed::from_int(0)},
	      ina_mid: ltfn::LightFunc{ftype:   ltfn::LightFuncType::Constant,
	      prd_nrm: 1,
	      prd_dta: 0,
	      val_nrm: Fixed::from_int(0),
	      val_dta: Fixed::from_int(0)},
	      tag: 0},

	// Strobe
	Light{ltype: LightType::Normal,
	      flags: LightFlags::SLAVE_VALUE,
	      phase: 0,
	      act_pri: ltfn::LightFunc{ftype:   ltfn::LightFuncType::Constant,
	      prd_nrm: TICKS_PER_SECOND,
	      prd_dta: 0,
	      val_nrm: Fixed::from_int(1),
	      val_dta: Fixed::from_int(0)},
	      act_sec: ltfn::LightFunc{ftype:   ltfn::LightFuncType::Constant,
	      prd_nrm: TICKS_PER_SECOND,
	      prd_dta: 0,
	      val_nrm: Fixed::from_int(0),
	      val_dta: Fixed::from_int(0)},
	      act_mid: ltfn::LightFunc{ftype:   ltfn::LightFuncType::Constant,
	      prd_nrm: 1,
	      prd_dta: 0,
	      val_nrm: Fixed::from_int(1),
	      val_dta: Fixed::from_int(0)},
	      ina_pri: ltfn::LightFunc{ftype:   ltfn::LightFuncType::Constant,
	      prd_nrm: TICKS_PER_SECOND,
	      prd_dta: 0,
	      val_nrm: Fixed::from_int(0),
	      val_dta: Fixed::from_int(0)},
	      ina_sec: ltfn::LightFunc{ftype:   ltfn::LightFuncType::Constant,
	      prd_nrm: TICKS_PER_SECOND,
	      prd_dta: 0,
	      val_nrm: Fixed::from_int(1),
	      val_dta: Fixed::from_int(0)},
	      ina_mid: ltfn::LightFunc{ftype:   ltfn::LightFuncType::Constant,
	      prd_nrm: 1,
	      prd_dta: 0,
	      val_nrm: Fixed::from_int(0),
	      val_dta: Fixed::from_int(0)},
	      tag: 0},

	// Flicker
	Light{ltype: LightType::Normal,
	      flags: LightFlags::SLAVE_VALUE,
	      phase: 0,
	      act_pri: ltfn::LightFunc{ftype:   ltfn::LightFuncType::Constant,
	      prd_nrm: TICKS_PER_SECOND,
	      prd_dta: 0,
	      val_nrm: Fixed::from_int(1),
	      val_dta: Fixed::from_int(0)},
	      act_sec: ltfn::LightFunc{ftype:   ltfn::LightFuncType::Constant,
	      prd_nrm: TICKS_PER_SECOND,
	      prd_dta: 0,
	      val_nrm: Fixed::from_int(1),
	      val_dta: Fixed::from_int(0)},
	      act_mid: ltfn::LightFunc{ftype:   ltfn::LightFuncType::Flicker,
	      prd_nrm: TICKS_PER_SECOND * 3,
	      prd_dta: 0,
	      val_nrm: Fixed::from_int(1),
	      val_dta: Fixed::from_int(0)},
	      ina_pri: ltfn::LightFunc{ftype:   ltfn::LightFuncType::Constant,
	      prd_nrm: TICKS_PER_SECOND,
	      prd_dta: 0,
	      val_nrm: Fixed::from_int(0),
	      val_dta: Fixed::from_int(0)},
	      ina_sec: ltfn::LightFunc{ftype:   ltfn::LightFuncType::Constant,
	      prd_nrm: TICKS_PER_SECOND,
	      prd_dta: 0,
	      val_nrm: Fixed::from_int(0),
	      val_dta: Fixed::from_int(0)},
	      ina_mid: ltfn::LightFunc{ftype:   ltfn::LightFuncType::Constant,
	      prd_nrm: 1,
	      prd_dta: 0,
	      val_nrm: Fixed::from_int(0),
	      val_dta: Fixed::from_int(0)},
	      tag: 0},

	// Pulsate
	Light{ltype: LightType::Normal,
	      flags: LightFlags::SLAVE_VALUE,
	      phase: 0,
	      act_pri: ltfn::LightFunc{ftype:   ltfn::LightFuncType::Smooth,
	      prd_nrm: TICKS_PER_SECOND * 2,
	      prd_dta: 0,
	      val_nrm: Fixed::from_int(1),
	      val_dta: Fixed::from_int(0)},
	      act_sec: ltfn::LightFunc{ftype:   ltfn::LightFuncType::Smooth,
	      prd_nrm: TICKS_PER_SECOND * 2 - 1,
	      prd_dta: 0,
	      val_nrm: Fixed::from_int(0),
	      val_dta: Fixed::from_int(0)},
	      act_mid: ltfn::LightFunc{ftype:   ltfn::LightFuncType::Smooth,
	      prd_nrm: TICKS_PER_SECOND * 2 - 1,
	      prd_dta: 0,
	      val_nrm: Fixed::from_int(1),
	      val_dta: Fixed::from_int(0)},
	      ina_pri: ltfn::LightFunc{ftype:   ltfn::LightFuncType::Smooth,
	      prd_nrm: TICKS_PER_SECOND * 2,
	      prd_dta: 0,
	      val_nrm: Fixed::from_int(0),
	      val_dta: Fixed::from_int(0)},
	      ina_sec: ltfn::LightFunc{ftype:   ltfn::LightFuncType::Smooth,
	      prd_nrm: TICKS_PER_SECOND * 2 - 1,
	      prd_dta: 0,
	      val_nrm: Fixed::from_int(1),
	      val_dta: Fixed::from_int(0)},
	      ina_mid: ltfn::LightFunc{ftype:   ltfn::LightFuncType::Smooth,
	      prd_nrm: TICKS_PER_SECOND * 2,
	      prd_dta: 0,
	      val_nrm: Fixed::from_int(0),
	      val_dta: Fixed::from_int(0)},
	      tag: 0},

	// Annoying
	Light{ltype: LightType::Normal,
	      flags: LightFlags::SLAVE_VALUE,
	      phase: 0,
	      act_pri: ltfn::LightFunc{ftype:   ltfn::LightFuncType::Random,
	      prd_nrm: 2,
	      prd_dta: 1,
	      val_nrm: Fixed::from_int(1),
	      val_dta: Fixed::from_int(0)},
	      act_sec: ltfn::LightFunc{ftype:   ltfn::LightFuncType::Constant,
	      prd_nrm: 2,
	      prd_dta: 0,
	      val_nrm: Fixed::from_int(0),
	      val_dta: Fixed::from_int(0)},
	      act_mid: ltfn::LightFunc{ftype:   ltfn::LightFuncType::Random,
	      prd_nrm: 1,
	      prd_dta: 0,
	      val_nrm: Fixed::from_int(1),
	      val_dta: Fixed::from_int(0)},
	      ina_pri: ltfn::LightFunc{ftype:   ltfn::LightFuncType::Constant,
	      prd_nrm: TICKS_PER_SECOND,
	      prd_dta: 0,
	      val_nrm: Fixed::from_int(0),
	      val_dta: Fixed::from_int(0)},
	      ina_sec: ltfn::LightFunc{ftype:   ltfn::LightFuncType::Constant,
	      prd_nrm: TICKS_PER_SECOND,
	      prd_dta: 0,
	      val_nrm: Fixed::from_int(0),
	      val_dta: Fixed::from_int(0)},
	      ina_mid: ltfn::LightFunc{ftype:   ltfn::LightFuncType::Constant,
	      prd_nrm: TICKS_PER_SECOND,
	      prd_dta: 0,
	      val_nrm: Fixed::from_int(0),
	      val_dta: Fixed::from_int(0)},
	      tag: 0},

	// Energy Efficient
	Light{ltype: LightType::Normal,
	      flags: LightFlags::SLAVE_VALUE,
	      phase: 0,
	      act_pri: ltfn::LightFunc{ftype:   ltfn::LightFuncType::Constant,
	      prd_nrm: TICKS_PER_SECOND,
	      prd_dta: 0,
	      val_nrm: Fixed::from_int(1),
	      val_dta: Fixed::from_int(0)},
	      act_sec: ltfn::LightFunc{ftype:   ltfn::LightFuncType::Constant,
	      prd_nrm: TICKS_PER_SECOND,
	      prd_dta: 0,
	      val_nrm: Fixed::from_int(0),
	      val_dta: Fixed::from_int(0)},
	      act_mid: ltfn::LightFunc{ftype:   ltfn::LightFuncType::Linear,
	      prd_nrm: TICKS_PER_SECOND * 2,
	      prd_dta: 0,
	      val_nrm: Fixed::from_int(1),
	      val_dta: Fixed::from_int(0)},
	      ina_pri: ltfn::LightFunc{ftype:   ltfn::LightFuncType::Constant,
	      prd_nrm: TICKS_PER_SECOND,
	      prd_dta: 0,
	      val_nrm: Fixed::from_int(0),
	      val_dta: Fixed::from_int(0)},
	      ina_sec: ltfn::LightFunc{ftype:   ltfn::LightFuncType::Constant,
	      prd_nrm: TICKS_PER_SECOND,
	      prd_dta: 0,
	      val_nrm: Fixed::from_int(0),
	      val_dta: Fixed::from_int(0)},
	      ina_mid: ltfn::LightFunc{ftype:   ltfn::LightFuncType::Linear,
	      prd_nrm: TICKS_PER_SECOND * 2,
	      prd_dta: 0,
	      val_nrm: Fixed::from_int(0),
	      val_dta: Fixed::from_int(0)},
	      tag: 0},
	];

// EOF
