#![allow(missing_docs)]
use std::{mem, fmt};
use {ErrorCode, FeatureCode, VcpValue};

pub trait Command {
	type Ok: CommandResult;
	const MIN_LEN: usize;
	const MAX_LEN: usize;
	const DELAY_RESPONSE_MS: u64;
	const DELAY_COMMAND_MS: u64;

	fn len(&self) -> usize;

	fn encode(&self, data: &mut [u8]) -> Result<usize, ErrorCode>;
}

pub trait CommandResult: Sized {
	const MAX_LEN: usize;
	fn decode(data: &[u8]) -> Result<Self, ErrorCode>;
}

#[derive(Copy, Clone, Debug)]
pub struct GetVcpFeature {
	pub code: FeatureCode,
}

impl GetVcpFeature {
	pub fn new(code: FeatureCode) -> Self {
		GetVcpFeature {
			code: code,
		}
	}
}

impl Command for GetVcpFeature {
	type Ok = VcpValue;
	const MIN_LEN: usize = 2;
	const MAX_LEN: usize = 2;
	const DELAY_RESPONSE_MS: u64 = 40;
	const DELAY_COMMAND_MS: u64 = 50; // the spec omits this, but 50 corresponds with what all other commands suggest

	fn len(&self) -> usize { 2 }

	fn encode(&self, data: &mut [u8]) -> Result<usize, ErrorCode> {
		assert!(data.len() >= 2);
		data[0] = 0x01;
		data[1] = self.code;

		Ok(2)
	}
}

#[derive(Copy, Clone, Debug)]
pub struct SetVcpFeature {
	pub code: FeatureCode,
	pub value: u16,
}

impl SetVcpFeature {
	pub fn new(code: FeatureCode, value: u16) -> Self {
		SetVcpFeature {
			code: code,
			value: value,
		}
	}
}

impl Command for SetVcpFeature {
	type Ok = ();
	const MIN_LEN: usize = 4;
	const MAX_LEN: usize = 4;
	const DELAY_RESPONSE_MS: u64 = 0;
	const DELAY_COMMAND_MS: u64 = 50;

	fn len(&self) -> usize { 4 }

	fn encode(&self, data: &mut [u8]) -> Result<usize, ErrorCode> {
		assert!(data.len() >= 4);

		data[0] = 0x03;
		data[1] = self.code;
		data[2] = (self.value >> 8) as _;
		data[3] = self.value as _;

		Ok(4)
	}
}

impl CommandResult for VcpValue {
	const MAX_LEN: usize = 8;

	fn decode(data: &[u8]) -> Result<Self, ErrorCode> {
		if data.len() != 8 {
			return Err(ErrorCode::InvalidLength)
		}

		if data[0] != 0x02 {
			return Err(ErrorCode::InvalidOpcode)
		}

		match data[1] {
			0x00 => (), // NoError
			0x01 => return Err(ErrorCode::Invalid("Unsupported VCP code".into())),
			rc => return Err(ErrorCode::Invalid(format!("Unrecognized VCP error code 0x{:02x}", rc))),
		}

		// data[2] == vcp code from request

		Ok(VcpValue {
			ty: data[2],
			mh: data[4],
			ml: data[5],
			sh: data[6],
			sl: data[7],
		})
	}
}

#[derive(Copy, Clone, Debug)]
pub struct SaveCurrentSettings;

impl Command for SaveCurrentSettings {
	type Ok = ();
	const MIN_LEN: usize = 1;
	const MAX_LEN: usize = 1;
	const DELAY_RESPONSE_MS: u64 = 0;
	const DELAY_COMMAND_MS: u64 = 200;

	fn len(&self) -> usize { 1 }

	fn encode(&self, data: &mut [u8]) -> Result<usize, ErrorCode> {
		assert!(data.len() >= 1);
		data[0] = 0x0c;

		Ok(1)
	}
}

#[derive(Copy, Clone, Debug)]
pub struct TableWrite<'a> {
	pub code: FeatureCode,
	pub offset: u16,
	pub data: &'a [u8],
}

impl<'a> TableWrite<'a> {
	pub fn new(code: FeatureCode, offset: u16, data: &'a [u8]) -> Self {
		TableWrite {
			code: code,
			offset: offset,
			data: data,
		}
	}
}

impl<'a> Command for TableWrite<'a> {
	type Ok = ();
	const MIN_LEN: usize = 4;
	const MAX_LEN: usize = 4 + 32; // Spec says this should be 3~35 but allows 32 bytes of data transfer?? how?? What does "P=1" mean?
	const DELAY_RESPONSE_MS: u64 = 0;
	const DELAY_COMMAND_MS: u64 = 50;

	fn len(&self) -> usize { 4 + self.data.len() }

	fn encode(&self, data: &mut [u8]) -> Result<usize, ErrorCode> {
		assert!(data.len() >= 4 + self.data.len());
		assert!(self.data.len() <= 32);

		data[0] = 0xe7;
		data[1] = self.code;
		data[2] = (self.offset >> 8) as _;
		data[3] = self.offset as _;
		data[4..4 + self.data.len()].copy_from_slice(self.data);

		Ok(4 + self.data.len())
	}
}

#[derive(Copy, Clone, Debug)]
pub struct TableRead {
	pub code: FeatureCode,
	pub offset: u16,
}

impl TableRead {
	pub fn new(code: FeatureCode, offset: u16) -> Self {
		TableRead {
			code: code,
			offset: offset,
		}
	}
}

impl Command for TableRead {
	type Ok = TableResponse;
	const MIN_LEN: usize = 4;
	const MAX_LEN: usize = 4;
	const DELAY_RESPONSE_MS: u64 = 40;
	const DELAY_COMMAND_MS: u64 = 50;

	fn len(&self) -> usize { 4 }

	fn encode(&self, data: &mut [u8]) -> Result<usize, ErrorCode> {
		assert!(data.len() >= 4);

		data[0] = 0xe2;
		data[1] = self.code;
		data[2] = (self.offset >> 8) as _;
		data[3] = self.offset as _;

		Ok(4)
	}
}

#[derive(Copy, Clone, Debug)]
pub struct CapabilitiesRequest {
	pub offset: u16,
}

impl CapabilitiesRequest {
	pub fn new(offset: u16) -> Self {
		CapabilitiesRequest {
			offset: offset,
		}
	}
}

impl Command for CapabilitiesRequest {
	type Ok = CapabilitiesReply;
	const MIN_LEN: usize = 3;
	const MAX_LEN: usize = 3;
	const DELAY_RESPONSE_MS: u64 = 40;
	const DELAY_COMMAND_MS: u64 = 50;

	fn len(&self) -> usize { 3 }

	fn encode(&self, data: &mut [u8]) -> Result<usize, ErrorCode> {
		assert!(data.len() >= 3);

		data[0] = 0xf3;
		data[1] = (self.offset >> 8) as _;
		data[2] = self.offset as _;

		Ok(3)
	}
}

#[derive(Copy, Clone)]
pub struct TableResponse {
	pub offset: u16,
	data: [u8; 32],
	len: u8,
}

impl TableResponse {
	pub fn bytes(&self) -> &[u8] {
		&self.data[..self.len as usize]
	}
}

impl fmt::Debug for TableResponse {
	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
		f.debug_struct("TableResponse")
			.field("offset", &self.offset)
			.field("bytes", &self.bytes())
			.finish()
	}
}

impl Default for TableResponse {
	fn default() -> Self {
		unsafe { mem::zeroed() }
	}
}

impl CommandResult for TableResponse {
	const MAX_LEN: usize = 36;

	fn decode(data: &[u8]) -> Result<Self, ErrorCode> {
		if data.len() < 4 || data.len() > 36  { // spec says 3 - 35???
			return Err(ErrorCode::InvalidLength)
		}

		if data[0] != 0xe4 {
			return Err(ErrorCode::InvalidOpcode)
		}

		let mut table = TableResponse::default();
		table.offset = ((data[1] as u16) << 8) | data[2] as u16;
		let data = &data[3..];
		table.len = data.len() as u8;
		table.data[..data.len()].copy_from_slice(data);
		Ok(table)
	}
}

#[derive(Clone, Debug)]
pub struct CapabilitiesReply {
	pub offset: u16,
	pub data: Box<[u8]>,
}

impl CommandResult for CapabilitiesReply {
	const MAX_LEN: usize = 35;

	fn decode(data: &[u8]) -> Result<Self, ErrorCode> {
		if data.len() < 3 || data.len() > 35  {
			return Err(ErrorCode::InvalidLength)
		}

		if data[0] != 0xe3 {
			return Err(ErrorCode::InvalidOpcode)
		}

		Ok(CapabilitiesReply {
			offset: ((data[1] as u16) << 8) | data[2] as u16,
			data: data[3..].to_owned().into_boxed_slice(),
		})
	}
}

#[derive(Copy, Clone, Debug)]
pub struct GetTimingReport;

impl Command for GetTimingReport {
	type Ok = TimingMessage;
	const MIN_LEN: usize = 1;
	const MAX_LEN: usize = 1;
	const DELAY_RESPONSE_MS: u64 = 40;
	const DELAY_COMMAND_MS: u64 = 50;

	fn len(&self) -> usize { 1 }

	fn encode(&self, data: &mut [u8]) -> Result<usize, ErrorCode> {
		assert!(data.len() >= 1);
		data[0] = 0x07;

		Ok(1)
	}
}

#[derive(Clone, Debug)]
pub struct TimingMessage {
	pub timing_status: u8,
	pub horizontal_frequency: u16,
	pub vertical_frequency: u16,
}

impl CommandResult for TimingMessage {
	const MAX_LEN: usize = 6;

	fn decode(data: &[u8]) -> Result<Self, ErrorCode> {
		if data.len() != 6 {
			return Err(ErrorCode::InvalidLength)
		}

		if data[0] != 0x4e {
			return Err(ErrorCode::InvalidOpcode)
		}

		Ok(TimingMessage {
			timing_status: data[1],
			horizontal_frequency: ((data[2] as u16) << 8) | data[3] as u16,
			vertical_frequency: ((data[4] as u16) << 8) | data[5] as u16,
		})
	}
}

impl CommandResult for () {
	const MAX_LEN: usize = 0;

	fn decode(data: &[u8]) -> Result<Self, ErrorCode> {
		if data.is_empty() {
			Ok(())
		} else {
			Err(ErrorCode::InvalidLength)
		}
	}
}

impl<'a, C: Command> Command for &'a C {
	type Ok = C::Ok;
	const MIN_LEN: usize = C::MIN_LEN;
	const MAX_LEN: usize = C::MAX_LEN;
	const DELAY_RESPONSE_MS: u64 = C::DELAY_RESPONSE_MS;
	const DELAY_COMMAND_MS: u64 = C::DELAY_COMMAND_MS;

	fn len(&self) -> usize { (*self).len() }

	fn encode(&self, data: &mut [u8]) -> Result<usize, ErrorCode> {
		(*self).encode(data)
	}
}
