package basics.time;

import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Locale;
import java.util.TimeZone;
import basics.testing.Units;

/**
 * @author kst Represents Date and Time in german 24h format as a string: 2004-04-12
 * 13:00:59 The empty Constructor is very special: it created an 'invalid' date with the current
 * time. This is intended to be used with the db classes: An invalid date can be used for
 * comparision but it is not stored (actually an empty string is stored). When a valid BDateTime
 * object of the current time is needed, use Chrono gt = Chrono.now();
 */
public class Chrono
{
@SuppressWarnings("unused")
public static void main(String [] args)
{
   Chrono now = Chrono.now();
   Chrono tomorrow = Chrono.now();
   tomorrow.addDays(1);
   // Toolbox.debug("Now is younger as tomorrow ? "��
   // + now.isYoungerThan(tomorrow));
   // Toolbox.debug("Now is older as tomorrow ? "
   // + now.isOlderThan(tomorrow));
   // Toolbox.debug("Tomorrow is younger as now ? "
   // + tomorrow.isYoungerThan(now));
   // Toolbox.debug("Tomorrow is older as now ? "
   // + tomorrow.isOlderThan(now));
}

Calendar calendar = null;
boolean  invalid  = true;

public Object clone()
{
   return new Chrono(toString());
}

/**
 * MUSS IMMER GEPR�FT WERDEN, BEVOR EIN DATUMSVERGLEICH VORGENOMMEN WIRD!
 */
public boolean isInvalid()
{
   return invalid;
}

public Chrono(String t)
{
   t = t == null ? "" : t.trim();
   Timestamp ts = null;
   try
   {
      if(t.length() > 0)
      {
         Chrono c = Chrono.now();
         if(t.length() <= 10)
         {
            if(t.length() == 4)
            {
               t = t + "-" + c.getMonth() + "-" + c.getDay();
            }
            else
               if(t.length() == 7)
               {
                  t = t + "-" + c.getDay();
               }
               else
               {
                  t = t + " 00:00:00";
               }
         }
         else
            if(t.length() >= 14 && t.length() <= 16)
            {
               t = t + ":00";
            }
      }
      ts = Timestamp.valueOf(t.trim());
      calendar = Calendar.getInstance();
      //      calendar = Calendar.getInstance(TimeZone.getTimeZone("GMT"), Locale.GERMANY);
      calendar.setTimeInMillis(ts.getTime());
      invalid = false;
   }
   catch(Exception e)
   {
      invalid = true;
   }
}

public Chrono(Timestamp ts)
{
   //   calendar = Calendar.getInstance(TimeZone.getTimeZone("GMT"), Locale.GERMANY);
   calendar = Calendar.getInstance();
   calendar.setTimeInMillis(ts.getTime());
   invalid = false;
}

public Chrono()
{
   //   calendar = Calendar.getInstance(TimeZone.getTimeZone("GMT"), Locale.GERMANY);
   calendar = Calendar.getInstance();
}

/**
 * Precision: seconds
 */
public static Chrono now()
{
   Chrono bdt = new Chrono();
   bdt.invalid = false;
   return bdt;
}

public static Chrono now(boolean zeroSeconds)
{
   Chrono bdt = new Chrono();
   if(zeroSeconds)
   {
      bdt.calendar.set(Calendar.SECOND, 0);
   }
   bdt.invalid = false;
   return bdt;
}

public Timestamp toTimestamp()
{
   Timestamp ts = null;
   if(!invalid)
   {
      ts = Timestamp.valueOf(toString());
   }
   return ts;
}

public String toString()
{
   return toString(false, false);
}

public String toString(boolean noTime, boolean noSeconds)
{
   if(invalid)
   {
      return "";
   }
   else
   {
      try
      {
         SimpleDateFormat sdf = null;
         if(noTime)
         {
            sdf = new SimpleDateFormat("yyyy-MM-dd");
         }
         else
         {
            sdf = new SimpleDateFormat(noSeconds ? "yyyy-MM-dd HH:mm" : "yyyy-MM-dd HH:mm:ss");
         }
         return sdf.format(calendar.getTime());
      }
      catch(Exception e)
      {
         return "";
      }
   }
}

public String toString(String format)
{
   if(invalid)
   {
      return "";
   }
   else
   {
      try
      {
         SimpleDateFormat sdf = new SimpleDateFormat(format);
         return sdf.format(calendar.getTime());
      }
      catch(Exception e)
      {
         return "";
      }
   }
}

/**
 * check if this date is in not more than x days
 */
public boolean isInMaxDays(int days)
{
   boolean result = false;
   days = (days < 0 ? days * -1 : days) - 1;
   //   Calendar now = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
   Calendar now = Calendar.getInstance();
   now.add(Calendar.DATE, days);
   result = calendar.after(now);
   return result;
}

/**
 * This method determines whether this date is the given days older than the given date.
 * @param date the date this object should be compared to
 * @param days the days this object should be older than the given date
 * @return if this date is the given days older than the given date
 */
public boolean isMaximumDaysOlder(Chrono date, int days)
{
   //   Calendar cal1 = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
   //   Calendar cal2 = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
   Calendar cal1 = Calendar.getInstance();
   Calendar cal2 = Calendar.getInstance();
   cal1.set(getYear(), getMonth(), getDay());
   cal2.set(date.getYear(), date.getMonth(), date.getDay());
   long oneDayInMillis = 1 * 24 * 60 * 60 * 1000;
   long xDaysInMillis = oneDayInMillis * (days + 1);
   long deltaDaysInMillis = cal1.getTimeInMillis() - cal2.getTimeInMillis();
   return xDaysInMillis >= deltaDaysInMillis;
}

public boolean isDaysYounger(Chrono date, int days)
{
   return date.isMaximumDaysOlder(this, days);
}

public boolean isSameDay(Chrono date)
{
   return isMaximumDaysOlder(date, 0);
}

public int getYear()
{
   return calendar.get(Calendar.YEAR);
}

public int getMonth()
{
   return calendar.get(Calendar.MONTH) + 1;
}

public int getDay()
{
   return calendar.get(Calendar.DAY_OF_MONTH);
}

public int getHour()
{
   return calendar.get(Calendar.HOUR_OF_DAY);
}

public int getMinute()
{
   return calendar.get(Calendar.MINUTE);
}

public int getSecond()
{
   return calendar.get(Calendar.SECOND);
}

/**
 * check if this in not more than x seconds
 */
public boolean isInMaxSeconds(int seconds)
{
   boolean result = false;
   seconds = (seconds < 0 ? seconds * -1 : seconds) - 1;
   //   Calendar now = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
   Calendar now = Calendar.getInstance();
   now.add(Calendar.SECOND, seconds);
   result = calendar.after(now);
   return result;
}

public void addYears(int x)
{
   calendar.add(Calendar.YEAR, x);
}

public void addMonths(int x)
{
   calendar.add(Calendar.MONTH, x);
}

public void addDays(int x)
{
   calendar.add(Calendar.DATE, x);
}

public void addSeconds(int x)
{
   calendar.add(Calendar.SECOND, x);
}

/**
 * check if this date is more than 'days' days old
 */
public boolean isAtLeastDaysOld(int days)
{
   boolean result = false;
   days = days > 0 ? days * -1 : days;
   //   Calendar now = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
   Calendar now = Calendar.getInstance();
   now.add(Calendar.DATE, days);
   result = calendar.before(now);
   return result;
}

/**
 * check if this date is more than 'seconds' seconds old
 */
public boolean isAtLeastSecondsOld(int seconds)
{
   boolean result = false;
   seconds = seconds > 0 ? seconds * -1 : seconds;
   //   Calendar now = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
   Calendar now = Calendar.getInstance();
   now.add(Calendar.SECOND, seconds);
   result = calendar.before(now);
   return result;
}

/**
 * check if this date is not more than 'seconds' seconds old
 */
public boolean isNotMoreThenSecondsOld(int seconds)
{
   boolean result = false;
   seconds = seconds < 0 ? seconds * -1 : seconds;
   //   Calendar now = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
   Calendar now = Calendar.getInstance();
   now.add(Calendar.SECOND, seconds);
   result = !calendar.after(now);
   return result;
}

/**
 * Checks whether this Crono object is older than the given Crono object.
 * @returns true, if condition above is fulfilled
 */
public boolean isOlderThan(Chrono other)
{
   return calendar.before(other.calendar);
}

/**
 * Checks whether this Chrono object is older than the given Chrono object.
 * @returns true, if condition above is fulfilled
 */
public boolean isYoungerThan(Chrono other)
{
   return calendar.after(other.calendar);
}

/**
 * check if this date is in the past
 */
public boolean isPast()
{
   //   Calendar now = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
   Calendar now = Calendar.getInstance();
   return now.after(calendar);
}

/**
 * Precision: seconds
 */
public static String currentTime()
{
   try
   {
      Chrono bdt = new Chrono();
      SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss.SSS");
      return sdf.format(bdt.calendar.getTime());
   }
   catch(Exception e)
   {
      return "";
   }
}

public static void unittest()
{
   Chrono c0 = new Chrono("1900-12-31 23:59:10");
   Chrono c1 = Chrono.now();
   Chrono cn = new Chrono("2345-12-31 23:59:59");
   Chrono ci1 = new Chrono("2000-13-31 23:59:59");
   Chrono ci2 = new Chrono("2000-12-32 23:59:59");
   Chrono ci3 = new Chrono("2000-12-31 24:59:59");
   Units.assertFalse(c0.invalid, "c0 should be valid");
   Units.assertFalse(c1.invalid, "c1 should be valid");
   Units.assertFalse(cn.invalid, "cn should be valid");
   Units.assertFalse(ci1.invalid, "ci1 should be invalid");
   Units.assertFalse(ci2.invalid, "ci2 should be invalid");
   Units.assertFalse(ci3.invalid, "ci3 should be invalid");
   Units.assertTrue(c0.isOlderThan(c1), "c0 < c1");
   Units.assertTrue(c0.isOlderThan(cn), "c0 < cn");
   Units.assertTrue(c1.isOlderThan(cn), "c1 < cn");
   Units.assertTrue(c1.isYoungerThan(c0), "c1 > c0");
   Units.assertTrue(cn.isYoungerThan(c0), "cn > c0");
   Units.assertTrue(cn.isYoungerThan(c1), "cn > c1");
   Units.assertTrue(c0.isPast(), "c0 should be past");
   Units.assertTrue(c1.isNotMoreThenSecondsOld(1), "c1 is not more than 1 sec old");
   Units.assertTrue(c1.isAtLeastSecondsOld(0), "c1 is at least 0 sec old");
   Units.assertTrue(c1.isSameDay(Chrono.now()), "should be same day");
   Chrono x = new Chrono("1980-01-02 23:59:10");
   Chrono z = new Chrono("1980-01-02");
   Units.assertTrue(x.isSameDay(z), "should be same day");
   Chrono y = new Chrono("1980-01-03 23:59:10");
   Units.assertTrue(y.isMaximumDaysOlder(x, 1), "should be next day");
}
}
