use cross::fs::FileMode;

use crate::proto::{Decode, DecodeError, Decoder, Encode, EncodeError, Encoder};

#[derive(Debug)]
pub struct RequestOpenWrite<'a> {
    pub filename: &'a str,
    pub path: &'a str,
    pub mode: FileMode,
    pub create: bool,
}

#[derive(Debug)]
pub struct ReadOpened {
    pub mode: FileMode,
    pub size: u64,
}

#[derive(Debug)]
pub enum ClientTransferControl<'a> {
    RequestOpenRead(&'a str),
    RequestOpenWrite(RequestOpenWrite<'a>),
    RequestRead(u32),
    RequestWrite(&'a [u8]),
    RequestClose,
}

#[derive(Debug)]
pub enum ServerTransferControl<'a> {
    ReadOpened(ReadOpened),
    WriteOpened,
    ReadData(&'a [u8]),
    WriteAck(u32),
    TransferError(&'a str),
}

impl ClientTransferControl<'_> {
    const TAG_REQUEST_OPEN_READ: u8 = 0x11;
    const TAG_REQUEST_OPEN_WRITE: u8 = 0x12;
    const TAG_REQUEST_READ: u8 = 0x20;
    const TAG_REQUEST_WRITE: u8 = 0x21;
    const TAG_REQUEST_CLOSE: u8 = 0x22;
}

impl Encode for ClientTransferControl<'_> {
    fn encode(&self, buffer: &mut Encoder) -> Result<(), EncodeError> {
        match self {
            Self::RequestOpenRead(filename) => {
                buffer.write(&[Self::TAG_REQUEST_OPEN_READ])?;
                buffer.write_str(filename)
            }
            Self::RequestOpenWrite(req) => {
                buffer.write(&[Self::TAG_REQUEST_OPEN_WRITE])?;
                req.encode(buffer)
            }
            Self::RequestRead(len) => {
                buffer.write(&[Self::TAG_REQUEST_READ])?;
                buffer.write(&len.to_le_bytes())
            }
            Self::RequestWrite(bytes) => {
                buffer.write(&[Self::TAG_REQUEST_WRITE])?;
                buffer.write_variable_bytes(bytes)
            }
            Self::RequestClose => buffer.write(&[Self::TAG_REQUEST_CLOSE]),
        }
    }
}

impl<'de> Decode<'de> for ClientTransferControl<'de> {
    fn decode(buffer: &mut Decoder<'de>) -> Result<Self, DecodeError> {
        let tag = buffer.read_u8()?;
        match tag {
            Self::TAG_REQUEST_OPEN_READ => buffer.read_str().map(Self::RequestOpenRead),
            Self::TAG_REQUEST_OPEN_WRITE => {
                RequestOpenWrite::decode(buffer).map(Self::RequestOpenWrite)
            }
            Self::TAG_REQUEST_READ => buffer.read_le_u32().map(Self::RequestRead),
            Self::TAG_REQUEST_WRITE => buffer.read_variable_bytes().map(Self::RequestWrite),
            Self::TAG_REQUEST_CLOSE => Ok(Self::RequestClose),
            _ => Err(DecodeError::InvalidMessage(
                "Invalid ClientTransferControl tag",
            )),
        }
    }
}

impl ServerTransferControl<'_> {
    const TAG_READ_OPENED: u8 = 0x11;
    const TAG_WRITE_OPENED: u8 = 0x12;
    const TAG_READ_DATA: u8 = 0x20;
    const TAG_WRITE_ACK: u8 = 0x21;
    const TAG_TRANSFER_ERROR: u8 = 0x30;
}

impl Encode for ServerTransferControl<'_> {
    fn encode(&self, buffer: &mut Encoder) -> Result<(), EncodeError> {
        match self {
            Self::ReadOpened(info) => {
                buffer.write(&[Self::TAG_READ_OPENED])?;
                info.encode(buffer)
            }
            Self::WriteOpened => buffer.write(&[Self::TAG_WRITE_OPENED]),
            Self::ReadData(data) => {
                buffer.write(&[Self::TAG_READ_DATA])?;
                buffer.write_variable_bytes(data)
            }
            Self::WriteAck(count) => {
                buffer.write(&[Self::TAG_WRITE_ACK])?;
                buffer.write(&count.to_le_bytes())
            }
            Self::TransferError(error) => {
                buffer.write(&[Self::TAG_TRANSFER_ERROR])?;
                buffer.write_str(error)
            }
        }
    }
}

impl<'de> Decode<'de> for ServerTransferControl<'de> {
    fn decode(buffer: &mut Decoder<'de>) -> Result<Self, DecodeError> {
        let tag = buffer.read_u8()?;
        match tag {
            Self::TAG_READ_OPENED => ReadOpened::decode(buffer).map(Self::ReadOpened),
            Self::TAG_WRITE_OPENED => Ok(Self::WriteOpened),
            Self::TAG_READ_DATA => buffer.read_variable_bytes().map(Self::ReadData),
            Self::TAG_WRITE_ACK => buffer.read_le_u32().map(Self::WriteAck),
            Self::TAG_TRANSFER_ERROR => buffer.read_str().map(Self::TransferError),
            _ => Err(DecodeError::InvalidMessage(
                "Invalid ServerTransferControl tag",
            )),
        }
    }
}

impl Encode for ReadOpened {
    fn encode(&self, buffer: &mut Encoder) -> Result<(), EncodeError> {
        buffer.write(&self.size.to_le_bytes())?;
        buffer.write(&self.mode.bits().to_le_bytes())?;
        Ok(())
    }
}

impl<'de> Decode<'de> for ReadOpened {
    fn decode(buffer: &mut Decoder<'de>) -> Result<Self, DecodeError> {
        let size = buffer.read_le_u64()?;
        let mode = buffer.read_le_u32().map(FileMode::from)?;
        Ok(Self { size, mode })
    }
}

impl Encode for RequestOpenWrite<'_> {
    fn encode(&self, buffer: &mut Encoder) -> Result<(), EncodeError> {
        buffer.write_str(self.filename)?;
        buffer.write_str(self.path)?;
        buffer.write(&self.mode.bits().to_le_bytes())?;
        buffer.write(&[self.create as u8])
    }
}

impl<'de> Decode<'de> for RequestOpenWrite<'de> {
    fn decode(buffer: &mut Decoder<'de>) -> Result<Self, DecodeError> {
        let filename = buffer.read_str()?;
        let path = buffer.read_str()?;
        let mode = buffer.read_le_u32().map(FileMode::from)?;
        let create = buffer.read_u8()? != 0;
        Ok(Self {
            filename,
            path,
            mode,
            create,
        })
    }
}
