/*
 * Utility program to query and set the current time zone settings
 * in the registry.
 *
 *      tz [-b Bias] [-s Name Bias Date] [-d Name Bias Date]
 *
 */

#include "tz.h"

void
Usage( void )
{
    fprintf( stderr, "usage: TZ [-b Bias] [-s Name Bias Date] [-d Name Bias Date]\n" );
    fprintf( stderr, "Where...\n" );
    fprintf( stderr, "    Default with no parameters is to display current time zone settings.\n" );
    exit( 1 );
}

char KeyValueBuffer[ 4096 ];

char *Order[] = {
    "first",

    "second",

    "third",

    "fourth",

    "last"
};

char *DayOfWeek[] = {
    "Sunday",

    "Monday",

    "Tuesday",

    "Wednesday",

    "Thursday",

    "Friday",

    "Saturday"
};

char *Months[] = {
    "January",

    "February",

    "March",

    "April",

    "May",

    "June",

    "July",

    "August",

    "September",

    "October",

    "November",

    "December"
};

#define NUMBER_DATE_TIME_FIELDS 6

BOOL
ParseTimeZoneInfo(
    IN LPSTR NameIn,
    OUT PWSTR NameOut,
    IN LPSTR BiasIn,
    OUT PLONG BiasOut,
    IN LPSTR StartIn,
    OUT PTIME_FIELDS StartOut
    )
{
    LPSTR s, s1;
    ULONG FieldIndexes[ NUMBER_DATE_TIME_FIELDS  ] = {1, 2, 0, 3, 4, 7};
    //
    // Month/Day/Year HH:MM DayOfWeek
    //

    ULONG CurrentField = 0;
    PCSHORT Fields;
    LPSTR Field;
    ULONG FieldValue;

    MultiByteToWideChar( CP_ACP,
                         0,
                         NameIn,
                         strlen( NameIn ),
                         NameOut,
                         32
                       );
    *BiasOut = atoi( BiasIn );
    s = StartIn;

    RtlZeroMemory( StartOut, sizeof( *StartOut ) );
    Fields = &StartOut->Year;
    while (*s) {
        if (CurrentField >= 7) {
            return( FALSE );
            }

        while (*s == ' ') {
            s++;
            }

        Field = s;
        while (*s) {
            if (CurrentField == (NUMBER_DATE_TIME_FIELDS-1)) {
                }
            else
            if (*s < '0' || *s > '9') {
                break;
                }

            s++;
            }

        if (*s) {
            s++;
            }

        if (CurrentField == (NUMBER_DATE_TIME_FIELDS-1)) {
            if (strlen( Field ) < 3) {
                printf( "TZ: %s invalid day of week length\n", Field );
                return FALSE;
                }

            if (StartOut->Year != 0) {
                printf( "TZ: Year must be zero to specify day of week\n" );
                return FALSE;
                }

            if (!_strnicmp( Field, "SUN", 3 )) {
                FieldValue = 0;
                }
            else
            if (!_strnicmp( Field, "MON", 3 )) {
                FieldValue = 1;
                }
            else
            if (!_strnicmp( Field, "TUE", 3 )) {
                FieldValue = 2;
                }
            else
            if (!_strnicmp( Field, "WED", 3 )) {
                FieldValue = 3;
                }
            else
            if (!_strnicmp( Field, "THU", 3 )) {
                FieldValue = 4;
                }
            else
            if (!_strnicmp( Field, "FRI", 3 )) {
                FieldValue = 5;
                }
            else
            if (!_strnicmp( Field, "SAT", 3 )) {
                FieldValue = 6;
                }
            else {
                printf( "TZ: %s invalid day of week\n", Field );
                return FALSE;
                }
            }
        else {
            FieldValue = atoi( Field );
            }

        Fields[ FieldIndexes[ CurrentField++ ] ] = (CSHORT)FieldValue;
        }

    if (StartOut->Year == 0) {
        if (StartOut->Day > 5) {
            printf( "TZ: Day must be 0 - 5 if year is zero.\n" );
            return FALSE;
            }
        }
    else
    if (StartOut->Year < 100) {
        StartOut->Year += 1900;
        }

    return TRUE;
}

int
_CRTAPI1 main( argc, argv )
int argc;
char *argv[];
{
    int i, j;
    LPSTR s, s1, s2, s3, AmPm;
    NTSTATUS Status;
    RTL_TIME_ZONE_INFORMATION tzi;
    BOOLEAN InfoModified = FALSE;

    ConvertAppToOem( argc, argv );
    if (argc < 1) {
        Usage();
        }

    Status = RtlQueryTimeZoneInformation( &tzi );
    if (!NT_SUCCESS( Status )) {
        fprintf( stderr, "TZ: Unable to query current information.\n" );
        }

    while (--argc) {
        s = *++argv;
        if (*s == '-' || *s == '/') {
            while (*++s) {
                switch( tolower( *s ) ) {
                    case 'b':
                        if (argc) {
                            --argc;
                            tzi.Bias = atoi( *++argv );
                            InfoModified = TRUE;
                            }
                        else {
                            Usage();
                            }
                        break;

                    case 's':
                        if (argc >= 3) {
                            argc -= 3;
                            s1 = *++argv;
                            s2 = *++argv;
                            s3 = *++argv;
                            if (ParseTimeZoneInfo( s1, tzi.StandardName,
                                                   s2, &tzi.StandardBias,
                                                   s3, &tzi.StandardStart
                                                 )
                               ) {
                                InfoModified = TRUE;
                                }
                            else {
                                Usage();
                                }
                            }
                        else {
                            Usage();
                            }
                        break;

                    case 'd':
                        if (argc >= 3) {
                            argc -= 3;
                            s1 = *++argv;
                            s2 = *++argv;
                            s3 = *++argv;
                            if (ParseTimeZoneInfo( s1, tzi.DaylightName,
                                                   s2, &tzi.DaylightBias,
                                                   s3, &tzi.DaylightStart
                                                 )
                               ) {
                                InfoModified = TRUE;
                                }
                            else {
                                Usage();
                                }
                            }
                        else {
                            Usage();
                            }
                        break;

                    default:    Usage();
                    }
                }
            }
        else {
            Usage();
            }
        }

    if (InfoModified) {
        Status = RtlSetTimeZoneInformation( &tzi );
        if (!NT_SUCCESS( Status )) {
            fprintf( stderr, "TZ: Unable to set new information.\n" );
            }
        }

    printf( "Time Zone Information.\n" );
    printf( "    Bias from UTC: %u:%02u\n", tzi.Bias / 60, tzi.Bias % 60 );
    printf( "    Standard time:\n" );
    printf( "        Name: %ws\n", &tzi.StandardName );
    printf( "        Bias: %d:%02d\n", tzi.StandardBias / 60, abs( tzi.StandardBias % 60 ) );

    if (tzi.StandardStart.Month != 0) {
        if (tzi.StandardStart.Hour > 12) {
            AmPm = "pm";
            tzi.StandardStart.Hour -= 12;
            }
        else {
            AmPm = "am";
            }
        printf( "        Start: %02u:%02u%s", tzi.StandardStart.Hour, tzi.StandardStart.Minute, AmPm );

        if (tzi.StandardStart.Year == 0) {
            printf( " %s %s of %s\n",
                    Order[ tzi.StandardStart.Day - 1 ],
                    DayOfWeek[ tzi.StandardStart.Weekday ],
                    Months[ tzi.StandardStart.Month - 1 ]
                  );
            }
        else {
            printf( "%s %02u, %u\n",
                    Months[ tzi.StandardStart.Month - 1 ],
                    tzi.StandardStart.Month, tzi.StandardStart.Day, tzi.StandardStart.Year
                  );
            }
        }

    printf( "    Daylight time:\n" );
    printf( "        Name: %ws\n", &tzi.DaylightName );
    printf( "        Bias: %d:%02d\n", tzi.DaylightBias / 60, abs( tzi.DaylightBias % 60 ) );

    if (tzi.DaylightStart.Month != 0) {
        if (tzi.DaylightStart.Hour > 12) {
            AmPm = "pm";
            tzi.DaylightStart.Hour -= 12;
            }
        else {
            AmPm = "am";
            }
        printf( "        Start: %02u:%02u%s", tzi.DaylightStart.Hour, tzi.DaylightStart.Minute, AmPm );

        if (tzi.DaylightStart.Year == 0) {
            printf( " %s %s of %s\n",
                    Order[ tzi.DaylightStart.Day - 1 ],
                    DayOfWeek[ tzi.DaylightStart.Weekday ],
                    Months[ tzi.DaylightStart.Month - 1 ]
                  );
            }
        else {
            printf( "%s %02u, %u\n",
                    Months[ tzi.DaylightStart.Month - 1 ],
                    tzi.DaylightStart.Month, tzi.DaylightStart.Day, tzi.DaylightStart.Year
                  );
            }
        }

    return( 0 );
}