Documentation
use crate::events::{RawStateEvent, ReplicationInventoryItem, ReplicationStateEvent};
use crate::time::monotonic_ns;
use crate::time::Time;
use crate::{EResult, Error};
use eva_common::acl::{OIDMask, OIDMaskList};
use eva_common::prelude::*;
use eva_common::ITEM_STATUS_ERROR;
use log::warn;
use serde::{Deserialize, Serialize};
use std::collections::{BTreeMap, HashMap};
use std::hash::{Hash, Hasher};
use std::str::Split;
use std::sync::atomic;
use std::sync::Arc;
use std::sync::Mutex;
use std::time::Duration;

#[cfg(test)]
mod tests {
    #[test]
    fn test_ser() {
        //let loc = super::Location::Geo(1.0, 2.0, 3.0);
        let mut logic = super::Logic::default();
        let mut range = super::Range::default();
        range.max = Some(33.22);
        logic.range = Some(range);
        let mut opts = super::ServiceOptions {
            svc: "test".to_owned(),
            timeout: 5.0,
            config: None,
        };
        opts.timeout = 33.3;
        let item = std::sync::Arc::new(super::ItemData {
            oid: "unit:tests/t1".parse().unwrap(),
            state: None,
            meta: None,
            //location: Some(loc),
            source: None,
            enabled: std::sync::atomic::AtomicBool::new(true),
            logic: Some(logic),
            action: None,
        });
        let payload = serde_json::to_string(&item).unwrap();
        let v: serde_json::Value = serde_json::from_str(&payload).unwrap();
        let _item: super::ItemData = serde_json::from_value(v).unwrap();
        let payload = rmp_serde::to_vec_named(&item).unwrap();
        let _item: super::ItemData = rmp_serde::from_read_ref(&payload).unwrap();
    }
    #[test]
    fn test_logic() {
        let oid: OID = "sensor:tests/s1".parse().unwrap();
        use eva_common::prelude::*;
        let mut state = super::ItemState::new0(IEID::new(1, 1), EvaItemKind::Sensor);
        state.value = Value::from(33.99);
        assert_eq!(state.apply_logic(None, &oid, 1), false);
        assert_eq!(state.status, 1);
        let mut logic = super::Logic { range: None };
        assert_eq!(state.apply_logic(Some(&logic), &oid, 1), false);
        assert_eq!(state.status, 1);
        logic.range = Some(super::Range {
            min: Some(0.0),
            max: None,
            min_eq: false,
            max_eq: false,
        });
        // test invalid value type
        state.value = Value::Unit;
        assert_eq!(state.apply_logic(Some(&logic), &oid, 1), true);
        assert_eq!(state.status, -1);
        // test min
        state.status = 1;
        state.value = Value::from(1);
        assert_eq!(state.apply_logic(Some(&logic), &oid, 1), false);
        assert_eq!(state.status, 1);
        // test value below min
        state.value = Value::from(0);
        assert_eq!(state.apply_logic(Some(&logic), &oid, 1), true);
        assert_eq!(state.status, -1);
        // change the range to min eq
        state.status = 1;
        logic.range = Some(super::Range {
            min: Some(0.0),
            max: None,
            min_eq: true,
            max_eq: false,
        });
        // test value min eq
        state.value = Value::from(0);
        assert_eq!(state.apply_logic(Some(&logic), &oid, 1), false);
        assert_eq!(state.status, 1);
        state.value = Value::from(-1);
        assert_eq!(state.apply_logic(Some(&logic), &oid, 1), true);
        assert_eq!(state.status, -1);
        state.status = 1;
        // test max
        logic.range = Some(super::Range {
            min: Some(0.0),
            max: Some(10.0),
            min_eq: false,
            max_eq: false,
        });
        // test value above max
        state.value = Value::from(1);
        assert_eq!(state.apply_logic(Some(&logic), &oid, 1), false);
        assert_eq!(state.status, 1);
        state.value = Value::from(10.0);
        assert_eq!(state.apply_logic(Some(&logic), &oid, 1), true);
        assert_eq!(state.status, -1);
        // change the range to max eq
        state.status = 1;
        logic.range = Some(super::Range {
            min: Some(0.0),
            max: Some(10.0),
            min_eq: true,
            max_eq: true,
        });
        // test value max eq
        state.value = Value::from(10);
        assert_eq!(state.apply_logic(Some(&logic), &oid, 1), false);
        assert_eq!(state.status, 1);
        // test value above max
        state.value = Value::from(11);
        assert_eq!(state.apply_logic(Some(&logic), &oid, 1), true);
        assert_eq!(state.status, -1);
    }
}

#[derive(Serialize)]
pub struct InventoryStats {
    sources: HashMap<String, usize>,
    items: usize,
}

#[derive(Debug)]
pub struct ItemSource {
    node: String,
    data: SourceData,
}

trait IeidX {
    fn generate(boot_id: u64) -> Self;
}

impl IeidX for IEID {
    #[inline]
    fn generate(boot_id: u64) -> Self {
        Self::new(boot_id, monotonic_ns())
    }
}

#[inline]
fn create_source(source_id: &str, svc: &str) -> Source {
    Arc::new(ItemSource::new(source_id, svc))
}

#[inline]
fn validate_logic(value: Option<&Value>, logic: Option<&Logic>) -> bool {
    if let Some(r) = logic.and_then(|l| l.range.as_ref()) {
        value.map_or(false, |v| {
            if let Ok(val) = TryInto::<f64>::try_into(v) {
                let mut correct = true;
                if let Some(min) = r.min {
                    if (r.min_eq && val < min) || (!r.min_eq && val <= min) {
                        correct = false;
                    }
                }
                if correct {
                    if let Some(max) = r.max {
                        if (r.max_eq && val > max) || (!r.max_eq && val >= max) {
                            correct = false;
                        }
                    }
                }
                correct
            } else {
                false
            }
        })
    } else {
        true
    }
}

impl ItemSource {
    pub fn new(node: &str, svc: &str) -> Self {
        Self {
            node: node.to_owned(),
            data: SourceData::new(svc),
        }
    }
    #[inline]
    pub fn mark_online(&self, online: bool) {
        self.data.online.store(online, atomic::Ordering::SeqCst);
    }
    #[inline]
    pub fn mark_destroyed(&self) {
        self.data.destroyed.store(true, atomic::Ordering::SeqCst);
    }
    #[inline]
    pub fn node(&self) -> &str {
        &self.node
    }
    #[inline]
    pub fn svc(&self) -> &str {
        &self.data.svc
    }
    #[inline]
    pub fn is_destroyed(&self) -> bool {
        self.data.destroyed.load(atomic::Ordering::SeqCst)
    }
}

#[derive(Debug, Serialize, Deserialize, Clone)]
#[serde(deny_unknown_fields)]
pub struct ItemState {
    status: ItemStatus,
    value: Value,
    #[serde(skip_deserializing)]
    act: Option<usize>, // None for all except units
    ieid: IEID,
    t: f64,
}

macro_rules! mark_out_of_range {
    ($self: expr, $oid: expr, $value: expr) => {
        warn!("{} value is invalid/out of range: {:?}", $oid, $value);
        $self.status = ITEM_STATUS_ERROR;
    };
}

impl ItemState {
    #[inline]
    pub fn new0(ieid: IEID, tp: EvaItemKind) -> Self {
        let (status, act) = if tp == EvaItemKind::Unit {
            (0, Some(0))
        } else {
            (1, None)
        };
        Self {
            status,
            value: Value::Unit,
            act,
            ieid,
            t: Time::now().timestamp(),
        }
    }
    #[inline]
    pub fn new(status: ItemStatus, value: Value, act: Option<usize>, ieid: IEID, t: f64) -> Self {
        Self {
            status,
            value,
            act,
            ieid,
            t,
        }
    }
    // used by lvar functions only
    pub fn force_set_state(&mut self, status: ItemStatus, mut value: Option<Value>, ieid: IEID) {
        self.status = status;
        if let Some(value) = value.take() {
            self.value = value;
        }
        self.ieid = ieid;
        self.t = Time::now().timestamp();
    }
    /// # Panics
    ///
    /// Will panic if attempted to reduce number of actions for non-busy unit. The core MUST
    /// decrease act counter only ONCE per action
    // used by unit actions
    pub fn act_decr(&mut self, ieid: IEID) {
        let a = self.act.unwrap_or_default();
        assert!(
            !(a == 0),
            "core fatal error, attempt to decr zero act for the unit"
        );
        self.act = Some(a - 1);
        self.ieid = ieid;
        self.t = Time::now().timestamp();
    }
    pub fn act_incr(&mut self, ieid: IEID) {
        self.act = Some(self.act.map_or(1, |a| a + 1));
        self.ieid = ieid;
        self.t = Time::now().timestamp();
    }
    #[inline]
    pub fn set_from_rse(&mut self, mut rpl: ReplicationStateEvent) {
        let status = rpl.status();
        let value = rpl.take_value().unwrap_or_default();
        self.status = status;
        if let Some(act) = rpl.act() {
            self.act = Some(act);
        }
        self.value = value;
        self.ieid = rpl.ieid().clone();
        self.t = rpl.time();
    }
    /// # Panics
    ///
    /// Will not panic
    #[inline]
    pub fn set_from_raw(
        &mut self,
        mut raw: RawStateEvent,
        logic: Option<&Logic>,
        oid: &OID,
        boot_id: u64,
    ) -> bool {
        let mut modified = false;
        let status = raw.status();
        let mut value = raw.take_value();
        if status == ITEM_STATUS_ERROR || validate_logic(value.as_ref(), logic) {
            if self.status != status {
                self.status = status;
                modified = true;
            }
            if let Some(val) = value.take() {
                if self.value != val {
                    self.value = val;
                    modified = true;
                }
            }
        } else {
            mark_out_of_range!(self, oid, value);
            modified = true;
        }
        if modified {
            self.ieid = IEID::generate(boot_id);
            self.t = Time::now().timestamp();
            true
        } else {
            false
        }
    }
    pub fn serialize_db_into(&self, result: &mut BTreeMap<Value, Value>) {
        result.insert("status".into(), Value::from(self.status));
        result.insert("value".into(), self.value.clone());
        result.insert("ieid".into(), self.ieid.to_value());
        result.insert("t".into(), Value::from(self.t));
    }
    #[inline]
    pub fn serialize_into(&self, result: &mut BTreeMap<Value, Value>) {
        self.serialize_db_into(result);
        self.act
            .as_ref()
            .map(|v| result.insert("act".into(), Value::U64(*v as u64)));
    }
    /// returns true if the item status is changed after the application
    #[inline]
    pub fn apply_logic(&mut self, logic: Option<&Logic>, oid: &OID, boot_id: u64) -> bool {
        if self.status == ITEM_STATUS_ERROR || validate_logic(Some(&self.value), logic) {
            false
        } else {
            mark_out_of_range!(self, oid, self.value);
            self.ieid = IEID::generate(boot_id);
            self.t = Time::now().timestamp();
            true
        }
    }
    pub fn ieid(&self) -> &IEID {
        &self.ieid
    }
    pub fn status(&self) -> ItemStatus {
        self.status
    }
}

#[derive(Debug, Default, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
struct Range {
    #[serde(default)]
    min: Option<f64>,
    #[serde(default)]
    max: Option<f64>,
    #[serde(default)]
    min_eq: bool,
    #[serde(default)]
    max_eq: bool,
}

#[derive(Debug, Default, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct Logic {
    range: Option<Range>, // Optional value checker (local only)
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct ServiceOptions {
    svc: String,
    #[serde(default)]
    timeout: f64, // zero for the default timeout
    #[serde(default, skip_serializing_if = "Option::is_none")]
    config: Option<Value>, // optional config
}

impl ServiceOptions {
    pub fn svc(&self) -> &str {
        &self.svc
    }
    pub fn timeout(&self) -> Option<Duration> {
        if self.timeout > 0.0 {
            Some(Duration::from_secs_f64(self.timeout))
        } else {
            None
        }
    }
    pub fn config(&self) -> Option<&Value> {
        self.config.as_ref()
    }
}

#[derive(Serialize, Deserialize, Debug)]
#[serde(deny_unknown_fields)]
pub struct ItemData {
    //#[serde(deserialize_with = "eva_common::deserialize_oid")]
    oid: OID,
    #[serde(skip_serializing)]
    state: Option<Mutex<ItemState>>, // None for macros
    meta: Option<Value>, // Optional location
    // props
    #[serde(skip)]
    source: Option<Source>, // None for local items
    enabled: atomic::AtomicBool,
    // local config only
    logic: Option<Logic>, // Optional logic (for all, except macros), local only
    action: Option<ServiceOptions>, // for units and lmacros
}

#[inline]
pub fn serialize_db_state_from(state: &ItemState) -> BTreeMap<Value, Value> {
    let mut result = BTreeMap::new();
    state.serialize_db_into(&mut result);
    result
}

impl ItemData {
    fn from_repl_item(i: ReplicationInventoryItem, source: Source) -> Self {
        Self {
            oid: i.oid,
            state: Some(Mutex::new(ItemState::new(
                i.status.unwrap_or_default(),
                i.value.unwrap_or_default(),
                i.act,
                i.ieid,
                i.t,
            ))),
            meta: i.meta,
            source: Some(source),
            enabled: atomic::AtomicBool::new(i.enabled),
            logic: None,
            action: None,
        }
    }
    #[inline]
    pub fn oid(&self) -> &OID {
        &self.oid
    }
    #[inline]
    pub fn state(&self) -> Option<&Mutex<ItemState>> {
        self.state.as_ref()
    }
    #[inline]
    pub fn source(&self) -> Option<&Source> {
        self.source.as_ref()
    }
    #[inline]
    pub fn meta(&self) -> Option<&Value> {
        self.meta.as_ref()
    }
    #[inline]
    pub fn is_enabled(&self) -> bool {
        self.enabled.load(atomic::Ordering::SeqCst)
    }
    #[inline]
    pub fn set_enabled(&self, val: bool) {
        self.enabled.store(val, atomic::Ordering::SeqCst);
    }
    #[inline]
    pub fn logic(&self) -> Option<&Logic> {
        self.logic.as_ref()
    }
    #[inline]
    pub fn action(&self) -> Option<&ServiceOptions> {
        self.action.as_ref()
    }
    #[inline]
    fn serialize_short_state_into(&self, result: &mut BTreeMap<Value, Value>) {
        if let Some(ref st) = self.state {
            let state = st.lock().unwrap();
            state.serialize_into(result);
        }
    }
    /// # Panics
    ///
    /// Will panic if the state mutex is poisoned
    #[inline]
    pub fn serialize_db_state(&self) -> Option<BTreeMap<Value, Value>> {
        if let Some(ref st) = self.state {
            let mut result = BTreeMap::new();
            let state = st.lock().unwrap();
            state.serialize_db_into(&mut result);
            Some(result)
        } else {
            None
        }
    }
    #[inline]
    fn serialize_meta_into(&self, result: &mut BTreeMap<Value, Value>) {
        if let Some(ref meta) = self.meta {
            result.insert("meta".into(), meta.clone());
        }
    }
    #[inline]
    fn serialize_enabled_into(&self, result: &mut BTreeMap<Value, Value>) {
        result.insert("enabled".into(), Value::from(self.is_enabled()));
    }
    #[inline]
    pub fn prepare_serialization_payload(&self) -> BTreeMap<Value, Value> {
        let mut result: BTreeMap<Value, Value> = BTreeMap::new();
        result.insert("oid".into(), Value::from(&self.oid));
        result
    }
    // when called for db state result, converts it to normal state
    #[inline]
    pub fn serialize_state_props_into(&self, hostname: &str, result: &mut BTreeMap<Value, Value>) {
        result.insert(
            "connected".into(),
            Value::from(
                self.source
                    .as_ref()
                    .map_or(true, |s| s.data.online.load(atomic::Ordering::SeqCst)),
            ),
        );
        result.insert(
            "node".into(),
            Value::from(self.source.as_ref().map_or(hostname, |s| &s.node)),
        );
    }
    #[inline]
    pub fn serialize_state(&self, hostname: &str) -> BTreeMap<Value, Value> {
        let mut result = self.prepare_serialization_payload();
        self.serialize_short_state_into(&mut result);
        self.serialize_state_props_into(hostname, &mut result);
        result
    }
    #[inline]
    pub fn serialize_state_from(
        &self,
        state: &ItemState,
        hostname: &str,
    ) -> BTreeMap<Value, Value> {
        let mut result = self.prepare_serialization_payload();
        state.serialize_into(&mut result);
        self.serialize_state_props_into(hostname, &mut result);
        result
    }
    #[inline]
    pub fn serialize_state_basic(&self) -> Option<BTreeMap<Value, Value>> {
        if self.state.is_some() {
            let mut result = self.prepare_serialization_payload();
            self.serialize_short_state_into(&mut result);
            Some(result)
        } else {
            None
        }
    }
    // serialize basic state (e.g. for replication and clients)
    #[inline]
    pub fn serialize_state_basic_from(&self, state: &ItemState) -> BTreeMap<Value, Value> {
        let mut result = self.prepare_serialization_payload();
        state.serialize_into(&mut result);
        result
    }
    // serialize state + descrptions (e.g. for replication and clients on reloads)
    #[inline]
    pub fn serialize_state_full(&self, hostname: &str) -> BTreeMap<Value, Value> {
        let mut result = self.serialize_state(hostname);
        self.serialize_meta_into(&mut result);
        self.serialize_enabled_into(&mut result);
        //self.serialize_location_into(&mut result);
        result
    }
    // serialize config
    // returns None for remote items
    pub fn serialize_config(&self) -> Option<BTreeMap<Value, Value>> {
        if self.source.is_some() {
            return None;
        }
        let mut result = self.prepare_serialization_payload();
        self.serialize_meta_into(&mut result);
        //self.serialize_location_into(&mut result);
        self.serialize_enabled_into(&mut result);
        self.serialize_logic_into(&mut result);
        self.serialize_action_into(&mut result);
        Some(result)
    }
    #[inline]
    fn serialize_logic_into(&self, result: &mut BTreeMap<Value, Value>) {
        if let Some(ref logic) = self.logic {
            result.insert("logic".into(), to_value(logic).unwrap());
        }
    }
    #[inline]
    fn serialize_action_into(&self, result: &mut BTreeMap<Value, Value>) {
        if let Some(ref action) = self.action {
            result.insert("action".into(), to_value(action).unwrap());
        }
    }
}

impl Hash for ItemData {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.oid.hash(state);
    }
}

impl Eq for ItemData {}

impl PartialEq for ItemData {
    fn eq(&self, other: &Self) -> bool {
        self.oid == other.oid
    }
}

#[derive(Debug)]
pub struct SourceData {
    online: atomic::AtomicBool,
    destroyed: atomic::AtomicBool,
    svc: String,
}

impl SourceData {
    fn new(svc: &str) -> Self {
        Self {
            online: atomic::AtomicBool::new(true),
            destroyed: <_>::default(),
            svc: svc.to_owned(),
        }
    }
}

pub type Item = Arc<ItemData>;
pub type Source = Arc<ItemSource>;

#[derive(Default)]
pub struct Inventory {
    items: ItemMap,
    // all elements should be ARC as hashmaps are CLONED for replication purposes
    items_by_source: HashMap<Option<String>, HashMap<Arc<OID>, Item>>,
    sources: HashMap<String, Source>,
}

// do not create heavy remove functions as they lock the inventory for a long time
impl Inventory {
    pub fn new() -> Self {
        <_>::default()
    }
    pub fn stats(&self) -> InventoryStats {
        let mut st = HashMap::new();
        let mut item_cnt = 0;
        for (src, items) in &self.items_by_source {
            item_cnt += items.len();
            st.insert(
                src.as_ref()
                    .map_or_else(|| crate::LOCAL_NODE_ALIAS.to_owned(), ToOwned::to_owned),
                items.len(),
            );
        }
        InventoryStats {
            sources: st,
            items: item_cnt,
        }
    }
    #[inline]
    pub fn get_or_create_source(&self, source_id: &str, svc: &str) -> Source {
        self.sources
            .get(source_id)
            .map_or_else(|| create_source(source_id, svc), Clone::clone)
    }
    #[inline]
    pub fn get_items_by_source(&self, source_id: &str) -> Option<HashMap<Arc<OID>, Item>> {
        self.items_by_source
            .get(&Some(source_id.to_owned()))
            .map(Clone::clone)
    }
    #[inline]
    pub fn list_local_items(&self) -> Vec<Item> {
        let mut result = Vec::new();
        if let Some(items) = self.items_by_source.get(&None) {
            for item in items.values() {
                result.push(item.clone());
            }
        }
        result
    }
    #[inline]
    pub fn mark_source_online(&self, source_id: &str, online: bool) {
        if let Some(source) = self.sources.get(source_id) {
            source.mark_online(online);
        }
    }
    /// # Panics
    ///
    /// Will panic if the mutex is poisoned
    ///
    /// requires boot id - generates new IEID and state if missing
    pub fn append_item_from_value(
        &mut self,
        oid: Option<&OID>,
        config_value: serde_json::Value, // use serde json value as registry returns them
        state: Option<ItemState>,
        boot_id: u64,
        replace: bool,
        allow_remote: bool,
    ) -> EResult<Item> {
        let mut data: ItemData = serde_json::from_value(config_value)?;
        if replace
            && state.is_none()
            && data.state.is_none()
            && data.oid.kind() != EvaItemKind::Lmacro
        {
            if let Some(item) = self.get_item(&data.oid) {
                if let Some(ref old_stc) = item.state {
                    data.state = Some(Mutex::new(old_stc.lock().unwrap().clone()));
                }
            }
        }
        if let Some(o) = oid {
            if data.oid != *o {
                return Err(Error::invalid_data("oid does not match"));
            }
        }
        if data.source.is_some() && !allow_remote {
            return Err(Error::invalid_data("unable to append remote item"));
        }
        macro_rules! check_none {
            ($field: expr, $n: expr) => {
                if $field.is_some() {
                    return Err(Error::invalid_data(format!("unsupported field: {}", $n)));
                }
            };
        }
        let tp = data.oid.kind();
        match tp {
            EvaItemKind::Unit => {}
            EvaItemKind::Sensor | EvaItemKind::Lvar => {
                check_none!(data.action, "action");
            }
            EvaItemKind::Lmacro => {
                check_none!(data.logic, "logic");
            }
        }
        if let Some(st) = state {
            data.state = Some(Mutex::new(st));
        } else if data.state.is_none() && tp != EvaItemKind::Lmacro {
            data.state = Some(Mutex::new(ItemState::new0(IEID::generate(boot_id), tp)));
        }
        self.append_item(Arc::new(data), replace)
    }
    // creates empty item
    pub fn create_item(
        &mut self,
        oid: OID,
        ieid: Option<IEID>,
        from: Option<(&str, &str)>, // source_id , svc
    ) -> EResult<Item> {
        let tp = oid.kind();
        let source = from.as_ref().map(|i| {
            if let Some(src) = self.sources.get(i.0) {
                src.clone()
            } else {
                create_source(i.0, i.1)
            }
        });
        let item = Arc::new(ItemData {
            oid,
            state: if tp == EvaItemKind::Lmacro {
                None
            } else {
                Some(Mutex::new(ItemState::new0(
                    ieid.ok_or_else(|| Error::invalid_data("IEID not specified"))?,
                    tp,
                )))
            },
            source,
            enabled: atomic::AtomicBool::new(true),
            meta: None,
            logic: None,
            action: None,
        });
        self.append_item(item, false)
    }
    pub fn append_remote_item(
        &mut self,
        remote_item: ReplicationInventoryItem,
        source: Source,
    ) -> EResult<Item> {
        let item_data: ItemData = ItemData::from_repl_item(remote_item, source);
        self.append_item(Arc::new(item_data), true)
    }
    fn append_item(&mut self, item: Item, replace: bool) -> EResult<Item> {
        let source = item.source.clone();
        self.items.append(item.clone(), replace)?;
        let mut node = source.as_ref().map(|i| i.node.clone());
        if let Some(items) = self.items_by_source.get_mut(&node) {
            items.insert(Arc::new(item.oid.clone()), item.clone());
        } else {
            let mut items = HashMap::new();
            items.insert(Arc::new(item.oid.clone()), item.clone());
            if let Some(s) = source {
                self.items_by_source.insert(node.clone(), items);
                self.sources.insert(node.take().unwrap(), s);
            } else {
                self.items_by_source.insert(node, items);
            }
        }
        Ok(item)
    }
    #[inline]
    pub fn get_items_by_mask(&self, mask: &OIDMask, filter: &Filter) -> Vec<Item> {
        self.items.get_by_mask(mask, filter)
    }
    #[inline]
    pub fn get_item(&self, oid: &OID) -> Option<Item> {
        self.items.get(oid)
    }
    #[inline]
    pub fn remove_item(&mut self, oid: &OID) -> Option<Item> {
        if let Some(item) = self.items.remove(oid) {
            let node = item.source.as_ref().map(|x| x.node.clone());
            if let Some(items) = self.items_by_source.get_mut(&node) {
                items.remove(&item.oid);
                if items.is_empty() {
                    self.items_by_source.remove(&node);
                    node.map(|ref i| self.sources.remove(i));
                }
            }
            Some(item)
        } else {
            None
        }
    }
}

#[derive(Default, Debug)]
struct ItemTree {
    childs: HashMap<String, ItemTree>,
    childs_any: Option<Box<ItemTree>>,
    members: HashMap<OID, Item>,
    members_wildcard: HashMap<OID, Item>,
}

impl ItemTree {
    fn is_empty(&self) -> bool {
        self.childs.is_empty() && self.members.is_empty()
    }
}

#[derive(Debug, Default)]
pub struct ItemMap {
    unit: ItemTree,
    sensor: ItemTree,
    lvar: ItemTree,
    lmacro: ItemTree,
}

pub enum NodeFilter<'a> {
    Local,
    Remote(&'a str),
}

#[derive(Default)]
pub struct Filter<'a> {
    include: Option<&'a OIDMaskList>,
    exclude: Option<&'a OIDMaskList>,
    node: Option<NodeFilter<'a>>,
}

impl<'a> Filter<'a> {
    pub fn new() -> Self {
        Self::default()
    }
    pub fn include(mut self, mask_list: &'a OIDMaskList) -> Self {
        self.include = Some(mask_list);
        self
    }
    pub fn exclude(mut self, mask_list: &'a OIDMaskList) -> Self {
        self.exclude = Some(mask_list);
        self
    }
    pub fn node(mut self, sid: NodeFilter<'a>) -> Self {
        self.node = Some(sid);
        self
    }
    #[inline]
    pub fn set_include(&mut self, mask_list: &'a OIDMaskList) {
        self.include = Some(mask_list);
    }
    #[inline]
    pub fn set_exclude(&mut self, mask_list: &'a OIDMaskList) {
        self.exclude = Some(mask_list);
    }
    #[inline]
    pub fn set_node(&mut self, sid: NodeFilter<'a>) {
        self.node = Some(sid);
    }
    #[inline]
    pub fn matches(&self, item: &Item) -> bool {
        if let Some(ref node) = self.node {
            match node {
                NodeFilter::Local => {
                    if item.source.is_some() {
                        return false;
                    }
                }
                NodeFilter::Remote(id) => {
                    if let Some(ref source) = item.source {
                        if *id != "#" && *id != "*" && source.node != *id {
                            return false;
                        }
                    } else {
                        return false;
                    }
                }
            }
        }
        if let Some(f) = self.include {
            if !f.matches(&item.oid) {
                return false;
            }
        }
        if let Some(f) = self.exclude {
            !f.matches(&item.oid)
        } else {
            true
        }
    }
}

impl ItemMap {
    #[inline]
    fn get_tree(&self, tp: EvaItemKind) -> &ItemTree {
        match tp {
            EvaItemKind::Unit => &self.unit,
            EvaItemKind::Sensor => &self.sensor,
            EvaItemKind::Lvar => &self.lvar,
            EvaItemKind::Lmacro => &self.lmacro,
            //_ => Err(Error::not_implemented()),
        }
    }
    #[inline]
    fn get_tree_mut(&mut self, tp: EvaItemKind) -> &mut ItemTree {
        match tp {
            EvaItemKind::Unit => &mut self.unit,
            EvaItemKind::Sensor => &mut self.sensor,
            EvaItemKind::Lvar => &mut self.lvar,
            EvaItemKind::Lmacro => &mut self.lmacro,
            //_ => Err(Error::not_implemented()),
        }
    }
    #[inline]
    pub fn append(&mut self, item: Item, replace: bool) -> EResult<()> {
        let tree = self.get_tree_mut(item.oid.kind());
        append_item_rec(tree, item.oid.full_id().split('/'), &item, replace)
    }
    #[inline]
    pub fn get(&self, oid: &OID) -> Option<Item> {
        let tree = self.get_tree(oid.kind());
        get_item_rec(tree, oid.full_id().split('/'))
    }
    #[inline]
    pub fn remove(&mut self, oid: &OID) -> Option<Item> {
        let tree = self.get_tree_mut(oid.kind());
        remove_item_rec(tree, oid.full_id().split('/'), oid)
    }
    pub fn get_by_mask(&self, mask: &OIDMask, filter: &Filter) -> Vec<Item> {
        if let Some(tp) = mask.kind() {
            let tree = self.get_tree(tp);
            if let Some(chunks) = mask.chunks() {
                let mut result = Vec::new();
                get_item_by_mask_rec(tree, chunks.iter(), &mut result, filter);
                result
            } else {
                tree.members_wildcard
                    .values()
                    .filter(|x| filter.matches(x))
                    .cloned()
                    .collect()
            }
        } else {
            let mut result = Vec::new();
            if let Some(chunks) = mask.chunks() {
                get_item_by_mask_rec(&self.unit, chunks.iter(), &mut result, filter);
                get_item_by_mask_rec(&self.sensor, chunks.iter(), &mut result, filter);
                get_item_by_mask_rec(&self.lvar, chunks.iter(), &mut result, filter);
                get_item_by_mask_rec(&self.lmacro, chunks.iter(), &mut result, filter);
            } else {
                result.extend(
                    self.unit
                        .members_wildcard
                        .values()
                        .filter(|x| filter.matches(x))
                        .cloned()
                        .collect::<Vec<Item>>(),
                );
                result.extend(
                    self.sensor
                        .members_wildcard
                        .values()
                        .filter(|x| filter.matches(x))
                        .cloned()
                        .collect::<Vec<Item>>(),
                );
                result.extend(
                    self.lvar
                        .members_wildcard
                        .values()
                        .filter(|x| filter.matches(x))
                        .cloned()
                        .collect::<Vec<Item>>(),
                );
                result.extend(
                    self.lmacro
                        .members_wildcard
                        .values()
                        .filter(|x| filter.matches(x))
                        .cloned()
                        .collect::<Vec<Item>>(),
                );
            }
            result
        }
    }
}

fn get_item_rec(tree: &ItemTree, mut sp: Split<char>) -> Option<Item> {
    if let Some(chunk) = sp.next() {
        if let Some(child) = tree.childs.get(chunk) {
            get_item_rec(child, sp)
        } else {
            None
        }
    } else if tree.members.is_empty() {
        None
    } else {
        Some(tree.members.values().next().unwrap().clone())
    }
}
fn remove_item_rec(tree: &mut ItemTree, mut sp: Split<char>, oid: &OID) -> Option<Item> {
    if let Some(chunk) = sp.next() {
        tree.members_wildcard.remove(oid)?;
        let item = if let Some(c) = tree.childs.get_mut(chunk) {
            let item = remove_item_rec(c, sp.clone(), oid)?;
            if c.is_empty() {
                tree.childs.remove(chunk);
            }
            item
        } else {
            return None;
        };
        if let Some(ref mut c) = tree.childs_any {
            remove_item_rec(c, sp, oid)?;
            if c.is_empty() {
                tree.childs_any = None;
            }
        }
        Some(item)
    } else {
        tree.members.remove(oid)
    }
}

fn get_item_by_mask_rec(
    tree: &ItemTree,
    mut iter: std::slice::Iter<&str>,
    result: &mut Vec<Item>,
    filter: &Filter,
) {
    if let Some(chunk) = iter.next() {
        if *chunk == "#" {
            result.extend(
                tree.members_wildcard
                    .values()
                    .filter(|x| filter.matches(x))
                    .cloned()
                    .collect::<Vec<Item>>(),
            );
        } else if *chunk == "+" {
            if let Some(ref child) = tree.childs_any {
                get_item_by_mask_rec(child, iter, result, filter);
            }
        } else if let Some(child) = tree.childs.get(*chunk) {
            get_item_by_mask_rec(child, iter, result, filter);
        }
    } else {
        result.extend(
            tree.members
                .values()
                .filter(|x| filter.matches(x))
                .cloned()
                .collect::<Vec<Item>>(),
        );
    }
}

fn append_item_rec(
    tree: &mut ItemTree,
    mut sp: Split<char>,
    item: &Item,
    replace: bool,
) -> EResult<()> {
    if let Some(chunk) = sp.next() {
        if tree.members_wildcard.contains_key(&item.oid) && !replace {
            return Err(Error::busy(format!(
                "item {} is already registered",
                item.oid
            )));
        }
        tree.members_wildcard.insert(item.oid.clone(), item.clone());
        if let Some(c) = tree.childs.get_mut(chunk) {
            append_item_rec(c, sp.clone(), item, replace)?;
        } else {
            let mut child = ItemTree::default();
            append_item_rec(&mut child, sp.clone(), item, replace)?;
            tree.childs.insert(chunk.to_owned(), child);
        }
        if let Some(ref mut c) = tree.childs_any {
            append_item_rec(c, sp, item, replace)
        } else {
            let mut child = ItemTree::default();
            append_item_rec(&mut child, sp, item, replace)?;
            tree.childs_any.replace(Box::new(child));
            Ok(())
        }
    } else if tree.members.contains_key(&item.oid) && !replace {
        Err(Error::busy(format!(
            "item {} is already registered",
            item.oid
        )))
    } else {
        tree.members.insert(item.oid.clone(), item.clone());
        Ok(())
    }
}