pub mod cbor;
pub mod json;
use crate::{ast::*, parser::ParserError, token::Numeric};
use std::{fmt, result};
pub type Result = result::Result<(), Error>;
#[derive(Debug)]
pub enum Error {
  
  Syntax(String),
  
  Target(Box<dyn std::error::Error>),
  
  Compilation(CompilationError),
  
  Occurrence(String),
  
  MultiError(Vec<Error>),
}
impl fmt::Display for Error {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    match self {
      Error::Syntax(se) => write!(f, "CDDL syntax error: {}", se),
      Error::Target(te) => write!(f, "{}", te),
      Error::Compilation(ce) => write!(f, "error on compilation: {}", ce),
      Error::Occurrence(oe) => write!(f, "occurrence error: {}", oe),
      Error::MultiError(me) => {
        let mut errors = String::new();
        for e in me.iter() {
          match e {
            
            Error::MultiError(_) => errors.push_str(&format!("{}", e)),
            _ => errors.push_str(&format!("{}\n\n", e)),
          }
        }
        write!(f, "{}", errors)
      }
    }
  }
}
impl std::error::Error for Error {
  fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
    match self {
      Error::Compilation(ce) => Some(ce),
      Error::Target(te) => Some(te.as_ref()),
      _ => None,
    }
  }
}
#[derive(Debug)]
pub enum CompilationError {
  
  CDDL(ParserError),
  
  Target(Box<dyn std::error::Error>),
}
impl fmt::Display for CompilationError {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    match self {
      CompilationError::CDDL(ce) => write!(f, "{}", ce),
      CompilationError::Target(te) => write!(f, "{}", te),
    }
  }
}
impl std::error::Error for CompilationError {
  fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
    match self {
      CompilationError::CDDL(ce) => Some(ce),
      CompilationError::Target(te) => Some(te.as_ref()),
    }
  }
}
pub trait Validator<T> {
  
  fn validate(&self, value: &T) -> Result;
  
  fn validate_rule_for_ident(
    &self,
    ident: &Identifier,
    is_enumeration: bool,
    expected_memberkey: Option<String>,
    actual_memberkey: Option<String>,
    occur: Option<&Occur>,
    value: &T,
  ) -> Result;
  
  fn validate_type_rule(
    &self,
    tr: &TypeRule,
    expected_memberkey: Option<String>,
    actual_memberkey: Option<String>,
    occur: Option<&Occur>,
    value: &T,
  ) -> Result;
  
  fn validate_group_rule(
    &self,
    gr: &GroupRule,
    is_enumeration: bool,
    occur: Option<&Occur>,
    value: &T,
  ) -> Result;
  
  
  fn validate_type(
    &self,
    t: &Type,
    expected_memberkey: Option<String>,
    actual_memberkey: Option<String>,
    occur: Option<&Occur>,
    value: &T,
  ) -> Result;
  
  
  fn validate_type1(
    &self,
    t1: &Type1,
    expected_memberkey: Option<String>,
    actual_memberkey: Option<String>,
    occur: Option<&Occur>,
    value: &T,
  ) -> Result;
  
  
  fn validate_range(&self, lower: &Type2, upper: &Type2, is_inclusive: bool, value: &T) -> Result;
  
  
  fn validate_control_operator(
    &self,
    target: &Type2,
    operator: &'static str,
    controller: &Type2,
    value: &T,
  ) -> Result;
  
  fn validate_type2(
    &self,
    t2: &Type2,
    expected_memberkey: Option<String>,
    actual_memberkey: Option<String>,
    occur: Option<&Occur>,
    value: &T,
  ) -> Result;
  
  fn validate_group(&self, g: &Group, occur: Option<&Occur>, value: &T) -> Result;
  
  fn validate_group_to_choice_enum(&self, g: &Group, occur: Option<&Occur>, value: &T) -> Result;
  
  fn validate_group_choice(&self, gc: &GroupChoice, occur: Option<&Occur>, value: &T) -> Result;
  
  fn validate_group_entry(
    &self,
    ge: &GroupEntry,
    is_enumeration: bool,
    wildcard_entry: Option<&Type>,
    occur: Option<&Occur>,
    value: &T,
  ) -> Result;
  
  fn validate_array_occurrence(&self, occur: &Occur, group: &str, values: &[T]) -> Result;
  
  fn expect_bool(&self, ident: &str, value: &T) -> Result;
  
  fn validate_numeric_data_type(
    &self,
    expected_memberkey: Option<String>,
    actual_memberkey: Option<String>,
    ident: &str,
    value: &T,
  ) -> Result;
}
impl CDDL {
  fn numerical_value_type_from_ident(&self, ident: &Identifier) -> Option<Vec<&Type2>> {
    let mut type_choices = Vec::new();
    for rule in self.rules.iter() {
      match rule {
        Rule::Type(tr) if tr.name == *ident => {
          for tc in tr.value.0.iter() {
            match &tc.type2 {
              Type2::IntValue(_) | Type2::UintValue(_) | Type2::FloatValue(_) => {
                type_choices.push(&tc.type2);
              }
              Type2::Typename((ident, _)) => return self.numerical_value_type_from_ident(ident),
              _ => continue,
            }
          }
        }
        _ => continue,
      }
    }
    if !type_choices.is_empty() {
      return Some(type_choices);
    }
    None
  }
  
  
  fn is_type_string_data_type(&self, t2: &Type2) -> bool {
    match t2 {
      Type2::Typename((Identifier((ident, _)), _)) if *ident == "text" || *ident == "tstr" => true,
      Type2::Typename((ident, _)) => self.rules.iter().any(|r| match r {
        Rule::Type(tr) if tr.name == *ident => tr
          .value
          .0
          .iter()
          .any(|tc| self.is_type_string_data_type(&tc.type2)),
        _ => false,
      }),
      _ => false,
    }
  }
  fn is_type_numeric_data_type(&self, t2: &Type2) -> bool {
    match t2 {
      Type2::Typename((Identifier((ident, _)), _)) if is_numeric_data_type(ident) => true,
      Type2::Typename((ident, _)) => self.rules.iter().any(|r| match r {
        Rule::Type(tr) if tr.name == *ident => tr
          .value
          .0
          .iter()
          .any(|tc| self.is_type_numeric_data_type(&tc.type2)),
        _ => false,
      }),
      _ => false,
    }
  }
  
  fn text_values_from_type(&self, ident: &Type2) -> result::Result<Vec<String>, Error> {
    match ident {
      Type2::TextValue(t) => Ok(vec![t.into()]),
      Type2::Typename((ident, _)) => {
        let mut text_values = Vec::new();
        for r in self.rules.iter() {
          match r {
            Rule::Type(tr) if tr.name == *ident => {
              for tc in tr.value.0.iter() {
                text_values.append(&mut self.text_values_from_type(&tc.type2)?);
              }
            }
            _ => continue,
          }
        }
        Ok(text_values)
      }
      _ => Err(Error::Syntax(
        "Value can only be referenced via another type name identifier".into(),
      )),
    }
  }
  
  
  fn numeric_values_from_type(
    &self,
    target: &Type2,
    ident: &Type2,
  ) -> result::Result<Vec<Numeric>, Error> {
    let target_idents = self.numerical_ident_from_type(target)?;
    match ident {
      Type2::IntValue(i) => {
        if target_idents.into_iter().any(|ti| ti == "int" || ti == "number") {
          return Ok(vec![Numeric::INT(*i)])
        }
        Err(Error::Syntax("Target data type must be an 'int' or 'number' in order to validate against an integer value".into()))
      }
      Type2::UintValue(ui) => {
        if target_idents.into_iter().any(|ti| ti == "uint" || ti == "number") {
          return Ok(vec![Numeric::UINT(*ui)]);
        }
        Err(Error::Syntax("Target data type must be a 'uint' or 'number' in order to validate against an unsigned integer value".into()))
      }
      Type2::FloatValue(f) => {
        if target_idents.into_iter().any(|ti| match ti.as_ref() {
          "float" | "float16" | "float32" | "float64" | "float16-32" | "float32-64" | "number" => true,
          _ => false,
        }) {
          return Ok(vec![Numeric::FLOAT(*f)]);
        }
        Err(Error::Syntax("Target data type must be a 'float', 'float16', 'float32', 'float64', 'float16-32', 'float32-64' or 'number' in order to validate against an unsigned integer value".into()))
      }
      Type2::Typename((ident, _)) => {
        let mut numeric_values = Vec::new();
        for r in self.rules.iter() {
          match r {
            Rule::Type(tr) if tr.name == *ident => {
              for tc in tr.value.0.iter() {
                numeric_values.append(&mut self.numeric_values_from_type(target, &tc.type2)?);
              }
            }
            _ => continue,
          }
        }
        Ok(numeric_values)
      }
      _ => Err(Error::Syntax(
        "Value can only be referenced via another type name identifier whose target type is the type of the value".into(),
      )),
    }
  }
  fn numerical_ident_from_type(&self, t2: &Type2) -> result::Result<Vec<String>, Error> {
    let mut numeric_type_idents = Vec::new();
    match t2 {
      Type2::Typename((Identifier((ident, _)), _)) if is_numeric_data_type(ident) => {
        numeric_type_idents.push(ident.clone());
        Ok(numeric_type_idents)
      }
      Type2::Typename((ident, _)) => {
        for r in self.rules.iter() {
          match r {
            Rule::Type(tr) if tr.name == *ident => {
              for tc in tr.value.0.iter() {
                numeric_type_idents.append(&mut self.numerical_ident_from_type(&tc.type2)?);
              }
            }
            _ => continue,
          }
        }
        Ok(numeric_type_idents)
      }
      _ => Err(Error::Syntax(
        "Numeric identifier can only be referenced via another type name identifier".into(),
      )),
    }
  }
}
fn is_numeric_data_type(t: &str) -> bool {
  match t {
    "uint" | "nint" | "int" | "number" | "float" | "float16" | "float32" | "float64"
    | "float16-32" | "float32-64" => true,
    _ => false,
  }
}