Documentation
use eva_common::prelude::*;
use std::fmt;
use std::time::Duration;

#[macro_use]
extern crate lazy_static;

pub const LOCAL_NODE_ALIAS: &str = ".local";

pub const PRODUCT_NAME: &str = "EVA ICS node server";
pub const PRODUCT_CODE: &str = "eva4node";
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
#[allow(clippy::inconsistent_digit_grouping)]
pub const BUILD: u64 = 2022_01_19_01;
pub const AUTHOR: &str = "(c) 2022 Bohemia Automation / Altertech";

pub const SLEEP_STEP: Duration = Duration::from_millis(100);

pub const DEFAULT_TIMEOUT: Duration = Duration::from_secs(5);

#[inline]
pub fn get_eva_dir() -> String {
    std::env::var("EVA_DIR").unwrap_or_else(|_| "/opt/eva4".to_owned())
}

#[inline]
pub fn get_version() -> &'static str {
    VERSION
}

#[inline]
pub fn get_version_owned() -> String {
    VERSION.to_owned()
}

#[inline]
pub fn get_default_sleep_step() -> Duration {
    SLEEP_STEP
}

#[inline]
pub fn get_build() -> u64 {
    BUILD
}

pub mod actions;
pub mod bus;
pub mod core;
pub mod eapi;
pub mod events;
pub mod items;
pub mod launcher;
pub mod logs;
pub mod node;
pub mod registry;
pub mod services;
pub mod time;
pub mod tools;
pub mod workers;

trait IeidX {
    //fn generate() -> Self;
}

impl IeidX for IEID {
    //#[allow(clippy::must_use_candidate)]
    //fn generate() -> Self {
    //Self::new(
    //heart::BOOT_ID.load(std::sync::atomic::Ordering::SeqCst),
    //crate::time::monotonic_ns(),
    //)
    //}
}

#[derive(Debug, Eq, PartialEq, Clone)]
pub struct Error {
    kind: EvaErrorKind,
    message: Option<String>,
}

impl std::fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let mut err = self.kind.to_string();
        if let Some(msg) = self.message.as_ref() {
            err += &format!(": {}", msg);
        }
        write!(f, "{}", err)
    }
}

impl std::error::Error for Error {}

impl Error {
    pub fn new0(kind: EvaErrorKind) -> Self {
        Self {
            kind,
            message: None,
        }
    }
    pub fn fatal() -> Self {
        Self {
            kind: EvaErrorKind::CoreError,
            message: None,
        }
    }
    pub fn core<T: fmt::Display>(message: T) -> Self {
        EvaError::core(message).into()
    }
    pub fn failed<T: fmt::Display>(message: T) -> Self {
        EvaError::failed(message).into()
    }
    pub fn duplicate<T: fmt::Display>(message: T) -> Self {
        EvaError::duplicate(message).into()
    }
    pub fn access<T: fmt::Display>(message: T) -> Self {
        EvaError::access(message).into()
    }
    pub fn not_found<T: fmt::Display>(message: T) -> Self {
        EvaError::not_found(message).into()
    }
    pub fn io<T: fmt::Display>(message: T) -> Self {
        EvaError::io(message).into()
    }
    pub fn busy<T: fmt::Display>(message: T) -> Self {
        EvaError::busy(message).into()
    }
    pub fn invalid_data<T: fmt::Display>(message: T) -> Self {
        EvaError::invalid_data(message).into()
    }
    pub fn registry<T: fmt::Display>(message: T) -> Self {
        EvaError::registry(message).into()
    }
    pub fn timeout() -> Self {
        EvaError::timeout().into()
    }
    pub fn method_not_found<T: fmt::Display>(message: T) -> Self {
        Self {
            kind: EvaErrorKind::MethodNotFound,
            message: Some(message.to_string()),
        }
    }
    pub fn invalid_params<T: fmt::Display>(message: T) -> Self {
        Self {
            kind: EvaErrorKind::InvalidParameter,
            message: Some(message.to_string()),
        }
    }
    pub fn not_implemented<T: fmt::Display>(message: T) -> Self {
        Self {
            kind: EvaErrorKind::MethodNotImplemented,
            message: Some(message.to_string()),
        }
    }
    pub fn kind(&self) -> EvaErrorKind {
        self.kind
    }
    pub fn message(&self) -> Option<&str> {
        self.message.as_deref()
    }
}

pub type EResult<T> = std::result::Result<T, Error>;

impl From<elbus::Error> for Error {
    fn from(error: elbus::Error) -> Self {
        EvaError::io(error).into()
    }
}

impl From<tokio::sync::oneshot::error::RecvError> for Error {
    fn from(error: tokio::sync::oneshot::error::RecvError) -> Self {
        EvaError::io(error).into()
    }
}

impl From<regex::Error> for Error {
    fn from(error: regex::Error) -> Self {
        EvaError::invalid_data(error).into()
    }
}

impl From<serde_json::Error> for Error {
    fn from(error: serde_json::Error) -> Self {
        EvaError::invalid_data(error).into()
    }
}

impl From<rmp_serde::encode::Error> for Error {
    fn from(error: rmp_serde::encode::Error) -> Self {
        EvaError::invalid_data(error).into()
    }
}

impl From<rmp_serde::decode::Error> for Error {
    fn from(error: rmp_serde::decode::Error) -> Self {
        EvaError::invalid_data(error).into()
    }
}

//impl From<ipnetwork::IpNetworkError> for Error {
//fn from(error: ipnetwork::IpNetworkError) -> Self {
//EvaError::invalid_data(error).into()
//}
//}

impl From<eva_common::EvaError> for Error {
    fn from(error: eva_common::EvaError) -> Self {
        Self {
            kind: error.kind(),
            message: error.message().map(ToOwned::to_owned),
        }
    }
}

impl From<eva_common::value::SerializerError> for Error {
    fn from(err: eva_common::value::SerializerError) -> Error {
        Error::invalid_data(err)
    }
}

impl From<eva_common::value::DeserializerError> for Error {
    fn from(e: eva_common::value::DeserializerError) -> Error {
        Error::invalid_data(e)
    }
}

impl From<std::num::ParseIntError> for Error {
    fn from(err: std::num::ParseIntError) -> Error {
        Error::invalid_data(err)
    }
}

impl From<std::num::ParseFloatError> for Error {
    fn from(err: std::num::ParseFloatError) -> Error {
        Error::invalid_data(err)
    }
}

impl From<hex::FromHexError> for Error {
    fn from(err: hex::FromHexError) -> Error {
        Error::invalid_data(err)
    }
}

impl From<std::array::TryFromSliceError> for Error {
    fn from(err: std::array::TryFromSliceError) -> Error {
        Error::invalid_data(err)
    }
}

impl From<Error> for EvaError {
    fn from(err: Error) -> EvaError {
        EvaError::newc(err.kind, err.message)
    }
}

impl From<std::io::Error> for Error {
    fn from(err: std::io::Error) -> Error {
        Error::io(err)
    }
}

impl From<tokio::time::error::Elapsed> for Error {
    fn from(_e: tokio::time::error::Elapsed) -> Error {
        Error::timeout()
    }
}

impl From<yedb::Error> for Error {
    fn from(e: yedb::Error) -> Error {
        Error::registry(e)
    }
}

impl From<tokio::sync::TryLockError> for Error {
    fn from(e: tokio::sync::TryLockError) -> Error {
        Error::core(e)
    }
}

impl From<Error> for elbus::rpc::RpcError {
    fn from(err: Error) -> Self {
        elbus::rpc::RpcError::new(
            err.kind() as i16,
            elbus::rpc::rpc_err_str(err.message().unwrap_or_default()),
        )
    }
}

impl From<elbus::rpc::RpcError> for Error {
    fn from(err: elbus::rpc::RpcError) -> Self {
        Error {
            kind: err.code().into(),
            message: err
                .data()
                .map(|v| std::str::from_utf8(v).unwrap_or_default().to_owned()),
        }
    }
}

//impl From<libloading::Error> for Error {
//fn from(err: libloading::Error) -> Error {
//Error::failed(err)
//}
//}