/*
Copyright 2024, Savanni D'Gerinel <savanni@luminescent-dreams.com>

This file is part of FitnessTrax.

FitnessTrax is free software: you can redistribute it and/or modify it under the terms of the GNU
General Public License as published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.

FitnessTrax is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.

You should have received a copy of the GNU General Public License along with FitnessTrax. If not, see <https://www.gnu.org/licenses/>.
*/

use crate::{components::{i32_field_builder, TextEntry, month_field_builder}, types::ParseError};
use chrono::{Datelike, Local};
use glib::Object;
use gtk::{prelude::*, subclass::prelude::*};
use std::{cell::RefCell, rc::Rc};

pub struct DateFieldPrivate {
    date: Rc<RefCell<chrono::NaiveDate>>,
    year: TextEntry<i32>,
    month: TextEntry<u32>,
    day: TextEntry<u32>,
}

#[glib::object_subclass]
impl ObjectSubclass for DateFieldPrivate {
    const NAME: &'static str = "DateField";
    type Type = DateField;
    type ParentType = gtk::Box;

    fn new() -> Self {
        let date = Rc::new(RefCell::new(Local::now().date_naive()));

        let year = i32_field_builder()
            .with_value(date.borrow().year())
            .with_on_update(
            {
                let date = date.clone();
                move |value| {
                    if let Some(year) = value {
                        let mut date = date.borrow_mut();
                        if let Some(new_date) = date.with_year(year) {
                            *date = new_date;
                        }
                    }
                }
            })
            .with_length(4)
            .with_css_classes(vec!["date-field__year".to_owned()]).build();

        let month = month_field_builder()
            .with_value(date.borrow().month())
            .with_on_update(
            {
                let date = date.clone();
                move |value| {
                    if let Some(month) = value {
                        let mut date = date.borrow_mut();
                        if let Some(new_date) = date.with_month(month) {
                            *date = new_date;
                        }
                    }
                }
            })
            .with_css_classes(vec!["date-field__month".to_owned()])
            .build();

        /* Modify this so that it enforces the number of days per month */
        let day = TextEntry::builder()
            .with_placeholder("day".to_owned())
            .with_value(date.borrow().day())
            .with_renderer(|v| format!("{}", v))
            .with_parser(|v| v.parse::<u32>().map_err(|_| ParseError))
            .with_on_update({
                let date = date.clone();
                move |value| {
                    if let Some(day) = value {
                        let mut date = date.borrow_mut();
                        if let Some(new_date) = date.with_day(day) {
                            *date = new_date;
                        }
                    }
                }
            })
            .with_css_classes(vec!["date-field__day".to_owned()])
            .build();

        Self {
            date,
            year,
            month,
            day,
        }
    }

}

impl ObjectImpl for DateFieldPrivate {}
impl WidgetImpl for DateFieldPrivate {}
impl BoxImpl for DateFieldPrivate {}

glib::wrapper! {
    pub struct DateField(ObjectSubclass<DateFieldPrivate>) @extends gtk::Box, gtk::Widget;
}

/* Render a date in the format 2006 Jan 01. The entire date is editable. When the user moves to one part of the date, it will be erased and replaced with a grey placeholder.
 */
impl DateField {
    pub fn new(date: chrono::NaiveDate) -> Self {
        let s: Self = Object::builder().build();
        s.add_css_class("date-field");

        s.append(&s.imp().year.widget());
        s.append(&gtk::Label::new(Some("-")));
        s.append(&s.imp().month.widget());
        s.append(&gtk::Label::new(Some("-")));
        s.append(&s.imp().day.widget());

        s.set_date(date);

        s
    }

    pub fn set_date(&self, date: chrono::NaiveDate) {
        self.imp().year.set_value(Some(date.year()));
        self.imp().month.set_value(Some(date.month()));
        self.imp().day.set_value(Some(date.day()));

        *self.imp().date.borrow_mut() = date;
    }

    pub fn date(&self) -> chrono::NaiveDate {
        *self.imp().date.borrow()
    }
    /*
    pub fn is_valid(&self) -> bool {
        false
    }
    */
}

#[cfg(test)]
mod test {
    use super::*;
    // use crate::gtk_init::gtk_init;

    // Enabling this test pushes tests on the TextField into an infinite loop. That likely indicates a bad interaction within the TextField itself, and that is going to need to be fixed.
    #[test]
    #[ignore]
    fn it_allows_valid_dates() {
        let reference = chrono::NaiveDate::from_ymd_opt(2006, 01, 02).unwrap();
        let field = DateField::new(reference);
        field.imp().year.set_value(Some(2023));
        field.imp().month.set_value(Some(10));
        field.imp().day.set_value(Some(13));
        // assert!(field.is_valid());
    }
    
    #[test]
    #[ignore]
    fn it_disallows_out_of_range_months() {
        unimplemented!()
    }

    #[test]
    #[ignore]
    fn it_allows_days_within_range_for_month() {
        unimplemented!()
    }
}