use super::prelude::*;
use crate::common::Endianness;
use crate::common::Pid;
use crate::protocol::commands::ext::HostInfo;
use crate::protocol::commands::ext::ProcessInfo;
use crate::protocol::ResponseWriterError;
use crate::target::ext::host_info::HostInfoResponse;
use crate::target::ext::process_info::ProcessInfoResponse;

// Convert the two distinct public enums to a single unified enum internally, so
// that we can reuse serialization code when convenient.
pub(crate) enum InfoResponse<'a> {
    Pid(Pid),
    Triple(&'a str),
    Endianness(Endianness),
    PointerSize(usize),
}

impl<'a> InfoResponse<'a> {
    pub(crate) fn write_response<C: Connection>(
        &self,
        res: &mut ResponseWriter<'_, C>,
    ) -> Result<(), ResponseWriterError<C::Error>> {
        match self {
            InfoResponse::Pid(pid) => {
                res.write_str("pid:")?;
                res.write_dec(usize::from(*pid))?;
            }
            InfoResponse::Triple(triple) => {
                res.write_str("triple:")?;
                res.write_hex_buf(triple.as_bytes())?;
            }
            InfoResponse::Endianness(endian) => {
                res.write_str("endian:")?;
                res.write_str(match endian {
                    Endianness::Big => "big;",
                    Endianness::Little => "little;",
                })?;
            }
            InfoResponse::PointerSize(p) => {
                res.write_str("ptrsize:")?;
                res.write_dec(*p)?;
            }
        }
        res.write_str(";")?;
        Ok(())
    }
}

impl<'a> From<&HostInfoResponse<'a>> for InfoResponse<'a> {
    fn from(resp: &HostInfoResponse<'a>) -> Self {
        match *resp {
            HostInfoResponse::Triple(s) => InfoResponse::Triple(s),
            HostInfoResponse::Endianness(e) => InfoResponse::Endianness(e),
            HostInfoResponse::PointerSize(p) => InfoResponse::PointerSize(p),
        }
    }
}

impl<'a> From<&ProcessInfoResponse<'a>> for InfoResponse<'a> {
    fn from(resp: &ProcessInfoResponse<'a>) -> Self {
        match *resp {
            ProcessInfoResponse::Pid(pid) => InfoResponse::Pid(pid),
            ProcessInfoResponse::Triple(s) => InfoResponse::Triple(s),
            ProcessInfoResponse::Endianness(e) => InfoResponse::Endianness(e),
            ProcessInfoResponse::PointerSize(p) => InfoResponse::PointerSize(p),
        }
    }
}

impl<T: Target, C: Connection> GdbStubImpl<T, C> {
    pub(crate) fn handle_process_info(
        &mut self,
        res: &mut ResponseWriter<'_, C>,
        target: &mut T,
        command: ProcessInfo,
    ) -> Result<HandlerStatus, Error<T::Error, C::Error>> {
        let ops = match target.support_process_info() {
            Some(ops) => ops,
            None => return Ok(HandlerStatus::Handled),
        };

        crate::__dead_code_marker!("process_info", "impl");

        let mut result = Ok(());
        let mut write_info = |info: &ProcessInfoResponse<'_>| {
            if result.is_ok() {
                if let Err(e) = InfoResponse::from(info).write_response(res) {
                    result = Err(e);
                }
            }
        };

        match command {
            ProcessInfo::qProcessInfo(_cmd) => {
                ops.process_info(&mut write_info)
                    .map_err(Error::TargetError)?;
                result?;
            }
        };

        Ok(HandlerStatus::Handled)
    }

    pub(crate) fn handle_host_info(
        &mut self,
        res: &mut ResponseWriter<'_, C>,
        target: &mut T,
        command: HostInfo,
    ) -> Result<HandlerStatus, Error<T::Error, C::Error>> {
        let ops = match target.support_host_info() {
            Some(ops) => ops,
            None => return Ok(HandlerStatus::Handled),
        };

        crate::__dead_code_marker!("host_info", "impl");

        let mut result = Ok(());
        let mut write_info = |info: &HostInfoResponse<'_>| {
            if result.is_ok() {
                if let Err(e) = InfoResponse::from(info).write_response(res) {
                    result = Err(e);
                }
            }
        };

        match command {
            HostInfo::qHostInfo(_cmd) => {
                ops.host_info(&mut write_info).map_err(Error::TargetError)?;
                result?;
            }
        };

        Ok(HandlerStatus::Handled)
    }
}
