use crate::error::{CaError, CaResult};
use crate::server::record::{FieldDesc, Record, RecordProcessResult};
use crate::types::{DbFieldType, EpicsValue};

/// Analog input record with conversion support.
pub struct AiRecord {
    // Display
    pub val: f64,
    pub egu: String,
    pub hopr: f64,
    pub lopr: f64,
    pub prec: i16,
    // Conversion
    pub rval: i32,
    pub linr: i16,      // 0=NO_CONVERSION, 1=LINEAR
    pub eguf: f64,
    pub egul: f64,
    pub eslo: f64,      // default 1.0
    pub roff: i32,
    pub aslo: f64,      // default 1.0
    pub aoff: f64,
    pub smoo: f64,      // smoothing 0~1
    // Deadband
    pub adel: f64,
    pub mdel: f64,
    // Runtime (alarm/monitor tracking)
    pub lalm: f64,
    pub alst: f64,
    pub mlst: f64,
    pub init: bool,
    // Simulation
    pub simm: i16,
    pub siml: String,
    pub siol: String,
    pub sims: i16,
}

impl Default for AiRecord {
    fn default() -> Self {
        Self {
            val: 0.0,
            egu: String::new(),
            hopr: 0.0,
            lopr: 0.0,
            prec: 0,
            rval: 0,
            linr: 0,
            eguf: 0.0,
            egul: 0.0,
            eslo: 1.0,
            roff: 0,
            aslo: 1.0,
            aoff: 0.0,
            smoo: 0.0,
            adel: 0.0,
            mdel: 0.0,
            lalm: 0.0,
            alst: 0.0,
            mlst: 0.0,
            init: false,
            simm: 0,
            siml: String::new(),
            siol: String::new(),
            sims: 0,
        }
    }
}

impl AiRecord {
    pub fn new(val: f64) -> Self {
        Self {
            val,
            ..Default::default()
        }
    }
}

static FIELDS: &[FieldDesc] = &[
    FieldDesc { name: "VAL", dbf_type: DbFieldType::Double, read_only: false },
    FieldDesc { name: "EGU", dbf_type: DbFieldType::String, read_only: false },
    FieldDesc { name: "HOPR", dbf_type: DbFieldType::Double, read_only: false },
    FieldDesc { name: "LOPR", dbf_type: DbFieldType::Double, read_only: false },
    FieldDesc { name: "PREC", dbf_type: DbFieldType::Short, read_only: false },
    FieldDesc { name: "RVAL", dbf_type: DbFieldType::Long, read_only: false },
    FieldDesc { name: "LINR", dbf_type: DbFieldType::Short, read_only: false },
    FieldDesc { name: "EGUF", dbf_type: DbFieldType::Double, read_only: false },
    FieldDesc { name: "EGUL", dbf_type: DbFieldType::Double, read_only: false },
    FieldDesc { name: "ESLO", dbf_type: DbFieldType::Double, read_only: false },
    FieldDesc { name: "ROFF", dbf_type: DbFieldType::Long, read_only: false },
    FieldDesc { name: "ASLO", dbf_type: DbFieldType::Double, read_only: false },
    FieldDesc { name: "AOFF", dbf_type: DbFieldType::Double, read_only: false },
    FieldDesc { name: "SMOO", dbf_type: DbFieldType::Double, read_only: false },
    FieldDesc { name: "ADEL", dbf_type: DbFieldType::Double, read_only: false },
    FieldDesc { name: "MDEL", dbf_type: DbFieldType::Double, read_only: false },
    FieldDesc { name: "LALM", dbf_type: DbFieldType::Double, read_only: true },
    FieldDesc { name: "ALST", dbf_type: DbFieldType::Double, read_only: true },
    FieldDesc { name: "MLST", dbf_type: DbFieldType::Double, read_only: true },
    FieldDesc { name: "INIT", dbf_type: DbFieldType::Char, read_only: true },
    FieldDesc { name: "SIMM", dbf_type: DbFieldType::Short, read_only: false },
    FieldDesc { name: "SIML", dbf_type: DbFieldType::String, read_only: false },
    FieldDesc { name: "SIOL", dbf_type: DbFieldType::String, read_only: false },
    FieldDesc { name: "SIMS", dbf_type: DbFieldType::Short, read_only: false },
];

impl Record for AiRecord {
    fn record_type(&self) -> &'static str {
        "ai"
    }

    fn process(&mut self) -> CaResult<RecordProcessResult> {
        match self.linr {
            1 => {
                let mut v = (self.rval as i64 + self.roff as i64) as f64;
                v = v * self.aslo + self.aoff;
                v = v * self.eslo + self.egul;
                if self.smoo == 0.0 || !self.init {
                    self.val = v;
                } else {
                    self.val = v * (1.0 - self.smoo) + self.val * self.smoo;
                }
            }
            _ => {}
        }
        self.init = true;
        Ok(RecordProcessResult::Complete)
    }

    fn get_field(&self, name: &str) -> Option<EpicsValue> {
        match name {
            "VAL" => Some(EpicsValue::Double(self.val)),
            "EGU" => Some(EpicsValue::String(self.egu.clone())),
            "HOPR" => Some(EpicsValue::Double(self.hopr)),
            "LOPR" => Some(EpicsValue::Double(self.lopr)),
            "PREC" => Some(EpicsValue::Short(self.prec)),
            "RVAL" => Some(EpicsValue::Long(self.rval)),
            "LINR" => Some(EpicsValue::Short(self.linr)),
            "EGUF" => Some(EpicsValue::Double(self.eguf)),
            "EGUL" => Some(EpicsValue::Double(self.egul)),
            "ESLO" => Some(EpicsValue::Double(self.eslo)),
            "ROFF" => Some(EpicsValue::Long(self.roff)),
            "ASLO" => Some(EpicsValue::Double(self.aslo)),
            "AOFF" => Some(EpicsValue::Double(self.aoff)),
            "SMOO" => Some(EpicsValue::Double(self.smoo)),
            "ADEL" => Some(EpicsValue::Double(self.adel)),
            "MDEL" => Some(EpicsValue::Double(self.mdel)),
            "LALM" => Some(EpicsValue::Double(self.lalm)),
            "ALST" => Some(EpicsValue::Double(self.alst)),
            "MLST" => Some(EpicsValue::Double(self.mlst)),
            "INIT" => Some(EpicsValue::Char(if self.init { 1 } else { 0 })),
            "SIMM" => Some(EpicsValue::Short(self.simm)),
            "SIML" => Some(EpicsValue::String(self.siml.clone())),
            "SIOL" => Some(EpicsValue::String(self.siol.clone())),
            "SIMS" => Some(EpicsValue::Short(self.sims)),
            _ => None,
        }
    }

    fn put_field(&mut self, name: &str, value: EpicsValue) -> CaResult<()> {
        match name {
            "VAL" => match value {
                EpicsValue::Double(v) => { self.val = v; Ok(()) }
                _ => Err(CaError::TypeMismatch(name.into())),
            },
            "EGU" => match value {
                EpicsValue::String(v) => { self.egu = v; Ok(()) }
                _ => Err(CaError::TypeMismatch(name.into())),
            },
            "HOPR" => match value {
                EpicsValue::Double(v) => { self.hopr = v; Ok(()) }
                _ => Err(CaError::TypeMismatch(name.into())),
            },
            "LOPR" => match value {
                EpicsValue::Double(v) => { self.lopr = v; Ok(()) }
                _ => Err(CaError::TypeMismatch(name.into())),
            },
            "PREC" => match value {
                EpicsValue::Short(v) => { self.prec = v; Ok(()) }
                _ => Err(CaError::TypeMismatch(name.into())),
            },
            "RVAL" => match value {
                EpicsValue::Long(v) => { self.rval = v; Ok(()) }
                _ => Err(CaError::TypeMismatch(name.into())),
            },
            "LINR" => match value {
                EpicsValue::Short(v) => { self.linr = v; Ok(()) }
                _ => Err(CaError::TypeMismatch(name.into())),
            },
            "EGUF" => match value {
                EpicsValue::Double(v) => { self.eguf = v; Ok(()) }
                _ => Err(CaError::TypeMismatch(name.into())),
            },
            "EGUL" => match value {
                EpicsValue::Double(v) => { self.egul = v; Ok(()) }
                _ => Err(CaError::TypeMismatch(name.into())),
            },
            "ESLO" => match value {
                EpicsValue::Double(v) => { self.eslo = v; Ok(()) }
                _ => Err(CaError::TypeMismatch(name.into())),
            },
            "ROFF" => match value {
                EpicsValue::Long(v) => { self.roff = v; Ok(()) }
                _ => Err(CaError::TypeMismatch(name.into())),
            },
            "ASLO" => match value {
                EpicsValue::Double(v) => { self.aslo = v; Ok(()) }
                _ => Err(CaError::TypeMismatch(name.into())),
            },
            "AOFF" => match value {
                EpicsValue::Double(v) => { self.aoff = v; Ok(()) }
                _ => Err(CaError::TypeMismatch(name.into())),
            },
            "SMOO" => match value {
                EpicsValue::Double(v) => { self.smoo = v; Ok(()) }
                _ => Err(CaError::TypeMismatch(name.into())),
            },
            "ADEL" => match value {
                EpicsValue::Double(v) => { self.adel = v; Ok(()) }
                _ => Err(CaError::TypeMismatch(name.into())),
            },
            "MDEL" => match value {
                EpicsValue::Double(v) => { self.mdel = v; Ok(()) }
                _ => Err(CaError::TypeMismatch(name.into())),
            },
            // Runtime fields (writable internally)
            "LALM" => match value {
                EpicsValue::Double(v) => { self.lalm = v; Ok(()) }
                _ => Err(CaError::TypeMismatch(name.into())),
            },
            "ALST" => match value {
                EpicsValue::Double(v) => { self.alst = v; Ok(()) }
                _ => Err(CaError::TypeMismatch(name.into())),
            },
            "MLST" => match value {
                EpicsValue::Double(v) => { self.mlst = v; Ok(()) }
                _ => Err(CaError::TypeMismatch(name.into())),
            },
            "SIMM" => match value {
                EpicsValue::Short(v) => { self.simm = v; Ok(()) }
                _ => Err(CaError::TypeMismatch(name.into())),
            },
            "SIML" => match value {
                EpicsValue::String(v) => { self.siml = v; Ok(()) }
                _ => Err(CaError::TypeMismatch(name.into())),
            },
            "SIOL" => match value {
                EpicsValue::String(v) => { self.siol = v; Ok(()) }
                _ => Err(CaError::TypeMismatch(name.into())),
            },
            "SIMS" => match value {
                EpicsValue::Short(v) => { self.sims = v; Ok(()) }
                _ => Err(CaError::TypeMismatch(name.into())),
            },
            _ => Err(CaError::FieldNotFound(name.into())),
        }
    }

    fn field_list(&self) -> &'static [FieldDesc] {
        FIELDS
    }
}
