//=-- Date.h - This class encapulates Date routines --------------------------=
//
//  This class provides a C++ wrapper around the time.h file.  The functions 
//    provided are mostly date oriented, although some time manipulation is 
//    possible.
//
//=---------------------------------------------------------------------------=
//  This file is copyright (c) 1997-2000 Chris Lattner
//=---------------------------------------------------------------------------=

#include <iostream.h>
#include <memory.h>
#include <malloc.h>
#include <assert.h>

#include "MSString.h"
#include "Date.h"
#include "Serialize.h"

Date::Date() {
  time_t CurrentTime;

  time(&CurrentTime);
  TM = localtime(&CurrentTime);
  TM = CopyTM(TM);
}

Date::Date(int Day, int Month, int Year) {
  time_t CurrentTime;

  time(&CurrentTime);
  TM = localtime(&CurrentTime);
  TM->tm_year = Year;
  TM->tm_mon = Month-1;
  TM->tm_mday = Day; 
  TM = CopyTM(TM);
  RecalcStruct();
}

Date::Date(String S) {
  time_t CurrentTime;
  String Result;

  time(&CurrentTime);
  TM = localtime(&CurrentTime);
  
  S.Tokenize("-/", Result);
  TM->tm_mon = Result.atoi()-1;

  S.Tokenize("-/", Result);
  TM->tm_mday = Result.atoi();

  S.Tokenize("-/", Result);
  TM->tm_year = Result.atoi();

  TM = CopyTM(TM);
}

Date::Date(const Date &D) : Object(D) {
  TM = CopyTM(D.TM);
}

Date::Date(const Date *D) {
  TM = CopyTM(D->TM);
}

Date::Date(const time_t &Time) {
  time_t Temp;

  TM = localtime(&Time);

  if (!TM) {    // If an invalid time is passed in, make it the current time.
    time(&Temp);
    TM = localtime(&Temp);
  }
  TM = CopyTM(TM);
}

Date &Date::operator=(const Date &D) {
  memcpy(TM, D.TM, sizeof(struct tm));
  return *this;
}

int Date::Compare(const Object &date) const {
  if (TM->tm_year < ((Date&)date).TM->tm_year) 
    return -1;
  else if (TM->tm_year > ((Date&)date).TM->tm_year) 
    return 1;
  
  if (TM->tm_mon < ((Date&)date).TM->tm_mon) 
    return -1;
  else if (TM->tm_mon > ((Date&)date).TM->tm_mon) 
    return 1;

  if (TM->tm_mday < ((Date&)date).TM->tm_mday)
    return -1;
  else if (TM->tm_mday > ((Date&)date).TM->tm_mday)
    return 1;

  return 0;
}

int Date::GetMonthNumber(const String &Month) {
  String Mon = Month;

  if (Mon.Length() < 3)
    return -1;          // Codes must be at least three chars long...

  Mon.ToUpper();  // Convert it to uppercase
  Mon.Left(3);

  if (Mon == String("JAN")) 
    return 1;
  else if (Mon == String("FEB")) 
    return 2;
  else if (Mon == String("MAR")) 
    return 3;
  else if (Mon == String("APR")) 
    return 4;
  else if (Mon == String("MAY")) 
    return 5;
  else if (Mon == String("JUN")) 
    return 6;
  else if (Mon == String("JUL")) 
    return 7;
  else if (Mon == String("AUG")) 
    return 8;
  else if (Mon == String("SEP")) 
    return 9;
  else if (Mon == String("OCT")) 
    return 10;
  else if (Mon == String("NOV")) 
    return 11;
  else if (Mon == String("DEC")) 
    return 12;

  return -1;
}

int Date::GetDaysInMonth() const {
  switch (TM->tm_mon) {
  case 1:
    return 28;
  case 0:
  case 2:
  case 4:
  case 6:
  case 7:
  case 9:
  case 11:
    return 31;
  default:
    return 30;
  }
}

void Date::RecalcStruct() {
  time_t temp;
  if (TM->tm_year > 1800)    // Is it a 4 digit date?
    TM->tm_year -= 1900;     // We want years since 1900...

  temp = mktime(TM);
  if (temp == -1) {
    cout << "\nisdst = " << TM->tm_isdst <<
            "\nyear = "  << TM->tm_year << 
            "\nmonth = " << TM->tm_mon <<
            "\nmday  = " << TM->tm_mday <<
            "\nhour  = " << TM->tm_hour << 
            "\nminute= " << TM->tm_min <<
            "\nsecond= " << TM->tm_sec << "\n\n";
           
    assert(temp != -1);
  }
  delete TM;
  TM = localtime(&temp);
  TM = CopyTM(TM);
}

Date Date::operator + (const Date &D) const {
  Date Return(this);
  Return.TM->tm_mon  += D.TM->tm_mon;
  Return.TM->tm_year += D.TM->tm_year;
  Return.TM->tm_mday += D.TM->tm_mday;
  Return.TM->tm_hour += D.TM->tm_hour;
  Return.TM->tm_min  += D.TM->tm_min;
  Return.TM->tm_sec  += D.TM->tm_sec;
  Return.RecalcStruct();
  return Return;
}

Date Date::operator + (const int Days) const {
  Date Return(this);
  Return.TM->tm_mday += Days;
  Return.RecalcStruct();
  return Return;
}

Date Date::Tomorrow() const {
  Date New(*this);
  New.TM->tm_mday++;
  New.RecalcStruct();
  return New;
}

Date Date::Yesterday() const {
  Date New(*this);
  New.TM->tm_mday--;
  New.RecalcStruct();
  return New;
}

Serialize &Date::Save(Serialize &O) const {
  return O << mktime(TM);
}

Serialize &Date::Restore(Serialize &I) {
  time_t T;
  I >> T;

  memcpy(TM, localtime(&T), sizeof(struct tm));
  return I;
}

double Date::ElapsedSeconds(Date &StartTime) {
  return difftime(mktime(TM), mktime(StartTime.TM));
}

ostream &Date::print(ostream &O) const {
  return O << TM->tm_mon+1 << "/" << TM->tm_mday << "/" << TM->tm_year+1900;
}

size_t Date::strftime(String &Dest, const char *Format) const {
  char Buffer[2000];
  assert(Format);
  size_t Size = 2000;

  size_t j = ::strftime(Buffer, Size, Format, TM);
  if (j < Size && j > 0) {
    Dest = Buffer;
    return j;
  }

  char *Buf = 0, *NewBuf;
  while (j == 0 || j == Size) {
    Size += 2000;
    NewBuf = (char*)realloc(Buf, Size);
    if (NewBuf == 0) {
      free(Buf);
      return 0;
    }

    Buf = NewBuf;
    j = ::strftime(Buf, Size, Format, TM);
  }

  Dest = Buf;
  free(Buf);
  return j;
}

struct tm *Date::CopyTM(struct tm *T) const { 
  assert(T);
  struct tm *Return = new struct tm();
  memcpy(Return, T, sizeof(struct tm));
  return Return;
}
