use smoltcp::socket::tcp::{Socket, State};

use crate::proto::{
    serialize_response_error, serialize_response_value, PacketParser, ResponsePacket, Settings, ErrorCodes
};

pub struct CommandInterface {
    parser: PacketParser,
}

impl CommandInterface {
    pub fn new() -> Self {
        Self {
            parser: PacketParser::new(),
        }
    }

    /// Run the command interface
    pub fn run(&mut self, sock: &mut Socket) {
        // Note that we don't attempt to check status of the connection. All of
        // the smoltcp functions handle the error cases gracefully. Recv
        // provides a buffer of length 0 when there is nothing to receive, so we
        // are good there. We just reset on any smoltcp errors.

        // TODO should put packet parsing behind a fn with the smoltcp result, then on smoltcp error
        // we reconfigure here instead of calling it 4 times

        // Try and parse a packet
        let res = sock.recv(|rx_buf| {
            let (res, processed_bytes) = self.parser.try_parse(rx_buf);

            (processed_bytes, res.ok())
        });

        // Check for socket errors
        let packet = match res {
            Ok(Some(packet)) => packet,
            // No packet to process
            Ok(None) => return,
            // Any socket error means we just want to drop the connection and re-connect
            Err(_) => {
                Self::reestablish_socket(sock);
                return;
            }
        };

        // Check that setting is valid
        let setting = match Settings::try_from(packet.setting) {
            Ok(v) => v,
            Err(()) => {
                // Write out an error packet
                let result = sock.send(|tx_buf| {
                    if tx_buf.len() < 8 {
                        return (0, ());
                    }

                    let response = serialize_response_error(packet.setting, ErrorCodes::InvalidSetting);
                    &tx_buf[0..8].copy_from_slice(&response);
                    return (8, ());
                });

                if let Err(_) = result {
                    // TX error, close socket and reconnect
                    Self::reestablish_socket(sock);
                }

                return;
            }
        };

        // TODO validate setting values

        // TODO handle actually changing/getting settings in all the ways
        // For temp testing, return values sent
        match sock.send(|tx_buf| {
            if tx_buf.len() < 8 {
                // Since this is a low-BW configuration socket, we assume we have space, and drop
                // the response otherwise. If this is an issue, may re-think this and queue commands but
                // for now this is fine.
                return (0, ());
            }
            let response = serialize_response_value(setting, packet.value);
            &tx_buf[0..8].copy_from_slice(&response);
            return (8, ());
        }) {
            Ok(()) => (),
            Err(_) => {
                Self::reestablish_socket(sock);
            }
        };
    }

    fn reestablish_socket(sock: &mut Socket) {
        sock.abort();
        sock.listen(2000);
    }
}
