/* UtilLib - Copyright (c) 2016-2025 Vincent Calame - Exemole
 * Logiciel libre donné sous triple licence :
 * 1) selon les termes de la CeCILL V2
 * 2) selon les termes de l’EUPL V.1.1
 * 3) selon les termes de la GNU GPLv3
 * Voir le fichier licences.txt
 */


package net.mapeadores.util.date;

import java.io.Serializable;
import java.text.ParseException;
import java.time.LocalDate;
import java.time.temporal.ChronoField;
import java.time.temporal.TemporalAccessor;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Locale;


/**
 *
 * @author Vincent Calame
 */
public final class FuzzyDate implements Serializable, Comparable<FuzzyDate> {

    public static final String YEAR_TYPE = "year";
    public static final String HALFYEAR_TYPE = "halfyear";
    public static final String QUARTER_TYPE = "quarter";
    public static final String MONTH_TYPE = "month";
    public static final String DAY_TYPE = "day";
    private static final long serialVersionUID = 1L;
    private final int intValue;

    private FuzzyDate(int intValue) {
        this.intValue = intValue;
    }

    public String getDateType() {
        return FuzzyDateInteger.getDateType(intValue);
    }

    @Override
    public int hashCode() {
        return intValue;
    }

    @Override
    public boolean equals(Object other) {
        if (other == null) {
            return false;
        }
        if (other == this) {
            return true;
        }
        if (other.getClass() != this.getClass()) {
            return false;
        }
        FuzzyDate otherDate = (FuzzyDate) other;
        return (this.intValue == otherDate.intValue);
    }

    @Override
    public int compareTo(FuzzyDate anotherDate) {
        int thisVal = this.intValue;
        int anotherVal = anotherDate.intValue;
        return (thisVal < anotherVal ? -1 : (thisVal == anotherVal ? 0 : 1));
    }

    @Override
    public String toString() {
        return FuzzyDateInteger.toString(intValue);
    }

    public String toSortString() {
        return FuzzyDateInteger.toSortString(intValue);
    }

    public String toISOString() {
        return FuzzyDateInteger.toISOString(intValue, false);
    }

    public String toISOString(boolean lastDate) {
        return FuzzyDateInteger.toISOString(intValue, lastDate);
    }

    public String toMonthString(boolean lastMonth) {
        return FuzzyDateInteger.toMonthString(intValue, lastMonth);
    }

    public LocalDate toLocalDate() {
        return toLocalDate(false);
    }

    public LocalDate toLocalDate(boolean lastDay) {
        return FuzzyDateInteger.toLocaleDate(intValue, lastDay);
    }

    public GregorianCalendar toGregorianCalendar() {
        return FuzzyDateInteger.toGregorianCalendar(intValue);
    }

    public FuzzyDate truncate(String type) {
        return new FuzzyDate(FuzzyDateInteger.truncate(intValue, type));
    }

    public Date toDate() {
        return FuzzyDateInteger.toDate(intValue);
    }

    public int getYear() {
        return FuzzyDateInteger.getYear(intValue);
    }

    public boolean isYear() {
        return getDateType().equals(FuzzyDate.YEAR_TYPE);
    }

    public int getHalfYear() {
        return FuzzyDateInteger.getHalfYear(intValue);
    }

    public boolean isHalfYear() {
        return getDateType().equals(FuzzyDate.HALFYEAR_TYPE);
    }

    public int getQuarter() {
        return FuzzyDateInteger.getQuarter(intValue);
    }

    public boolean isQuarter() {
        return getDateType().equals(FuzzyDate.QUARTER_TYPE);
    }

    public int getMonth() {
        return FuzzyDateInteger.getMonth(intValue);
    }

    public boolean isMonth() {
        return FuzzyDateInteger.isMonth(intValue);
    }

    public int getDay() {
        return FuzzyDateInteger.getDay(intValue);
    }

    public boolean isDay() {
        return FuzzyDateInteger.isDay(intValue);
    }

    public FuzzyDate roll(int dayAmount) {
        return new FuzzyDate(FuzzyDateInteger.roll(intValue, dayAmount));
    }

    public static int toInt(FuzzyDate fuzzyDate) {
        return fuzzyDate.intValue;
    }

    public static FuzzyDate fromInt(int dateInt) {
        return new FuzzyDate(dateInt);
    }

    public static FuzzyDate parse(String s) throws ParseException {
        int intValue = FuzzyDateInteger.parse(s);
        return new FuzzyDate(intValue);
    }

    public static FuzzyDate parse(String s, Locale locale) throws ParseException {
        return new FuzzyDate(FuzzyDateInteger.parse(s, locale));
    }

    public static FuzzyDate fromYear(int year) {
        return new FuzzyDate(FuzzyDateInteger.fromYear(year));
    }

    public static FuzzyDate fromHalfYear(int year, int halfyear) {
        return new FuzzyDate(FuzzyDateInteger.fromHalfYear(year, halfyear));
    }

    public static FuzzyDate fromQuarter(int year, int quarter) {
        return new FuzzyDate(FuzzyDateInteger.fromQuarter(year, quarter));
    }

    public static FuzzyDate fromMonth(int year, int month) {
        return new FuzzyDate(FuzzyDateInteger.fromMonth(year, month));
    }

    public static FuzzyDate fromDay(int year, int month, int day) {
        return new FuzzyDate(FuzzyDateInteger.fromDay(year, month, day));
    }

    public static FuzzyDate fromLocalDate(LocalDate localDate) {
        return new FuzzyDate(FuzzyDateInteger.fromDay(localDate.getYear(), localDate.getMonthValue(), localDate.getDayOfMonth()));
    }

    public static FuzzyDate fromTemporal(TemporalAccessor temporalAccessor) {
        int year = temporalAccessor.get(ChronoField.YEAR);
        try {
            int month = temporalAccessor.get(ChronoField.MONTH_OF_YEAR);
            try {
                int day = temporalAccessor.get(ChronoField.DAY_OF_MONTH);
                return fromDay(year, month, day);
            } catch (Exception e) {
                return fromMonth(year, month);
            }
        } catch (Exception e) {
            return fromYear(year);
        }
    }

    public static int compare(FuzzyDate date1, FuzzyDate date2) {
        if (date1 != null) {
            if (date2 == null) {
                return 1;
            } else {
                return date1.compareTo(date2);
            }
        } else if (date2 != null) {
            return -1;
        } else {
            return 0;
        }
    }

    public static FuzzyDate max(FuzzyDate date1, FuzzyDate date2) {
        int comp = compare(date1, date2);
        if (comp < 0) {
            return date2;
        } else {
            return date1;
        }
    }

    public static FuzzyDate min(FuzzyDate date1, FuzzyDate date2) {
        int comp = compare(date1, date2);
        if (comp > 0) {
            return date2;
        } else {
            return date1;
        }
    }

    public static FuzzyDate current() {
        return new FuzzyDate(FuzzyDateInteger.current());
    }

    public static String checkDateType(String dateType) {
        switch (dateType) {
            case FuzzyDate.DAY_TYPE:
            case "jour":
                return FuzzyDate.DAY_TYPE;
            case FuzzyDate.MONTH_TYPE:
            case "mois":
                return FuzzyDate.MONTH_TYPE;
            case FuzzyDate.QUARTER_TYPE:
            case "trimestre":
                return FuzzyDate.QUARTER_TYPE;
            case FuzzyDate.HALFYEAR_TYPE:
            case "semestre":
                return FuzzyDate.HALFYEAR_TYPE;
            case FuzzyDate.YEAR_TYPE:
            case "annee":
                return FuzzyDate.YEAR_TYPE;
            default:
                throw new IllegalArgumentException("unknown type = " + dateType);
        }
    }


}
