/*=================================================================
   Copyright (C) 2000 2013 BizStation Corp All rights reserved.

   This program 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 2
   of the License, or (at your option) any later version.

   This program 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 this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
   02111-1307, USA.
=================================================================*/
#include "datetime.h"
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <bzs/env/crosscompile.h>
#ifdef LINUX
#include <wchar.h>
#if __clang__
#include <pthread.h>
#endif
#endif

#pragma package(smart_init) // BCB package
#pragma warning(disable : 4996) // VC++ unsafe function

#undef USETLS
#if ((defined(_WIN32) && _MSC_VER) || (__APPLE__ && !defined(__BCPLUSPLUS__)))
#define USETLS
#endif

#ifdef USETLS
extern tls_key g_tlsiID_SC3;
#else
__THREAD _TCHAR __THREAD_BCB g_date[30];
#endif

inline _TCHAR* databuf()
{
#ifdef USETLS
    _TCHAR* p = (_TCHAR*)tls_getspecific(g_tlsiID_SC3);
    if (p == NULL)
    {
        p = (_TCHAR*)new wchar_t[45];
        tls_setspecific(g_tlsiID_SC3, p);
    }
    return p;
#else
    return g_date;
#endif
}

namespace bzs
{
namespace rtl
{

int GetDate(const _TCHAR* NowDate)
{
    int ret;
    ret = _ttol(NowDate + 8);
    return ret;
}

int GetYear(const _TCHAR* NowDate, bool PrevMonth)
{
    int ret;
    ret = _ttol(NowDate);
    if ((PrevMonth) && (GetMonth(NowDate, false) == 1))
        ret--;
    return ret;
}

int GetMonth(const _TCHAR* NowDate, bool PrevMonth)
{
    int ret;
    ret = _ttol(NowDate + 5);
    if (PrevMonth)
        ret--;
    if (ret == 0)
        ret = 12;

    return ret;
}

bool IsUrudosi(int Year)
{
    if ((Year % 4) == 0)
    {
        if ((Year % 400) == 0)
            return true;
        else if ((Year % 100) == 0)
            return false;
        else
            return true;
    }
    return false;
}

bool GetDatefromYearSerialDays(int year, int Days, _TCHAR* date,
                               bool nextYearRight)
{
    // The date of the day counted from the beginning of the year is returned.
    // if orver the year then return false.

    int mm;
    int dd = Days;
    for (mm = 1; mm < 14; mm++)
    {
        switch (mm)
        {
        case 2:
            if (IsUrudosi(year))
                Days -= 29;
            else
                Days -= 28;
            break;
        case 4:
        case 6:
        case 9:
        case 11:
            Days -= 30;
            break;
        default:
            Days -= 31;
        }
        if (Days <= 0)
            break;
        dd = Days;
    }
    bool ret = true;
    if ((dd > 31) || (mm == 13))
    {
        if (nextYearRight)
        {
            year += 1;
            mm = 1;
        }
        else
        { // for old program
            dd = 31;
            mm--;
        }
        ret = false;
    }
    _stprintf(date, _T("%04d/%02d/%02d"), year, mm, dd);
    return ret;
}

//---------------------------------------------------------------------------
int JDate2NumOLD(const _TCHAR* date)
{ // 1900/01/01 = 2415021
    int yy = GetYear(date, false);
    int mm = GetMonth(date, false);
    int dd = GetDate(date);
    int i;

    // The number of leap years
    int n = 2415020;
    for (i = 1900; i < yy; i += 4)
    {
        if (IsUrudosi(i))
            n += 1;
    }
    n += (yy - 1900) * 365;
    for (i = 1; i < mm; i++)
    {
        switch (i)
        {
        case 2:
            if (IsUrudosi(yy))
                n += 29;
            else
                n += 28;
            break;
        case 4:
        case 6:
        case 9:
        case 11:
            n += 30;
            break;
        default:
            n += 31;
        }
    }
    n += dd;
    return n;
}
#ifdef _WIN32
//---------------------------------------------------------------------------
int JDate2Num(const _NTCHAR* date)
{
#ifdef _UNICODE
    wchar_t buf[20];
    MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, date, -1, buf, 20);
#else
    char buf[20];
    wtoa(buf, date, 20);
#endif
    return JDate2Num(buf);
}
#endif
//---------------------------------------------------------------------------
int JDate2Num(const _TCHAR* date)
{ // 1980/01/01 = 2444240
    int yy = GetYear(date, false);
    if (yy < 1980)
        return JDate2NumOLD(date);

    int mm = GetMonth(date, false);
    int dd = GetDate(date);
    int i;

    // The number of leap years
    int n = 2444239;
    for (i = 1980; i < yy; i += 4)
    {
        if (IsUrudosi(i))
            n += 1;
    }
    n += (yy - 1980) * 365;
    for (i = 1; i < mm; i++)
    {
        switch (i)
        {
        case 2:
            if (IsUrudosi(yy))
                n += 29;
            else
                n += 28;
            break;
        case 4:
        case 6:
        case 9:
        case 11:
            n += 30;
            break;
        default:
            n += 31;
        }
    }
    n += dd;
    return n;
}
//---------------------------------------------------------------------------
_TCHAR* JNum2DateOLD(int n)
{ // 1900/01/01 = 2415021
    bool uru = false;
    if (n < 2415021)
        return NULL;

    int yy = 1900;
    n -= 2415020;
    while (1)
    {
        if (uru)
        {
            if (n <= 366)
                break;
        }
        else
        {
            if (n <= 365)
                break;
        }
        if (uru)
            n -= 366;
        else
            n -= 365;
        yy++;
        uru = IsUrudosi(yy);
    }
    GetDatefromYearSerialDays(yy, n, databuf());
    return databuf();
}

//---------------------------------------------------------------------------
const _TCHAR* JNum2Date(int n)
{ // 1980/01/01 = 2444240
    bool uru = true;
    if (n < 2444240)
        return JNum2DateOLD(n);

    int yy = 1980;
    n -= 2444239;
    while (1)
    {
        if (uru)
        {
            if (n <= 366)
                break;
        }
        else
        {
            if (n <= 365)
                break;
        }
        if (uru)
            n -= 366;
        else
            n -= 365;
        yy++;
        uru = IsUrudosi(yy);
    }
    GetDatefromYearSerialDays(yy, n, databuf());
    return databuf();
}

//---------------------------------------------------------------------------
long StrToLongDate(_TCHAR* strdat)
{ // Conver string date to long. ex : 20011231
    _TCHAR buf[9];
    _tcsncpy(buf, strdat, 4);
    _tcsncpy(buf + 4, strdat + 5, 2);
    _tcsncpy(buf + 6, strdat + 8, 2);
    buf[8] = 0x00;
    return _ttol(buf);
}
//---------------------------------------------------------------------------
_TCHAR* LongToStrDate(long ldat, _TCHAR* strdat)
{ // Conver long date to string date�B
    // No check long date is valid.
    _TCHAR buf[11];
    if (ldat == 0)
        _tcscpy(strdat, _T("0000/00/00"));
    else
    {
        _stprintf_s(buf, 11, _T("%08ld"), ldat);

        _tcsncpy(strdat, buf, 4);
        strdat[4] = '/';
        _tcsncpy(strdat + 5, buf + 4, 2);
        strdat[7] = '/';
        _tcsncpy(strdat + 8, buf + 6, 2);
        strdat[10] = 0x00;
    }
    return strdat;
}
//---------------------------------------------------------------------------
_TCHAR* LongToStrTime(long ltime, _TCHAR* strdat)
{ // Conver long time to string time�B
    // No check long time is valid.
    _TCHAR buf[9];
    _stprintf_s(buf, 9, _T("%ld"), ltime);
    _tcsncpy(strdat, buf, 2);
    strdat[2] = ':';
    _tcsncpy(strdat + 3, buf + 2, 2);
    strdat[5] = ':';
    _tcsncpy(strdat + 6, buf + 4, 2);
    strdat[8] = 0x00;
    return strdat;
}
//---------------------------------------------------------------------------
const char* dateTimeStr(char* buf, unsigned int bufsize)
{
    struct tm* date;
    time_t now;
    time(&now);
#ifdef __MINGW32__
    date = localtime(&now);
#else
    struct tm tmp;
    date = &tmp;
    localtime_x(date, &now);
#endif
    sprintf_s(buf, bufsize, "%04d/%02d/%02d %02d:%02d:%02d",
              date->tm_year + 1900, date->tm_mon + 1, date->tm_mday,
              date->tm_hour, date->tm_min, date->tm_sec);
    return buf;
}

} // namespace rtl
} // namespace bzs

#pragma warning(default : 4996)