Documentation
use crate::{BsdlError, Entity};

/// Cell Functions for boundary register cells
#[derive(Debug, Clone, PartialEq)]
pub enum CellFunction {
    /// not associated with a system pin
    Internal,
    /// associated with a system pin, similar to Input
    ObserveOnly,
    /// similar to ObserveOnly, used for clock pins
    Clock,
    /// similar to ObserveOnly, supports INTEST
    Input,
    /// 2 state (1,0) output cell
    Output2,
    /// 3 state (1,0,z) output cell
    Output3,
    /// direction control, associated with an Output3 or Bidir which comes just after
    Control,
    /// like Control but with preset/clear in TLR
    ControlR,
    /// Output3 plus an input which will be after it
    Bidir,
    /// not IEEE: something we don't recognize
    Unknown(String),
    /// not IEEE: cell function was missing from file
    Missing,
}

impl CellFunction {
    /// if s is None, returns LogicVal::None otherwise
    fn from_str(s: Option<&str>) -> CellFunction {
        if let Some(s) = s {
            let s = s.trim().to_lowercase();
            let s = s.as_str();
            match s {
                "internal" => CellFunction::Internal,
                "observeonly" => CellFunction::ObserveOnly,
                "clock" => CellFunction::Clock,
                "input" => CellFunction::Input,
                "output2" => CellFunction::Output2,
                "output3" => CellFunction::Output3,
                "control" => CellFunction::Control,
                "controlr" => CellFunction::ControlR,
                "bidir" => CellFunction::Bidir,
                _ => CellFunction::Unknown(s.to_string()),
            }
        } else {
            CellFunction::Missing
        }
    }
    pub fn is_input(&self) -> bool {
        match self {
            CellFunction::Bidir
            | CellFunction::Clock
            | CellFunction::Input
            | CellFunction::ObserveOnly => true,
            _ => false,
        }
    }
    pub fn is_output(&self) -> bool {
        match self {
            CellFunction::Bidir | CellFunction::Output2 | CellFunction::Output3 => true,
            _ => false,
        }
    }
}

impl std::fmt::Display for CellFunction {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f, "{self:?}")
    }
}
#[derive(Debug, Clone, PartialEq)]
#[allow(non_camel_case_types)]
pub enum CellType {
    /// 1149.1 boundary cell BC_n
    BC(u8),
    /// 1149.6 AC boundary cell AC_n
    AC(u8),
    /// non-IEEE
    Other(String),
}

impl CellType {
    fn from_str(s: Option<&str>) -> Result<CellType, BsdlError> {
        if let Some(s) = s {
            let s = s.trim();
            if s.starts_with("BC_") {
                let n = &s[3..];
                let n = u8::from_str_radix(n, 10)?;
                Ok(CellType::BC(n))
            } else if s.starts_with("AC_") {
                let n = &s[3..];
                let n = u8::from_str_radix(n, 10)?;
                Ok(CellType::AC(n))
            } else {
                Ok(CellType::Other(s.to_string()))
            }
        } else {
            Err(BsdlError::ParseError(format!("")))
        }
    }
}

impl std::fmt::Display for CellType {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f, "{self:?}")
    }
}
/// 4 state logic value with the possibility of being unspecified (None)
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum LogicVal {
    /// logic 0
    Zero = 0,
    /// logic 1
    One = 1,
    /// undefined / don't care
    X = 2,
    /// high impedance
    Z = 3,
    /// unspecifed in BSDL
    None = 4,
}

impl LogicVal {
    /// if s is None, returns LogicVal::None otherwise
    /// returns proper value
    fn from_str(s: Option<&str>) -> Result<LogicVal, BsdlError> {
        if let Some(s) = s {
            let s = s.trim();
            match s {
                "0" => Ok(LogicVal::Zero),
                "1" => Ok(LogicVal::One),
                "x" | "X" => Ok(LogicVal::X),
                "z" | "Z" => Ok(LogicVal::Z),
                _ => Err(BsdlError::ParseError(format!(
                    "LogicVal can only be created from '0', '1', 'x', 'X', 'z', 'Z', not {s}"
                ))),
            }
        } else {
            Ok(LogicVal::None)
        }
    }
}

impl std::fmt::Display for LogicVal {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        let rv = match self {
            LogicVal::Zero => '0',
            LogicVal::One => '1',
            LogicVal::X => 'x',
            LogicVal::Z => 'z',
            LogicVal::None => '-',
        };
        write!(f, "{rv}")
    }
}

/// Scan Cell disable result
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum DisableResult {
    /// internal pulldown
    Pull0 = 0,
    /// internal pullup
    Pull1 = 1,
    /// external pulldown
    Weak0 = 2,
    /// external pullup
    Weak1 = 3,
    /// pulls in last strongly driven state
    Keeper = 4,
    /// high impedance
    Z = 5,
    /// no DisableResult value in scan cell entry
    None,
}

impl std::fmt::Display for DisableResult {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f, "{self:?}")
    }
}

impl DisableResult {
    /// if s is None, returns LogicVal::None otherwise
    /// returns proper value
    fn from_str(s: Option<&str>) -> Result<DisableResult, BsdlError> {
        if let Some(s) = s {
            let s = s.trim();
            let s = s.to_lowercase();
            match s.as_str() {
                "pull0" => Ok(DisableResult::Pull0),
                "pull1" => Ok(DisableResult::Pull1),
                "weak0" => Ok(DisableResult::Weak0),
                "weak1" => Ok(DisableResult::Weak1),
                "z" => Ok(DisableResult::Z),
                _ => Err(BsdlError::ParseError(format!(
                    "DisableResult cannot be created from {s}"
                ))),
            }
        } else {
            Ok(DisableResult::None)
        }
    }
}

#[derive(Debug, Clone)]
pub struct ScanCell {
    pub ctype: CellType,
    pub port: Option<usize>,
    pub function: CellFunction,
    /// update value to load if otherwise doesn't matter
    pub safe: LogicVal,
    /// cell index for control cell associated with this output
    pub ccell: Option<usize>,
    /// update value to load to disable cell
    pub disval: LogicVal,
    /// disable result, ususally LogicVal::Z
    pub disresult: DisableResult,
}

impl ScanCell {
    pub(crate) fn default() -> ScanCell {
        ScanCell {
            ctype: CellType::BC(0),
            port: None,
            function: CellFunction::Internal,
            safe: LogicVal::None,
            ccell: None,
            disval: LogicVal::None,
            disresult: DisableResult::None,
        }
    }
    /// accepts a string like "BC_2, CCLK_F10, output3, X, 4, 1, Z"
    pub(crate) fn from_string(
        s: &str,
        entity: &mut Entity,
        cellnum: usize,
    ) -> Result<ScanCell, BsdlError> {
        let mut s = s.trim().split(',');
        // BC_2 for the example
        let ctype = CellType::from_str(s.next())?;
        // CCLK_F10 in example, * if no pin associated
        let mut port = s.next().ok_or(BsdlError::AttributeError)?.trim();
        if port == "*" {
            port = "";
        }
        // output3 in example
        let function = s.next();
        // optional, X in example
        let safe = s.next();
        // optional, 4 in example
        let ccell = s.next();
        let ccell = match ccell {
            Some(x) => Some(usize::from_str_radix(x.trim(), 10)?),
            None => None,
        };
        // optional, 1 in example
        let disval = s.next();
        // optional, Z in example
        let disresult = s.next();

        let port_index = entity.port_number_from_portname(port);

        let rv = ScanCell {
            ctype,
            port: port_index,
            function: CellFunction::from_str(function),
            safe: LogicVal::from_str(safe)?,
            ccell,
            disval: LogicVal::from_str(disval)?,
            disresult: DisableResult::from_str(disresult)?,
        };
        if let Some(port_index) = port_index {
            if rv.function.is_input() {
                let port = &mut entity.ports.v[port_index];
                port.cell_i = Some(cellnum);
            }
            if rv.function.is_output() {
                let port = &mut entity.ports.v[port_index];
                port.cell_o = Some(cellnum);
            }
        }
        Ok(rv)
    }
}

impl std::fmt::Display for ScanCell {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(
            f,
            "ctype: {}, port = {:?}, function = {}, safe = {}, ccell = {:?}, dv = {}, dr = {}",
            self.ctype,
            self.port,
            self.function,
            self.safe,
            self.ccell,
            self.disval,
            self.disresult
        )
    }
}