/* (C) 2003-2007 Willem Jan Hengeveld <itsme@xs4all.nl>
 * Web: http://www.xs4all.nl/~itsme/
 *      http://wiki.xda-developers.com/
 *
 * $Id$
 *
 * to start this util automatically each time a device is cradled:
 *
 * [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows CE Services\AutoStartOnConnect]
 * "psynctime"="c:\\path-to\\psynctime.exe"
 * 
 * todo: better algorithm:
 * pct0  pc -> [pct0]
 *                     -> dev   dvt0
 *             [dvt0]  <- dev
 * pct1  pc <-
 *       pc -> [pct1]
 *                     -> dev   dvt1
 *   {pct0,dvt0,pct1}       {pct0,dvt0,pct1,dvt1}
 *
 * determines when the systemtime is updated
 */
#include <util/wintypes.h>
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <util/rapitypes.h>

#include "debug.h"
#include "args.h"

#include "itsutils.h"
#include "dllversion.h"

bool ITSetSystemTime(bool bSetLocal, bool bSetSystem, bool bSetTz, SYSTEMTIME *time, TIME_ZONE_INFORMATION *tzinfo);
bool ITGetSystemTime(bool bGetLocal, SYSTEMTIME *time, TIME_ZONE_INFORMATION *tzinfo, DWORD *ptzid);

int64_t calcftdiff(const FILETIME& t0, const FILETIME& t1)
{
    return (*(uint64_t*)&t0) - (*(uint64_t*)&t1);
}
void ftadd(FILETIME& ft, int64_t off)
{
    *(uint64_t*)&ft += off;
}
void writetdiff(int64_t tdiff)
{
    char sign= ' ';
    if (tdiff<0) {
        sign= '-';
        tdiff=-tdiff;
    }
    
    debug("difference: %c%I64d.%07d sec\n", sign, tdiff/10000000LL, (int)(tdiff%10000000LL));
}
void usage()
{
    printf("(C) 2003-2008 Willem jan Hengeveld  itsme@xs4all.nl\n");
    printf("Usage: psynctime [-l | -s] [-t] [-v]\n");
    printf("        -l : set local time\n");
    printf("        -s : set system time\n");
    printf("        -t : set timezone\n");
    printf("        -v : verbose\n");
    printf("        -q : query only\n");
}
int main( int argc, char *argv[])
{
    DebugStdOut();

    bool bAdjustsyst= false;
    bool bSetLocal= false;
    bool bSetSystem= true;
//    bool bUseRapicall= false;
    bool bSetTz= true;
    bool bVerbose= false;
    int nRepeat=5;

    for (int i=1 ; i<argc ; i++)
    {
        if (argv[i][0]=='-') switch(argv[i][1])
        {
            case 'a': bAdjustsyst= true; break;
            case 'l': bSetLocal= true; break;
            case 's': bSetSystem= !bSetSystem; break;
            case 't': bSetTz= !bSetTz; break;
            case 'v': bVerbose= true; break;
            case 'q': bSetLocal= bSetSystem = bSetTz= false; bVerbose= true; break;
            case 'n': HANDLEULOPTION(nRepeat, int); break;
//            case 'r': bUseRapicall= true; break;
            default:
                usage();
                return 1;
        }
        else 
            usage();
    }

    CheckITSDll();

    SYSTEMTIME devtime; memset(&devtime, 0, sizeof(devtime));
    TIME_ZONE_INFORMATION devtzinfo; memset(&devtzinfo, 0, sizeof(devtzinfo));
    DWORD tzid=0;

    if (bVerbose) {
        ITGetSystemTime(bSetLocal, &devtime, &devtzinfo, &tzid);
    }

    SYSTEMTIME systime; memset(&systime, 0, sizeof(systime));
    TIME_ZONE_INFORMATION systzinfo; memset(&systzinfo, 0, sizeof(systzinfo));
    int64_t tdiff=0;
    for (int loop= 0 ; loop < nRepeat ; loop++) {
        GetSystemTime(&systime);
        GetTimeZoneInformation(&systzinfo);
        FILETIME sysft;
        if (!SystemTimeToFileTime(&systime, &sysft))
            error("SystemTimeToFileTime - systime");

        if (bAdjustsyst && (loop==2 || loop==3)) {
            ftadd(sysft, -tdiff);

            FileTimeToSystemTime(&sysft, &systime);
        }
        if (bSetLocal || bSetSystem || bSetTz)
            ITSetSystemTime(bSetLocal, bSetSystem, bSetTz, &systime, &systzinfo);

        ITGetSystemTime(bSetLocal, &devtime, &devtzinfo, &tzid);

        FILETIME devft;
        if (!SystemTimeToFileTime(&devtime, &devft))
            error("SystemTimeToFileTime - devtime");

        tdiff= calcftdiff(sysft, devft);
        if (bVerbose) {
            writetdiff(tdiff);
        }
    }

    StopItsutils();

    if (bVerbose) {
        debug("pc  timezone: %8d %ls\n", systzinfo.Bias, 
                systzinfo.StandardBias==systzinfo.Bias?systzinfo.StandardName
                :systzinfo.DaylightBias==systzinfo.Bias?systzinfo.DaylightName:L"unknown");
        debug("pc  systime: %04d-%02d-%02d(%d) %02d:%02d:%02d.%03d\n",
            systime.wYear,
            systime.wMonth,
            systime.wDay,
            systime.wDayOfWeek,
            systime.wHour,
            systime.wMinute,
            systime.wSecond,
            systime.wMilliseconds);

        debug("dev timezone: %d - %8d %ls\n", tzid, devtzinfo.Bias, 
                devtzinfo.StandardBias==devtzinfo.Bias?devtzinfo.StandardName
                :devtzinfo.DaylightBias==devtzinfo.Bias?devtzinfo.DaylightName:L"unknown");
        debug("dev systime: %04d-%02d-%02d(%d) %02d:%02d:%02d.%03d\n",
            devtime.wYear,
            devtime.wMonth,
            devtime.wDay,
            devtime.wDayOfWeek,
            devtime.wHour,
            devtime.wMinute,
            devtime.wSecond,
            devtime.wMilliseconds);

        FILETIME sysft, devft;
        if (!SystemTimeToFileTime(&systime, &sysft))
            error("SystemTimeToFileTime - systime");
        if (!SystemTimeToFileTime(&devtime, &devft))
            error("SystemTimeToFileTime - devtime");


        tdiff= calcftdiff(sysft, devft);
        writetdiff(tdiff);
    }

    return 0;
}

bool ITSetSystemTime(bool bSetLocal, bool bSetSystem, bool bSetTz, SYSTEMTIME *time, TIME_ZONE_INFORMATION *tzinfo)
{
    DWORD insize= sizeof(SetSystemTimeParams);
    SetSystemTimeParams inbuf;
    DWORD outsize=0;

    inbuf.bSetLocal= bSetLocal;
    inbuf.bSetSystem= bSetSystem;
    inbuf.bSetTz= bSetTz;
    memcpy(&inbuf.time, time, sizeof(SYSTEMTIME));
    memcpy(&inbuf.tzinfo, tzinfo, sizeof(TIME_ZONE_INFORMATION));

    HRESULT res= ItsutilsInvoke("ITSetSystemTime",
            insize, (BYTE*)&inbuf,
            &outsize, NULL);

    return res==0;
}

bool ITGetSystemTime(bool bGetLocal, SYSTEMTIME *time, TIME_ZONE_INFORMATION *tzinfo, DWORD *ptzid)
{
    DWORD insize= sizeof(GetSystemTimeParams);
    GetSystemTimeParams inbuf;
    DWORD outsize=0;
    GetSystemTimeResult *outbuf=NULL;

    inbuf.bGetLocal= bGetLocal;

    HRESULT res= ItsutilsInvoke("ITGetSystemTime",
            insize, (BYTE*)&inbuf,
            &outsize, (BYTE**)&outbuf);

    if (outbuf) {
        memcpy(time, &(outbuf->time), sizeof(SYSTEMTIME));
        memcpy(tzinfo, &(outbuf->tzinfo), sizeof(TIME_ZONE_INFORMATION));
        *ptzid= outbuf->tzid;
        RapiFree(outbuf);
    }
    return res==0;
}
