// main author: Robin Willmann
// date : see git.
// WIP, changes welcome

extern crate image;

pub mod model {
    pub struct Sha256(pub [u8; 64]);

    pub struct Texture {
        pub id: String,
        pub name: String,
        pub tags: Vec<String>,
        pub format: TextureFormat,
        pub resolution: (usize, usize),
        pub texture_hash: Sha256,
    }

    pub enum TextureFormat {
        PNG,
        JPG,
    }
}

pub mod protocol {

    use crate::model::*;

    use std::io;

    pub enum ReplaceTextureStatus {
        // Call Again With Texture Binary
        NeedTextureData,
        // Done.
        Ok,
    }

    pub type ProtocolResult<T> = Result<T, ProtocolError>;
    pub enum ProtocolError {
        BadRequest(String),
        FileNotFound(String),
        Conflict(String),
        InternalServerError(std::io::Error),
        NotImplemented,
    }

    pub trait ProtocolHandler: Send + Sync {
        fn query(&mut self, query: &[String]) -> ProtocolResult<Vec<Texture>>;

        fn get_texture_by_id(&mut self, id: String) -> ProtocolResult<Option<Texture>>;

        fn get_texture_by_name(&mut self, id: String) -> ProtocolResult<Option<Texture>>;

        fn get_texture_file(&mut self, hash: Sha256) -> ProtocolResult<Vec<u8>>;

        fn get_texture_preview(&mut self, hash: Sha256) -> ProtocolResult<Vec<u8>>;

        fn replace_texture(
            &mut self,
            delete: Option<Texture>,
            insert: Option<Texture>,
            insert_texture_data: Option<Vec<u8>>,
        ) -> ProtocolResult<ReplaceTextureStatus>;
    }

    pub struct ProtocolConfig {
        pub port: u16,
    }

    pub fn listen_forever(handler: &ProtocolHandler) -> io::Result<()> {
        unimplemented!()
    }
}

pub mod persistency {

    use crate::model::*;

    use std::collections::HashMap;
    use std::io;
    use std::path::{Path, PathBuf};
    use std::sync::Arc;
    
    pub type TextureFileResult = Result< Arc<Vec<u8>> , TextureFileError>
    pub enum TextureFileError {
        NotFound,
        IOError(io::Error),
        ImgError(::image::Error),
    }

    pub struct DataStore {
        // private attributes
        // may change
        data_dir: PathBuf,
        texture: Vec<Texture>,
        preview_cache: HashMap<(TextureFormat, Sha256), Arc<Vec<u8>>>,
    }

    impl DataStore {
        pub fn new(path: &Path) -> io::Result<DataStore> {
            unimplemented!()
        }

        pub fn garbage_collect(&mut self) -> io::Result<()> {
            unimplemented!()
        }

        pub fn query(&mut self, query: &self::search::Query) -> Vec<Texture> {
            unimplemented!();
            // calls self::search::search(... )
        }

        /// returns true if successful
        pub fn delete(&mut self, tex: &Texture) -> bool {
            unimplemented!();
        }

        pub fn insert(&mut self, tex: Texture) -> bool {
            unimplemented!();
        }

        pub fn by_name<'a>(&'a self, name: &str) -> &'a Texture {
            unimplemented!();
        }

        pub fn by_id<'a, 'b>(&'a self, id: &'b str) -> &'a Texture {
            unimplemented!();
        }

        pub fn has_hash(&self, hash: &Sha256) -> bool {
            unimplemented!();
        }

        pub fn get_texture_file(&mut self, hash: &Sha256) -> TextureFileResult {
            unimplemented!();
        }

        pub fn get_texture_preview(&mut self, hash: &Sha256) -> TextureFileResult {
            unimplemented!();
        }
    }
    
    pub use self::search::Query;

    mod search {

        use crate::model::*;

        pub struct Query {
            filters: Vec<QueryFilterModifier>,
        }

        pub type QueryParserResult = Result<Query, QuerySyntaxError>;
        pub enum QuerySyntaxError {
            UnknownFilter,
        }

        impl Query {
            pub fn parse(input: &[String]) -> QueryParserResult {
                unimplemented!()
            }
        }

        pub fn search(input: &[Texture], query: &Query) -> Vec<Texture> {
            unimplemented!()
        }

        //private
        enum QueryFilterModifier {
            None(QueryFilter),
            Not(QueryFilter),
        }

        //private
        enum QueryFilter {
            TagName(String),
            InName(String),
            MinResolution(usize),
            BeforeDate { year: u16, month: u16, day: u16 },
        }
    }

    pub mod image_convert {
        use crate::model::*;

        pub struct ConvertConfig {
            pub desired_size: (usize, usize),
        }

        pub fn generate_preview(
            input: &[u8],
            format: TextureFormat,
            config: &ConvertConfig,
        ) -> ::image::ImageResult<Vec<u8>> {
            unimplemented!()
        }
    }
}

pub mod main {

    use crate::model::*;
    use crate::protocol::*;

    use std::sync::Arc;

    struct ServerState {
        // private attributes
    }

    impl ProtocolHandler for ServerState {
        fn query(&mut self, query: &[String]) -> ProtocolResult<Vec<Texture>> {
            unimplemented!()
        }

        fn get_texture_by_id(&mut self, id: String) -> ProtocolResult<Option<Texture>> {
            unimplemented!()
        }

        fn get_texture_by_name(&mut self, id: String) -> ProtocolResult<Option<Texture>> {
            unimplemented!()
        }

        fn get_texture_file(&mut self, hash: Sha256) -> ProtocolResult<Arc<Vec<u8>>> {
            unimplemented!()
        }

        fn get_texture_preview(&mut self, hash: Sha256) -> ProtocolResult<Arc<Vec<u8>>> {
            unimplemented!()
        }

        fn replace_texture(
            &mut self,
            delete: Option<Texture>,
            insert: Option<Texture>,
            insert_texture_data: Option<Vec<u8>>,
        ) -> ProtocolResult<ReplaceTextureStatus> {
            unimplemented!()
        }
    }

    pub fn main() {
        unimplemented!()
    }
}