// License: see LICENSE file at root directory of `master` branch

//! # Notifications

use {
    core::convert::TryInto,
    std::{
        convert::TryFrom,
        io::Error,
    },

    binn_ir::Value,

    crate::db::Id,
};

#[cfg(test)]
use std::collections::HashSet;

const KEY_ID: i32 = 0;
const KEY_DATE_CREATED: i32 = 1;
const KEY_USER_ID: i32 = 2;
const KEY_TITLE: i32 = 3;
const KEY_CONTENT: i32 = 4;
const KEY_DATA: i32 = 5;
const KEY_READ: i32 = 6;

#[test]
fn test_keys() {
    let keys = &[KEY_ID, KEY_DATE_CREATED, KEY_USER_ID, KEY_TITLE, KEY_CONTENT, KEY_DATA, KEY_READ];
    assert_eq!(keys.len(), keys.iter().collect::<HashSet<_>>().len());
}

/// # Notification
#[derive(Debug)]
pub struct Notification {
    id: Option<Id>,
    date_created: Option<u64>,
    user_id: Option<Id>,
    title: Option<String>,
    content: Option<String>,
    data: Option<Vec<u8>>,
    read: bool,
}

impl Notification {

    /// # Makes new instance
    pub const fn new(
        id: Option<Id>, date_created: Option<u64>, user_id: Option<Id>, title: Option<String>, content: Option<String>, data: Option<Vec<u8>>,
        read: bool,
    ) -> Self {
        Self {
            id,
            date_created,
            user_id,
            title,
            content,
            data,
            read,
        }
    }

    /// # ID
    pub fn id(&self) -> Option<Id> {
        self.id
    }

    /// # Date created
    pub fn date_created(&self) -> Option<u64> {
        self.date_created
    }

    /// # User ID
    pub fn user_id(&self) -> Option<Id> {
        self.user_id
    }

    /// Title
    pub fn title(&self) -> Option<&str> {
        self.title.as_ref().map(|t| t.as_str())
    }

    /// # Content
    pub fn content(&self) -> Option<&str> {
        self.content.as_ref().map(|c| c.as_str())
    }

    /// # Data
    pub fn data(&self) -> Option<&[u8]> {
        self.data.as_ref().map(|d| d.as_slice())
    }

    /// # Read flag
    pub fn is_read(&self) -> bool {
        self.read
    }

}

impl From<Notification> for Value {

    fn from(notification: Notification) -> Self {
        let mut map = binn_ir::Map::new();

        if let Some(id) = notification.id {
            binn_ir::map_insert(&mut map, KEY_ID, id);
        }
        if let Some(date_created) = notification.date_created {
            binn_ir::map_insert(&mut map, KEY_DATE_CREATED, date_created);
        }
        if let Some(user_id) = notification.user_id {
            binn_ir::map_insert(&mut map, KEY_USER_ID, user_id);
        }
        if let Some(title) = notification.title {
            binn_ir::map_insert(&mut map, KEY_TITLE, title);
        }
        if let Some(content) = notification.content {
            binn_ir::map_insert(&mut map, KEY_CONTENT, content);
        }
        if let Some(data) = notification.data {
            binn_ir::map_insert(&mut map, KEY_DATA, data);
        }
        binn_ir::map_insert(&mut map, KEY_READ, notification.read);

        map.into()
    }

}

impl TryFrom<Value> for Notification {

    type Error = Error;

    fn try_from(mut value: Value) -> Result<Self, Self::Error> {
        Ok(Self {
            id: match value.map_maybe_take_by(&[KEY_ID])? {
                Some(v) => Some(v.try_into()?),
                None => None,
            },
            date_created: match value.map_maybe_take_by(&[KEY_DATE_CREATED])? {
                Some(v) => Some(v.try_into()?),
                None => None,
            },
            user_id: match value.map_maybe_take_by(&[KEY_USER_ID])? {
                Some(v) => Some(v.try_into()?),
                None => None,
            },
            title: match value.map_maybe_take_by(&[KEY_TITLE])? {
                Some(v) => Some(v.try_into()?),
                None => None,
            },
            content: match value.map_maybe_take_by(&[KEY_CONTENT])? {
                Some(v) => Some(v.try_into()?),
                None => None,
            },
            data: match value.map_maybe_take_by(&[KEY_DATA])? {
                Some(v) => Some(v.try_into()?),
                None => None,
            },
            read: value.map_take_by(&[KEY_READ])?.try_into()?,
        })
    }

}
