extern crate rexiv2;
extern crate num_rational;
extern crate chrono;

use serde::{Deserialize, Serialize};
use std::fs::{DirEntry};
use std::time::SystemTime;
use chrono::prelude::*;
use std::borrow::Borrow;
use std::hash::{Hash, Hasher};

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Picture {
    pub filename: String,
    // if media-type is None, file is not a picture
    pub media_type: Option<String>,
    pub extension: String,
    pub path: String,
    pub file_datetime: DateTime<Utc>,
    pub harvest_datetime: DateTime<Utc>,
    pub picture_datetime: Option<DateTime<Utc>>,
    pub exposure_time: Option<i32>,
    pub f_number: Option<f64>,
    pub focal_length: Option<f64>,
    pub iso_speed: Option<i32>,
    // thumbnail: Option<&[u8]>,
    pub width: Option<i32>,
    pub height: Option<i32>,
    pub size: u64,
    pub device: Option<String>,
    pub author: Option<String>,
}

impl Picture {
    pub fn new(entry: DirEntry) -> Picture {
        let mut file = Picture {
            filename: entry.file_name().into_string().unwrap_or_default(),
            media_type: None,
            extension: entry.path().extension().unwrap_or_default().to_os_string().into_string().unwrap(),
            path: entry.path().into_os_string().into_string().unwrap(),
            file_datetime: Picture::convert_date_from_system_time(entry.borrow()),
            harvest_datetime: DateTime::from(SystemTime::now()),
            picture_datetime: None,
            exposure_time: None,
            f_number: None,
            focal_length: None,
            iso_speed: None,
            width: None,
            height: None,
            size: entry.metadata().unwrap().len(),
            device: None,
            author: None,
        };

        let meta = rexiv2::Metadata::new_from_path(&entry.path());
        if meta.is_ok() {
            let metadata = meta.unwrap();
            file.media_type = Some(metadata.get_media_type().unwrap().to_string());
            file.picture_datetime = Picture::convert_date_from_string(
                metadata.get_tag_string("Exif.Image.DateTime").unwrap_or_default());
            file.exposure_time = if metadata.get_exposure_time().is_some() { Some(metadata.get_exposure_time().unwrap().to_integer()) } else { None };
            file.f_number = metadata.get_fnumber();
            file.focal_length = metadata.get_focal_length();
            file.iso_speed = metadata.get_iso_speed();
            file.width = Some(metadata.get_pixel_width());
            file.height = Some(metadata.get_pixel_height());
            file.device = Some(metadata.get_tag_string("Exif.Image.Model").unwrap_or_default());
            file.author = Some(metadata.get_tag_string("Exif.Image.Artist").unwrap_or_default());
        }
        file
    }

    fn convert_date_from_system_time(entry: &DirEntry) -> DateTime<Utc> {
        let metadata = entry.metadata().unwrap();
        let system_date = metadata.modified().unwrap_or(
            SystemTime::from(DateTime::parse_from_str("1978-03-18 00:05:00 +01:00", "%Y-%m-%d %H:%M:%S %z").unwrap()));
        DateTime::from(system_date)
    }

    fn convert_date_from_string(date: String) -> Option<DateTime<Utc>> {
        let dt = Utc.datetime_from_str(date.as_ref(), "%Y:%m:%d %H:%M:%S");
        if dt.is_ok() {
            Some(dt.unwrap())
        } else {
            None
        }
    }
}

impl PartialEq for Picture {
    fn eq(&self, other: &Self) -> bool {
        self.filename == other.filename
            && self.media_type == other.media_type
            && self.picture_datetime == other.picture_datetime
            && self.width == other.width
            && self.height == other.height
            && self.size == other.size
            && self.device == other.device
    }
}

impl Eq for Picture {}

impl Hash for Picture {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.filename.hash(state);
        self.media_type.hash(state);
        self.extension.hash(state);
        self.path.hash(state);
        self.file_datetime.hash(state);
        self.harvest_datetime.hash(state);
        self.picture_datetime.hash(state);
        self.exposure_time.hash(state);
        self.iso_speed.hash(state);
        self.width.hash(state);
        self.height.hash(state);
        self.size.hash(state);
        self.device.hash(state);
        self.author.hash(state);
    }
}
