use diesel::backend::Backend;
use diesel::deserialize;
use diesel::deserialize::FromSql;
use diesel::serialize;
use diesel::serialize::{Output, ToSql};
use diesel::sql_types::Text;
use diesel::sqlite::Sqlite;
use lazy_static::lazy_static;
use serde_derive::{Deserialize, Serialize};
use std::fmt;
use std::io::Write;
use std::str;

#[derive(Eq, PartialEq, Hash, Clone, Debug, Serialize, Deserialize, AsExpression, FromSqlRow)]
#[sql_type = "Text"]
pub struct ArticleID(String);
impl ArticleID {
    pub fn new(id: &str) -> ArticleID {
        ArticleID(id.to_owned())
    }
}

impl ArticleID {
    pub fn to_str(&self) -> &str {
        &self.0
    }
}

impl fmt::Display for ArticleID {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.0)
    }
}

impl FromSql<Text, Sqlite> for ArticleID {
    fn from_sql(bytes: Option<&<Sqlite as Backend>::RawValue>) -> deserialize::Result<Self> {
        let bytes = not_none!(bytes).read_blob();
        let string = str::from_utf8(bytes as &[u8])?;
        Ok(ArticleID::new(&string))
    }
}

impl ToSql<Text, Sqlite> for ArticleID {
    fn to_sql<W: Write>(&self, out: &mut Output<'_, W, Sqlite>) -> serialize::Result {
        ToSql::<Text, Sqlite>::to_sql(&self.to_string(), out)
    }
}

//------------------------------------------------------------------

#[derive(Eq, PartialEq, Hash, Clone, Debug, Serialize, Deserialize, AsExpression, FromSqlRow)]
#[sql_type = "Text"]
pub struct FeedID(String);
impl FeedID {
    pub fn new(id: &str) -> FeedID {
        FeedID(id.to_owned())
    }
}

impl FeedID {
    pub fn to_str(&self) -> &str {
        &self.0
    }
}

impl fmt::Display for FeedID {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.0)
    }
}

impl FromSql<Text, Sqlite> for FeedID {
    fn from_sql(bytes: Option<&<Sqlite as Backend>::RawValue>) -> deserialize::Result<Self> {
        let bytes = not_none!(bytes).read_blob();
        let string = str::from_utf8(bytes as &[u8])?;
        Ok(FeedID::new(&string))
    }
}

impl ToSql<Text, Sqlite> for FeedID {
    fn to_sql<W: Write>(&self, out: &mut Output<'_, W, Sqlite>) -> serialize::Result {
        ToSql::<Text, Sqlite>::to_sql(&self.to_string(), out)
    }
}

//------------------------------------------------------------------

lazy_static! {
    pub static ref NEWSFLASH_TOPLEVEL: CategoryID = CategoryID("NewsFlash.Toplevel".to_owned());
}

#[derive(Eq, PartialEq, Hash, Clone, Debug, Serialize, Deserialize, AsExpression, FromSqlRow)]
#[sql_type = "Text"]
pub struct CategoryID(String);
impl CategoryID {
    pub fn new(id: &str) -> CategoryID {
        CategoryID(id.to_owned())
    }
}

impl CategoryID {
    pub fn to_str(&self) -> &str {
        &self.0
    }
}

impl fmt::Display for CategoryID {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.0)
    }
}

impl FromSql<Text, Sqlite> for CategoryID {
    fn from_sql(bytes: Option<&<Sqlite as Backend>::RawValue>) -> deserialize::Result<Self> {
        let bytes = not_none!(bytes).read_blob();
        let string = str::from_utf8(bytes as &[u8])?;
        Ok(CategoryID::new(&string))
    }
}

impl ToSql<Text, Sqlite> for CategoryID {
    fn to_sql<W: Write>(&self, out: &mut Output<'_, W, Sqlite>) -> serialize::Result {
        ToSql::<Text, Sqlite>::to_sql(&self.to_string(), out)
    }
}

//------------------------------------------------------------------

#[derive(Eq, PartialEq, Hash, Clone, Debug, Serialize, Deserialize, AsExpression, FromSqlRow)]
#[sql_type = "Text"]
pub struct TagID(String);
impl TagID {
    pub fn new(id: &str) -> TagID {
        TagID(id.to_owned())
    }
}

impl TagID {
    pub fn to_str(&self) -> &str {
        &self.0
    }
}

impl fmt::Display for TagID {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.0)
    }
}

impl FromSql<Text, Sqlite> for TagID {
    fn from_sql(bytes: Option<&<Sqlite as Backend>::RawValue>) -> deserialize::Result<Self> {
        let bytes = not_none!(bytes).read_blob();
        let string = str::from_utf8(bytes as &[u8])?;
        Ok(TagID::new(&string))
    }
}

impl ToSql<Text, Sqlite> for TagID {
    fn to_sql<W: Write>(&self, out: &mut Output<'_, W, Sqlite>) -> serialize::Result {
        ToSql::<Text, Sqlite>::to_sql(&self.to_string(), out)
    }
}

//------------------------------------------------------------------

#[derive(Eq, PartialEq, Hash, Clone, Debug, Serialize, Deserialize, AsExpression, FromSqlRow)]
#[sql_type = "Text"]
pub struct PluginID(String);
impl PluginID {
    pub fn new(id: &str) -> PluginID {
        PluginID(id.to_owned())
    }
}

impl PluginID {
    pub fn to_str(&self) -> &str {
        &self.0
    }
}

impl fmt::Display for PluginID {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.0)
    }
}

impl FromSql<Text, Sqlite> for PluginID {
    fn from_sql(bytes: Option<&<Sqlite as Backend>::RawValue>) -> deserialize::Result<Self> {
        let bytes = not_none!(bytes).read_blob();
        let string = str::from_utf8(bytes as &[u8])?;
        Ok(PluginID::new(&string))
    }
}

impl ToSql<Text, Sqlite> for PluginID {
    fn to_sql<W: Write>(&self, out: &mut Output<'_, W, Sqlite>) -> serialize::Result {
        ToSql::<Text, Sqlite>::to_sql(&self.to_string(), out)
    }
}
