#[macro_use]
mod register_generation;

use super::Register;
use bitfield::bitfield;
use jep106::JEP106Code;

use crate::DebugProbeError;
use std::fmt::Display;
use thiserror::Error;

#[derive(Error, Debug)]
pub enum DebugPortError {
    #[error("Register {register} not supported by debug port version {version}")]
    UnsupportedRegister {
        register: &'static str,
        version: DebugPortVersion,
    },
    #[error("A Debug Probe Error occured")]
    DebugProbe(#[from] DebugProbeError),
}

impl From<DebugPortError> for DebugProbeError {
    fn from(error: DebugPortError) -> Self {
        DebugProbeError::ArchitectureSpecific(Box::new(error))
    }
}

pub trait DPAccess {
    fn read_dp_register<R: DPRegister>(&mut self) -> Result<R, DebugPortError>;

    fn write_dp_register<R: DPRegister>(&mut self, register: R) -> Result<(), DebugPortError>;
}

#[derive(Debug, PartialEq, Copy, Clone)]
pub enum DPBankSel {
    DontCare,
    Bank(u8),
}

pub trait DPRegister: Register {
    const DP_BANK: DPBankSel;
    const VERSION: DebugPortVersion;
}

bitfield! {
    #[derive(Clone)]
    pub struct Abort(u32);
    impl Debug;
    pub _, set_orunerrclr: 5;
    pub _, set_wderrclr: 4;
    pub _, set_stkerrclr: 3;
    pub _, set_stkcmpclr: 2;
    pub _, set_dapabort: 1;
}

impl Default for Abort {
    fn default() -> Self {
        Abort(0)
    }
}

impl From<u32> for Abort {
    fn from(raw: u32) -> Self {
        Abort(raw)
    }
}

impl From<Abort> for u32 {
    fn from(raw: Abort) -> Self {
        raw.0
    }
}

impl DPRegister for Abort {
    const DP_BANK: DPBankSel = DPBankSel::DontCare;
    const VERSION: DebugPortVersion = DebugPortVersion::DPv1;
}

impl Register for Abort {
    const ADDRESS: u8 = 0x0;
    const NAME: &'static str = "ABORT";
}

bitfield! {
    #[derive(Clone)]
    pub struct Ctrl(u32);
    impl Debug;
    pub csyspwrupack, _: 31;
    pub csyspwrupreq, set_csyspwrupreq: 30;
    pub cdbgpwrupack, _: 29;
    pub cdbgpwrupreq, set_cdbgpwrupreq: 28;
    pub cdbgrstack, _: 27;
    pub c_dbg_rst_req, set_c_dbg_rst_req: 26;
    pub u16, trn_cnt, set_trn_cnt: 23, 12;
    pub u8, mask_lane, set_mask_lane: 11, 8;
    pub w_data_err, _ : 7;
    pub read_ok, _ : 6;
    pub sticky_err, _: 5;
    pub stick_cmp, _: 4;
    pub u8, trn_mode, _: 3, 2;
    pub sticky_orun, _: 1;
    pub orun_detect, set_orun_detect: 0;
}

impl Default for Ctrl {
    fn default() -> Self {
        Ctrl(0)
    }
}

impl From<u32> for Ctrl {
    fn from(raw: u32) -> Self {
        Ctrl(raw)
    }
}

impl From<Ctrl> for u32 {
    fn from(raw: Ctrl) -> Self {
        raw.0
    }
}

impl DPRegister for Ctrl {
    const DP_BANK: DPBankSel = DPBankSel::Bank(0);
    const VERSION: DebugPortVersion = DebugPortVersion::DPv1;
}

impl Register for Ctrl {
    const ADDRESS: u8 = 0x4;
    const NAME: &'static str = "CTRL/STAT";
}

bitfield! {
    #[derive(Clone)]
    pub struct Select(u32);
    impl Debug;
    pub u8, ap_sel, set_ap_sel: 31, 24;
    pub u8, ap_bank_sel, set_ap_bank_sel: 7, 4;
    pub u8, dp_bank_sel, set_dp_bank_sel: 3, 0;
}

impl From<u32> for Select {
    fn from(raw: u32) -> Self {
        Select(raw)
    }
}

impl From<Select> for u32 {
    fn from(raw: Select) -> Self {
        raw.0
    }
}

impl DPRegister for Select {
    const DP_BANK: DPBankSel = DPBankSel::DontCare;
    const VERSION: DebugPortVersion = DebugPortVersion::DPv1;
}

impl Register for Select {
    const ADDRESS: u8 = 0x8;
    const NAME: &'static str = "SELECT";
}

bitfield! {
    #[derive(Clone)]
    pub struct DPIDR(u32);
    impl Debug;
    pub u8, revision, _: 31, 28;
    pub u8, part_no, _: 27, 20;
    pub min, _: 16;
    pub u8, version, _: 15, 12;
    pub designer, _: 11, 1;
    pub u8, jep_cc, _: 11, 8;
    pub u8, jep_id, _: 7, 1;
}

impl From<u32> for DPIDR {
    fn from(raw: u32) -> Self {
        Self(raw)
    }
}

impl From<DPIDR> for u32 {
    fn from(raw: DPIDR) -> Self {
        raw.0
    }
}

impl DPRegister for DPIDR {
    const DP_BANK: DPBankSel = DPBankSel::DontCare;
    const VERSION: DebugPortVersion = DebugPortVersion::DPv1;
}

impl Register for DPIDR {
    const ADDRESS: u8 = 0x0;
    const NAME: &'static str = "DPIDR";
}

bitfield! {
    #[derive(Clone)]
    pub struct TARGETID(u32);
    impl Debug;
    pub u8, trevision, _: 31, 28;
    pub u16, tpartno, _: 27, 12;
    pub u16, tdesigner, _: 11, 1;
}

impl From<u32> for TARGETID {
    fn from(raw: u32) -> Self {
        Self(raw)
    }
}

impl From<TARGETID> for u32 {
    fn from(raw: TARGETID) -> Self {
        raw.0
    }
}

impl DPRegister for TARGETID {
    const DP_BANK: DPBankSel = DPBankSel::Bank(2);
    const VERSION: DebugPortVersion = DebugPortVersion::DPv2;
}

impl Register for TARGETID {
    const ADDRESS: u8 = 0x4;
    const NAME: &'static str = "TARGETID";
}

#[derive(Debug)]
pub struct DebugPortId {
    pub revision: u8,
    pub part_no: u8,
    pub version: DebugPortVersion,
    pub min_dp_support: MinDpSupport,
    pub designer: JEP106Code,
}

impl From<DPIDR> for DebugPortId {
    fn from(dpidr: DPIDR) -> DebugPortId {
        DebugPortId {
            revision: dpidr.revision(),
            part_no: dpidr.part_no(),
            version: dpidr.version().into(),
            min_dp_support: dpidr.min().into(),
            designer: JEP106Code::new(dpidr.jep_cc(), dpidr.jep_id()),
        }
    }
}

#[derive(Debug, Clone)]
pub struct RdBuff(pub u32);

impl Register for RdBuff {
    const ADDRESS: u8 = 0xc;
    const NAME: &'static str = "RDBUFF";
}

impl From<u32> for RdBuff {
    fn from(val: u32) -> Self {
        RdBuff(val)
    }
}

impl From<RdBuff> for u32 {
    fn from(register: RdBuff) -> Self {
        let RdBuff(val) = register;
        val
    }
}

impl DPRegister for RdBuff {
    const DP_BANK: DPBankSel = DPBankSel::DontCare;
    const VERSION: DebugPortVersion = DebugPortVersion::DPv1;
}

#[derive(Debug, PartialEq)]
pub enum MinDpSupport {
    NotImplemented,
    Implemented,
}

impl From<bool> for MinDpSupport {
    fn from(bit_set: bool) -> Self {
        if bit_set {
            MinDpSupport::Implemented
        } else {
            MinDpSupport::NotImplemented
        }
    }
}

#[derive(Debug, PartialEq, Copy, Clone)]
pub enum DebugPortVersion {
    DPv0,
    DPv1,
    DPv2,
    Unsupported(u8),
}

impl From<DebugPortVersion> for u8 {
    fn from(version: DebugPortVersion) -> Self {
        use DebugPortVersion::*;

        match version {
            DPv0 => 0,
            DPv1 => 1,
            DPv2 => 2,
            Unsupported(val) => val,
        }
    }
}

impl PartialOrd for DebugPortVersion {
    fn partial_cmp(&self, other: &DebugPortVersion) -> Option<std::cmp::Ordering> {
        let self_value = u8::from(*self);
        let other_value = u8::from(*other);

        self_value.partial_cmp(&other_value)
    }
}

impl Display for DebugPortVersion {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        use DebugPortVersion::*;

        match self {
            DPv0 => write!(f, "DPv0"),
            DPv1 => write!(f, "DPv1"),
            DPv2 => write!(f, "DPv2"),
            Unsupported(version) => write!(f, "<unsupported Debugport Version {}>", version),
        }
    }
}

impl From<u8> for DebugPortVersion {
    fn from(value: u8) -> Self {
        match value {
            0 => DebugPortVersion::DPv0,
            1 => DebugPortVersion::DPv1,
            2 => DebugPortVersion::DPv2,
            value => DebugPortVersion::Unsupported(value),
        }
    }
}
