package org.fujion.common;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.Temporal;
import java.util.Date;

/**
 * Wrapper for local date/time and legacy date.
 */
public class DateTimeWrapper implements Comparable<DateTimeWrapper> {

    private final Temporal temporal;

    /**
     * Returns a wrapper for the current date and time.
     *
     * @return Current date and time.
     */
    public static DateTimeWrapper now() {
        return new DateTimeWrapper(LocalDateTime.now());
    }

    /**
     * Returns a wrapper for the current date.
     *
     * @return Current date.
     */
    public static DateTimeWrapper today() {
        return new DateTimeWrapper(LocalDate.now());
    }

    /**
     * Returns a wrapper for the current time.
     *
     * @return The current time.
     */
    public static DateTimeWrapper time() {
        return new DateTimeWrapper(LocalTime.now());
    }

    public static DateTimeWrapper parse(String value) {
        Date date = DateUtil.parseDate(value);
        MiscUtil.assertTrue(date != null, () -> "Unable to parse input '" + value + "'.");
        return new DateTimeWrapper(date);
    }

    public DateTimeWrapper(LocalDate date) {
        this.temporal = validateTemporal(date);
    }

    public DateTimeWrapper(LocalDateTime datetime) {
        this.temporal = validateTemporal(datetime);
    }

    public DateTimeWrapper(LocalTime time) {
        this.temporal = validateTemporal(time);
    }

    public DateTimeWrapper(Date date) {
        this.temporal = validateTemporal(DateUtil.hasTime(date) ? DateUtil.toLocalDateTime(date) : DateUtil.toLocalDate(date));
    }

    public boolean hasDate() {
        return temporal instanceof LocalDate || temporal instanceof LocalDateTime;
    }

    public boolean hasTime() {
        return temporal instanceof LocalDateTime || temporal instanceof LocalTime;
    }

    public boolean hasDateAndTime() {
        return temporal instanceof LocalDateTime;
    }

    public Date getLegacyDate() {
        validateDate();
        return DateUtil.toDate(temporal);
    }

    public LocalDateTime getDateTime() {
        validateDate();
        validateTime();
        return (LocalDateTime) temporal;
    }

    public LocalDate getDate() {
        validateDate();
        return temporal instanceof LocalDateTime ? ((LocalDateTime) temporal).toLocalDate() : (LocalDate) temporal;
    }

    public LocalTime getTime() {
        validateTime();
        return temporal instanceof LocalDateTime ? ((LocalDateTime) temporal).toLocalTime() : (LocalTime) temporal;
    }

    private Temporal validateTemporal(Temporal temporal) {
        MiscUtil.assertTrue(temporal != null, "Date/time value must not be null.");
        return temporal;
    }

    private void validateDate() {
        MiscUtil.assertState(hasDate(), "No date component available.");
    }

    private void validateTime() {
        MiscUtil.assertState(hasTime(), "No time component available.");
    }

    @Override
    public int compareTo(DateTimeWrapper w) {
        Temporal temporal2 = w.temporal;

        if (temporal instanceof LocalTime && temporal2 instanceof LocalTime) {
            return ((LocalTime) temporal).compareTo((LocalTime) temporal2);
        }

        if (temporal instanceof LocalDate && temporal2 instanceof LocalDate) {
            return ((LocalDate) temporal).compareTo((LocalDate) temporal2);
        }

        if (temporal instanceof LocalDateTime && temporal2 instanceof LocalDateTime) {
            return ((LocalDateTime) temporal).compareTo((LocalDateTime) temporal2);
        }

        if (temporal instanceof LocalDateTime && temporal2 instanceof LocalDate) {
            return ((LocalDateTime) temporal).compareTo(((LocalDate) temporal2).atStartOfDay());
        }

        if (temporal instanceof LocalDate && temporal2 instanceof LocalDateTime) {
            return ((LocalDate) temporal).atStartOfDay().compareTo(((LocalDateTime) temporal2));
        }

        throw new IllegalArgumentException("Incompatible date/time components for comparison.");
    }

    public String toISOString() {
        return hasDate() && hasTime() ? DateTimeFormatter.ISO_DATE_TIME.format(temporal) :
                hasDate() ? DateTimeFormatter.ISO_DATE.format(temporal) :
                        hasTime() ? DateTimeFormatter.ISO_TIME.format(temporal) :
                                null;
    }

    @Override
    public String toString() {
        return DateUtil.formatDate(temporal);
    }

}

@@ -373,24 +373,54 @@
  temporal;%0A%0A
+    private final Date date;%0A%0A
     /**%0A    
@@ -837,214 +837,8 @@
  %7D%0A%0A
-    /**%0A     * Returns a wrapper for the current time.%0A     *%0A     * @return The current time.%0A     */%0A    public static DateTimeWrapper time() %7B%0A        return new DateTimeWrapper(LocalTime.now());%0A    %7D%0A%0A
     
@@ -1161,32 +1161,79 @@
 Temporal(date);%0A
+        this.date = DateUtil.toDate(temporal);%0A
     %7D%0A%0A    publi
@@ -1336,101 +1336,48 @@
     
-%7D%0A%0A
     
-public DateTimeWrapper(LocalTime time) %7B%0A        this.temporal = validateT
+this.date = DateUtil.toDate(t
 emporal
-(time
 );%0A 
@@ -1558,243 +1558,27 @@
     
-%7D%0A%0A
     
-public boolean hasDate() %7B%0A        return temporal instanceof LocalDate %7C%7C temporal instanceof LocalDateTime;%0A    %7D%0A%0A    public boolean hasTime() %7B%0A        return temporal instanceof LocalDateTime %7C%7C temporal instanceof LocalTim
+this.date = dat
 e;%0A 
@@ -1609,15 +1609,8 @@
  has
-DateAnd
 Time
@@ -1709,32 +1709,8 @@
 ) %7B%0A
-        validateDate();%0A
     
@@ -1724,33 +1724,12 @@
 urn 
-D
+d
 ate
-Util.toDate(temporal)
 ;%0A  
@@ -1778,32 +1778,8 @@
 ) %7B%0A
-        validateDate();%0A
     
@@ -1883,32 +1883,8 @@
 ) %7B%0A
-        validateDate();%0A
     
@@ -2361,122 +2361,8 @@
  %7D%0A%0A
-    private void validateDate() %7B%0A        MiscUtil.assertState(hasDate(), %22No date component available.%22);%0A    %7D%0A%0A
     
@@ -2577,174 +2577,8 @@
 l;%0A%0A
-        if (temporal instanceof LocalTime && temporal2 instanceof LocalTime) %7B%0A            return ((LocalTime) temporal).compareTo((LocalTime) temporal2);%0A        %7D%0A%0A
     
@@ -3364,21 +3364,16 @@
 ble date
-/time
  compone
@@ -3454,21 +3454,8 @@
 turn
- hasDate() &&
  has
@@ -3517,36 +3517,8 @@
 l) :
-%0A                hasDate() ?
  Dat
@@ -3561,129 +3561,8 @@
 ral)
- :%0A                        hasTime() ? DateTimeFormatter.ISO_TIME.format(temporal) :%0A                                null
 ;%0A  

Remove standalone time component from DateTimeWrapper.