#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include "socket_unix_io.h"
#include "errors.h"
#include "largeint.h"

// Copyright vennerable consulting, 1996.
// All rights reserved.
// Permission to use this code is granted as long as the copyright
// stays in the code.
// It is otherwise subject to the gnu public license.
// Jason Venner
// http://www.vennerable.com
//

main( int argc, char** argv )
{

  char *host = "anami";
  char *port = "time";
  SYSTEMTIME	unix1970s;
  FILETIME	unix1970f;
  FILETIME	unixnowf;
  SYSTEMTIME	unixnows;
  DWORDLONG	ticks1970;
  DWORDLONG	ticksnow;
  int	linuxTime = 1;
  int c;
  extern int optind;
  extern char* optarg;
  

  while( (c = getopt( argc, argv, "blp:?" )) != EOF ) {
    switch( c ) {
    case 'b':                   // bsd time
      linuxTime = 0;
      break;
    case 'l':                   // linux time
      linuxTime = 1;
      break;
    case 'p':
      port = optarg;
      break;
    default:
      fprintf( stderr, "%s:unknown argument '%c'\n", argv[0], c );
      fprintf( stderr, "Usage:%s flags [host (%s)]\n", argv[0], host );
      fprintf( stderr, "\t-b bsd time server Jan1 1970%s\n",
              linuxTime ? "" : " set" );
      fprintf( stderr, "\t-l linux time server Jan 1 1900%s\n",
              linuxTime ? " set" : "" );
      return 1;
    }
  }
  if( optind < argc ) {
    host = argv[optind];
  }

  unix1970s.wYear = 1970;
  unix1970s.wMonth = 1;
  unix1970s.wDayOfWeek = 4;      // Thursday
  unix1970s.wDay = 1;
  unix1970s.wHour = 0;
  unix1970s.wMinute = 0;
  unix1970s.wSecond = 0;
  unix1970s.wMilliseconds = 0;
  
  if( !SystemTimeToFileTime( &unix1970s, &unix1970f ) ) {
    DWORD last_error = GetLastError();
    fprintf( stderr, "%s:unable to get correct base time %ld %s\n",
            (long) last_error, ShortError( last_error ) );
    return 1;
  }

#if 0
  if( !FileTimeToSystemTime( &unix1970f, &unix1970s ) ) {
    DWORD last_error = GetLastError();
    fprintf( stderr, "%s:unable to get reverse correct base time %ld %s\n",
            (long) last_error, ShortError( last_error ) );
    return 1;
  }
  printf( "Time would be %04d:%02d:%02d %d %02d/%02d/%02d.%04d\n",
         unix1970s.wYear,
         unix1970s.wMonth,
         unix1970s.wDay,
         unix1970s.wDayOfWeek,
         unix1970s.wHour,
         unix1970s.wMinute,
         unix1970s.wSecond,
         unix1970s.wMilliseconds
         );


  fprintf( stderr, "1970 %u - %u\n",
          unix1970f.dwHighDateTime,
          unix1970f.dwLowDateTime );
#endif
  ticks1970 = unix1970f.dwHighDateTime;
  ticks1970 <<= 32;
  ticks1970 += unix1970f.dwLowDateTime;

#if 0
  unix1970f.dwHighDateTime = (DWORD) ((DWORDLONG) ticks1970 >> 32);
  unix1970f.dwLowDateTime = (DWORD) ticks1970;

  fprintf( stderr, "1970 %u - %u\n",
          unix1970f.dwHighDateTime,
          unix1970f.dwLowDateTime );
#endif

  Servicename service( port );
  Servicename *pService = &service;
  if( !pService->Ok() ) {
    fprintf( stderr, "Unable to lookup service %s", port );
    perror( " " );
    return 1;
  }
  Hostname hostname( host );
  Hostname *pHostname = &hostname;
  if( !pHostname->Ok() ) {
    fprintf( stderr, "Unable to lookup host %s", host );
    perror( " " );
    return 1;
  }
  
  HANDLE thread = GetCurrentProcess();
  HANDLE access;
  TOKEN_PRIVILEGES priv;
  TOKEN_PRIVILEGES orig;
  
  memset( &priv, 0, sizeof(priv) );
  priv.PrivilegeCount = 1;
  if( !LookupPrivilegeValue( NULL, SE_SYSTEMTIME_NAME,
                            &priv.Privileges[0].Luid ) ) {
    DWORD last_error = GetLastError();
    fprintf( stderr,
            "%s:unable to lookup priviledge name %s %ld - %s\n",
            argv[0],
            SE_SYSTEMTIME_NAME,
            (long) last_error, ShortError(last_error) );
    return 1;
  }
    
  fprintf( stderr, "Tick\n" );
  priv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

  fprintf( stderr, "Tick1\n" );

  if( !OpenProcessToken( thread, TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY, &access ) ) {
    fprintf( stderr, "Tick1a\n" );
    DWORD last_error = GetLastError();
    fprintf( stderr,
            "%s:unable to open thread tokens for priviledge adjust %ld - %s\n",
            argv[0],
            (long) last_error, ShortError(last_error) );
    fprintf( stderr, "Tick1b\n" );
    fflush( stderr );
    return 1;
  }
  fprintf( stderr, "Tick2\n" );
  DWORD size = sizeof(orig);
  if( !AdjustTokenPrivileges(
                             access, // token handle
                             FALSE,  // DisableAll
                             &priv,  // NewState
                             sizeof(priv), // size of NewState
                             &orig,        // previous state
                             &size         // in/out size of orig
                             ) ) {
    fprintf( stderr, "Tick2a\n" );
  
    DWORD last_error = GetLastError();
    fprintf( stderr,
            "%s:unable to set timeset priviledge adjust %ld - %s\n",
            argv[0],
            (long) last_error, ShortError(last_error) );
    return 1;
  }
  fprintf( stderr, "Tick3\n" );

  ClientSocketUnixIO socket( pHostname, pService );
  ClientSocketUnixIO *pSocket;
  pSocket = &socket;
  if( !pSocket->Ok() ) {
    fprintf( stderr, "Unable to create socket to %s - %s", host, port );
    perror( " " );
    return 1;
  }
  unsigned __int32 secs;
  int count;
  if( (count = pSocket->Read( &secs, 4 )) != 4 ) {
    fprintf( stderr, "Unable to read time information from socket, got %d", count );
    perror( " " );
    return 1;
  }
  secs = ntohl(secs);

  if( linuxTime ) {
    secs -= 2208988800UL;
  }
  ticksnow = secs;
  ticksnow *= 10;               // to micro seconds;
  ticksnow *= 1000;             // to milliseconds;
  ticksnow *= 1000;             // to seconds
  ticksnow += ticks1970;
  
  unixnowf.dwHighDateTime =  (DWORD) ((DWORDLONG) ticksnow >> 32);
  unixnowf.dwLowDateTime = (DWORD) ticksnow;
  

  if( !FileTimeToSystemTime( &unixnowf, &unixnows ) ) {
    DWORD last_error = GetLastError();
    fprintf( stderr, "%s:unable to convert time to native format %ld %s\n",
            (long) last_error, ShortError( last_error ) );
    return 1;
  }
  if( !SetSystemTime(&unixnows) ) {
    DWORD last_error = GetLastError();
    fprintf( stderr, "%s:unable to convert set the system time %ld %s\n",
            (long) last_error, ShortError( last_error ) );
    return 1;
  }

  GetLocalTime(&unixnows);
  
  printf( "Time would be %04d:%02d:%02d %d %02d/%02d/%02d.%04d\n",
         unixnows.wYear,
         unixnows.wMonth,
         unixnows.wDay,
         unixnows.wDayOfWeek,
         unixnows.wHour,
         unixnows.wMinute,
         unixnows.wSecond,
         unixnows.wMilliseconds
         );
  return 0;
}


