Documentation
pub mod database;
pub mod eval;
pub mod task;
pub mod utils;

pub use crate::database::{Database, DatabaseKey};
pub use async_std::sync::{Arc, Mutex};
pub use botnet_utils::type_id;
pub use bytes::Bytes;
pub use nom::AsBytes;
use serde::{Deserialize, Serialize};
pub use serde_json::Value as SerdeValue;
use std::{
    collections::HashMap,
    hash::{Hash, Hasher},
};
use thiserror::Error;
pub use url::Url;

pub struct Input(pub Bytes);

impl Input {
    pub fn new(s: &'static str) -> Self {
        Self(Bytes::from(s.as_bytes()))
    }
}

pub type BotnetResult<T> = Result<T, BotnetError>;

pub mod prelude {
    pub use super::{
        eval::Evaluator, task::Task, type_id, utils::values_to_bytes, Arc, AsBytes,
        BotnetResult, Bytes, Database, DatabaseKey, Extractor, Extractors, Field,
        FieldMetadata, FieldType, Input, Key, KeyType, Metadata, Mutex, SerdeValue, Url,
    };
}

impl AsRef<str> for Input {
    fn as_ref(&self) -> &str {
        std::str::from_utf8(self.0.as_bytes()).expect("Bad input.")
    }
}

impl From<&'static str> for Input {
    fn from(value: &'static str) -> Self {
        Self::new(value)
    }
}

pub trait AsValue {
    fn as_value(&self) -> Bytes;
}

impl AsValue for bool {
    fn as_value(&self) -> Bytes {
        match self {
            true => Bytes::from("1"),
            false => Bytes::from("0"),
        }
    }
}

impl AsValue for u64 {
    fn as_value(&self) -> Bytes {
        Bytes::from(u64::to_le_bytes(*self).to_vec())
    }
}

impl AsValue for &'static str {
    fn as_value(&self) -> Bytes {
        Bytes::from(self.to_string())
    }
}

#[derive(Debug, Error)]
pub enum BotnetError {
    #[error("ParseError: {0:#?}")]
    ParseError(#[from] url::ParseError),
    #[error("BincodeError: {0:#?}")]
    BincodeError(#[from] bincode::Error),
    #[error("Utf8Error: {0:#?}")]
    Utf8Error(#[from] std::str::Utf8Error),
    #[cfg(feature = "redisdb")]
    #[error("RedisError: {0:#?}")]
    RedisError(#[from] redis::RedisError),
}

#[derive(Debug, Serialize, Deserialize, Default, Clone, Eq)]
pub struct ValueMap {
    items: HashMap<usize, Bytes>,
}

impl ValueMap {
    pub fn from_values(v: Vec<Bytes>) -> Self {
        Self {
            items: HashMap::from_iter(
                v.iter().map(|v| (type_id(v.as_bytes()), v.clone())),
            ),
        }
    }
}

impl Hash for ValueMap {
    fn hash<H: Hasher>(&self, state: &mut H) {
        for (k, v) in self.items.iter() {
            k.hash(state);
            v.hash(state);
        }
    }
}

impl PartialEq for ValueMap {
    fn eq(&self, other: &Self) -> bool {
        for k in self.items.keys() {
            if other.items.contains_key(k) {
                return true;
            }
        }
        false
    }
}

pub enum KeyType {
    Compressed,
    Decompressed,
}

pub enum FieldType {
    Compressed,
    Decompressed,
}

#[derive(Debug, Serialize, Deserialize, Default, Clone, Eq, PartialEq, Hash)]
pub struct FieldMetadata {
    name: String,
    key: String,
    value_map: ValueMap,
    type_id: usize,
    description: String,
}

impl FieldMetadata {
    pub fn new(name: &str, key: &str, values: Vec<Bytes>, description: &str) -> Self {
        Self {
            name: name.to_string(),
            key: key.to_string(),
            value_map: ValueMap::from_values(values),
            type_id: type_id(key),
            description: description.to_string(),
        }
    }
}

#[derive(Debug, Serialize, Deserialize, Default, Clone, Eq)]
pub struct KeyMetadata {
    field_meta: HashMap<String, FieldMetadata>,
    type_id: usize,
}

impl Hash for KeyMetadata {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.type_id.hash(state);
        for (k, v) in self.field_meta.iter() {
            k.hash(state);
            v.hash(state);
        }
    }
}

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

impl KeyMetadata {
    pub fn new() -> Self {
        Self {
            type_id: 0,
            field_meta: HashMap::default(),
        }
    }

    pub fn as_bytes(&self) -> BotnetResult<Bytes> {
        Ok(Bytes::from(bincode::serialize(&self)?))
    }

    pub fn field(&mut self, f: FieldMetadata) -> &mut Self {
        self.field_meta.insert(f.name.clone(), f);
        self
    }

    pub fn build(&self) -> Self {
        self.clone()
    }

    pub fn with_key_code(meta: Self, type_id: usize) -> Self {
        Self {
            field_meta: meta.field_meta,
            type_id,
        }
    }
}

#[derive(Debug, Default, Serialize, Deserialize, Clone, Eq, PartialEq, Hash)]
pub struct Field {
    pub type_id: usize,
    pub field_name: String,
    pub value: Bytes,
}

impl Field {
    pub fn new(field_name: &str, value: Bytes) -> Self {
        Self {
            type_id: type_id(field_name),
            field_name: field_name.to_string(),
            value,
        }
    }
}

pub trait Key {
    const NAME: &'static str;
    const TYPE_ID: usize;

    type Item;
    type Metadata;

    fn build(&self) -> Self;
    fn field(&mut self, f: Self::Item) -> &mut Self;
    fn flatten(&self) -> Bytes;
    fn get_metadata(&self) -> Self::Metadata;
    fn metadata(&mut self, meta: KeyMetadata) -> &mut Self;
    fn builder() -> Self;
    fn type_id(&self) -> usize;
    fn name(&self) -> &'static str;
}

pub struct Extractor {
    #[allow(unused)]
    key: String,
    func: fn(&Input) -> BotnetResult<Field>,
}

impl Default for Extractor {
    fn default() -> Self {
        fn default_func(_input: &Input) -> BotnetResult<Field> {
            Ok(Field::default())
        }

        Self {
            key: String::default(),
            func: default_func,
        }
    }
}

impl Extractor {
    pub fn new(key: &str, func: fn(&Input) -> BotnetResult<Field>) -> Self {
        Self {
            key: key.to_string(),
            func,
        }
    }

    pub fn call(&self, input: &Input) -> BotnetResult<Field> {
        (self.func)(input)
    }
}

#[derive(Default)]
pub struct Extractors {
    pub items: HashMap<String, Extractor>,
}

impl Extractors {
    pub fn new() -> Self {
        Self {
            items: HashMap::default(),
        }
    }

    pub fn add(&mut self, key: &str, value: fn(&Input) -> BotnetResult<Field>) {
        self.items
            .insert(key.to_string(), Extractor::new(key, value));
    }
}

#[derive(Default)]
pub struct Metadata {
    items: HashMap<usize, KeyMetadata>,
}

impl Metadata {
    pub fn new() -> Self {
        Self {
            items: HashMap::default(),
        }
    }

    pub fn insert(&mut self, ty_id: usize, meta: KeyMetadata) {
        self.items.insert(ty_id, meta);
    }

    pub fn get(&self, ty_id: &usize) -> &KeyMetadata {
        self.items.get(ty_id).unwrap()
    }
}