#define WIN32_LEAN_AND_MEAN
#define STRICT
#include <windows.h>
#include <shellapi.h>

#include <fstream.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

extern "C"        
{
#include "../include/hlapi_c.h"
#include "../include/skeylink.h"
}

void ErrorBox(LPCSTR str)
{
  MessageBox(GetFocus(), str, "Installazione", MB_ICONSTOP|MB_OK);
}

void WarningBox(LPCSTR str)
{
  MessageBox(GetFocus(), str, "Installazione", MB_ICONEXCLAMATION|MB_OK);
}

/****************************/
/* Gestione chiave Hardlock */
/****************************/

bool HardlockGarble(unsigned int* data)
{
  HL_CODE(data, 1);
  return TRUE;
}

unsigned int HardlockLogin(int& year)
{
  unsigned int serno = 0xFFFF;
  unsigned char REFKEY[16] = "CAMPOKEY";
  unsigned char VERKEY[16] = "ìpÙˆ¬cê<";
  if (HL_LOGIN(26952, LOCAL_DEVICE, REFKEY, VERKEY) == STATUS_OK)
  { 
    unsigned int eprom[64]; memset(eprom, 0, sizeof(eprom));
    HL_READBL((unsigned char*)eprom);
    unsigned int data[4];
    memcpy(data, eprom, sizeof(data));
    HardlockGarble(data);
    if (data[0] == 0xFAE8)
      serno = data[1];
    else  
      serno = 0;
      
    memcpy(data, &eprom[60], sizeof(data));
    if (HardlockGarble(data))
      year = (int)data[0];
  }
  return serno;
}

/**************************/
/* Gestione chiave Eutron */
/**************************/

void EncodeEutronPassword(char* str)
{
  const char* const key = "QSECOFR-";
  char tmp[16];
  for (int i = 0; str[i]; i++)
    tmp[i] = str[i] + (i < 8 ? key[i] : str[i - 8]);
  tmp[i] = '\0';
  strcpy(str, tmp);
}

unsigned int EutronLogin(int& year)
{
  unsigned int serno = 0xFFFF;
  
  KEY_NET eutron_key;
  memset(&eutron_key, 0, sizeof(KEY_NET));
  eutron_key.net_command = NET_KEY_OPEN;
  eutron_key.command = LOCATING_MODE;
  
  const char* chiaro = "AGA.CAMPO";  
  char cifrato[16]; 
  strcpy(cifrato, chiaro);
  EncodeEutronPassword(cifrato);
  
  memset(eutron_key.label, 0, LABEL_LENGTH);
  strcpy((char*)eutron_key.label, chiaro);  
  memset(eutron_key.password, 0, PASSWORD_LENGTH);
  strcpy((char*)eutron_key.password, cifrato);  

  smartlink(&eutron_key);
  if (eutron_key.status == ST_OK)
  {
    eutron_key.net_command = NET_KEY_ACCESS;
    eutron_key.command = BLOCK_READING_MODE;
    int* pointer = (int*)(&eutron_key.data[0]);
    int* number  = (int*)(&eutron_key.data[2]);
    *pointer = 0; // Posizione in cui leggere
    *number = 8;  // Words da leggere
    smartlink(&eutron_key);
    if (eutron_key.status == ST_OK)
    {
      serno = (unsigned int)atol((const char*)eutron_key.data+4);
      const int y = *(int*)(eutron_key.data+12);
      if (y > 2000 && y < 3000)
        year = y;
    }
  }
  
  return serno;
}

/***********************************/
/* Gestione cifratura lista codici */
/***********************************/

static char key[16] = "";

void BuildKey()
{
  for (int i = 0; i < 8; i++)  
    key[i] = 'A'+ rand()%26;
}
  
void DecodeString(char* data)
{                             
  char __tmp_string[256];
  BuildKey();
  for (int i = 0; data[i]; i++)
    __tmp_string[i] = data[i] - (i < 8 ? key[i] : __tmp_string[i - 8]);
  __tmp_string[i] = '\0';
  strcpy(data, __tmp_string); 
}

int ThisYear()
{  
  int anno = 2001;
  time_t lt;
  if (time(&lt) == 0) 
  {
    struct tm* timeloc = localtime(&lt) ;
    if (timeloc != NULL)
      anno = timeloc->tm_year + 1900;
  }
  return anno;
}

int VersionYear()
{                                                             
  char ver[32];
  GetPrivateProfileString("ba", "Versione", "", ver, sizeof(ver), "./program/zip/install.ini");
  ver[4] = '\0';
  return atoi(ver);
}

void ProgramName(char* name)
{
  char ver[80];
  GetPrivateProfileString("Main", "Producer", "", ver, sizeof(ver), "./program/zip/install.ini");

  if (ver[0] != '\0')
  {
    const char * const encryption_key = "QSECOFR-";
    for (int i = 0; ver[i]; i++)
      name[i] = ver[i] - (i < 8 ? encryption_key[i] : name[i - 8]);
    name[i] = '\0';
  }
  else
    strcpy(name, "Campo");
}

bool DongleTest()
{ 
  int yearKey = 0;
  
  unsigned int serno = HardlockLogin(yearKey);
  if (serno == 0xFFFF)
    serno = EutronLogin(yearKey);   
    
  if (serno == 0 || serno == 0xFFFF)
    return TRUE;          // Chiave inesistente o invisibile = Prima installazione
  
  const int verYear = VersionYear();
  if (yearKey >= verYear) // Chiave già programmata con assistenza pagata
    return TRUE;
  
  bool ok = FALSE;
  ifstream keys("./program/zip/dninst.zip", ios::in | ios::nocreate);
  if (keys.good())
  {
    char line[256];
    keys.getline(line, sizeof(line));
    srand(883);
    DecodeString(line);
    const int ass_year = atoi(line);
    srand(ass_year);
    while (!keys.eof())
    {
      keys.getline(line, sizeof(line));
      DecodeString(line);
      unsigned int sn = (unsigned int)atol(line);
      if (sn == serno || line[0] == '*')
      {
        ok = TRUE;
        break;
      }
    }
    if (ok)
    {
      wsprintf(line, "Il contratto di manutenzione %d verrà attivato automaticamente", ass_year);
      WarningBox(line);
    }
    else  
    {
      wsprintf(line, "È necessario contattare l'assistenza tecnica A.G.A.\n"
                     "per l'abilitazione del contratto di manutenzione %d", ass_year);
      WarningBox(line);
    }
  } 
  else
  {
    ErrorBox("Impossibile verificare il contratto di manutenzione");
  }
  return ok;
}

unsigned int Run(LPCSTR app)
{
  unsigned int ret = WinExec(app, SW_SHOWNORMAL);
  if (ret <= 32)
  {
    char s[128];
    wsprintf(s,"Impossibile eseguire\n%s\nErrore %d", app, (int)ret);
    ErrorBox(s);
  }
  return ret;
}

/* 
 * MainWndProc 
 * 
 * Purpose: 
 *  Window procedure for main window. The main window is a dialog. 
 * 
 */ 
LRESULT CALLBACK MainWndProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) 
{ 
   switch (msg) 
   {                
    case WM_ERASEBKGND:
      {
        /* Repaint lo sfondo quando necessario*/
        HDC hdc = (HDC) wParam;
        HPEN hpenOld = (HPEN)SelectObject(hdc, GetStockObject(NULL_PEN));
        RECT rect; GetClientRect(hwnd,&rect); 

        COLORREF cStart = GetSysColor(COLOR_ACTIVECAPTION);
        COLORREF cStop = GetSysColor(27 /*COLOR_GRADIENTACTIVECAPTION*/);
        int deltaR = GetRValue(cStop) - GetRValue(cStart);
        int deltaG = GetGValue(cStop) - GetGValue(cStart);
        int deltaB = GetBValue(cStop) - GetBValue(cStart);

        const int ysize=rect.bottom-rect.top;
        const int step = 4;
        for (int y=rect.bottom; y>=rect.top-step; y-=step)
        {
          int r = GetRValue(cStart) + deltaR * y / rect.bottom;
          int g = GetGValue(cStart) + deltaG * y / rect.bottom;
          int b = GetBValue(cStart) + deltaB * y / rect.bottom;
          HBRUSH hbrush = CreateSolidBrush(RGB(r,g,b));
          HBRUSH hbrushOld = (HBRUSH)SelectObject(hdc, hbrush);
          Rectangle(hdc, rect.left, y, rect.right, y+step+1);
          SelectObject(hdc, hbrushOld);
          DeleteObject(hbrush);      
        }
        SelectObject(hdc, hpenOld);
      }
      break;
    case WM_PAINT:
      {
        PAINTSTRUCT ps;
        RECT rct;
        const char* s1 = "Installa la versione commerciale protetta. E' necessario disporre di una chiave di protezione hardware.";
        const char* s2 = "Installa la versione Demo. La versione Demo è priva di protezione hardware ma soggetta ad alcune limitazioni.";
        const char* s4 = "Termina installazione";
        const char* s5 = "Scelta installazione";
        
        HDC hdc = BeginPaint(hwnd, &ps);
        
        GetWindowRect(GetDesktopWindow(),&rct); 
        SetBkMode(hdc, TRANSPARENT);
        
        const int offx=(rct.right - rct.left-640)/2;
        const int offy=(rct.bottom - rct.top-260)/2;
        rct.left   = offx+300;
        rct.top    = offy;
        rct.right  = rct.left+350;
        rct.bottom = offy+60;

        SetTextColor(hdc, GetSysColor(COLOR_CAPTIONTEXT));
        DrawText(hdc, s1, strlen(s1), &rct, DT_LEFT|DT_WORDBREAK);
        rct.top += 80; rct.bottom += 80;
        DrawText(hdc, s2, strlen(s2), &rct, DT_LEFT|DT_WORDBREAK);
        rct.top += 80; rct.bottom += 80;
        DrawText(hdc, s4, strlen(s4), &rct, DT_LEFT|DT_WORDBREAK);
        
        /* Titolo con ombreggiatura */
        HFONT hfont = CreateFont(64, 0, 0, 0, FW_BOLD, TRUE, FALSE, FALSE, ANSI_CHARSET, OUT_TT_PRECIS, CLIP_TT_ALWAYS, PROOF_QUALITY, VARIABLE_PITCH | FF_SWISS, "Arial");
        HFONT hfontOld = (HFONT)SelectObject(hdc, hfont);
        
        SetTextColor(hdc, RGB(0,0,0));
        TextOut(hdc, 20, 20, s5, strlen(s5));

        SetTextColor(hdc, GetSysColor(COLOR_CAPTIONTEXT));
        TextOut(hdc, 16, 16, s5, strlen(s5));
        
        SelectObject(hdc, hfontOld);
        DeleteObject(hfont);
        EndPaint(hwnd, &ps);
      } 
      break;
    case WM_COMMAND:
      {
        if (wParam == 1000) 
        {  
          if (DongleTest())
            Run("program\\disk1\\setup.exe");
        }
        else if (wParam == 1001) 
        {
          Run("demo\\disk1\\setup.exe");
        }
        else if (wParam != 2) 
          return 0;
  
        PostQuitMessage(0);
      }
      break;
    case WM_CHAR:
      if (wParam == VK_ESCAPE)
        PostQuitMessage(0);
      break;
    case WM_DESTROY:
      PostQuitMessage(0);
      break;
    default:
      return DefDlgProc(hwnd, msg, wParam, lParam);
   }

   return NULL;
}


/*
 * InitApplication
 *
 * Purpose:
 *  Registers window class
 *
 * Parameters:
 *  hinst       hInstance of application
 *
 * Return Value:
 *  TRUE if initialization succeeded, FALSE otherwise.
 */
BOOL InitApplication (HINSTANCE hinst)
{ 
   WNDCLASS wc; 
    
   wc.style = CS_DBLCLKS | CS_SAVEBITS | CS_BYTEALIGNWINDOW; 
   wc.lpfnWndProc = MainWndProc; 
   wc.cbClsExtra = 0; 
   wc.cbWndExtra = DLGWINDOWEXTRA; 
   wc.hInstance = hinst; 
   wc.hIcon = LoadIcon(hinst, MAKEINTRESOURCE(103)); 
   wc.hCursor = LoadCursor(NULL, IDC_ARROW); 
   wc.hbrBackground = HBRUSH(COLOR_WINDOW + 1); 
   wc.lpszMenuName = NULL; 
   wc.lpszClassName = "MainWndClass";  
      
   return RegisterClass(&wc);         
} 
 
 
/* 
 * WinMain 
 * 
 * Purpose: 
 *  Main entry point of application.  
 * 
 */ 

int PASCAL WinMain (HINSTANCE hInst, HINSTANCE hinstPrev, LPSTR pCmdLine, int nCmdShow) 
{ 
    MSG msg; 
                     
    if (!hinstPrev) 
       if (!InitApplication(hInst)) // Register window class         
          return FALSE; 

    RECT rect;
    GetWindowRect(GetDesktopWindow(),&rect); 
    HWND hwndDlg=CreateWindow("MainWndClass","Scelta installazione",WS_VISIBLE| WS_CAPTION | WS_SYSMENU | WS_MAXIMIZE,
      rect.left,rect.top,rect.right,rect.bottom,NULL,NULL,hInst,NULL );

    const int offx=(rect.right - rect.left-700)/2;
    const int offy=(rect.bottom - rect.top-260)/2;

    char name[80];
    ProgramName(name);

    char prompt[128];
    strcpy(prompt, name);
    
    /* Disegna i pulsanti */
    CreateWindow("BUTTON",prompt, WS_CHILD | WS_VISIBLE,
                 offx, offy, 300, 32, hwndDlg , (HMENU)1000, hInst, NULL );


    strcat(prompt, " DEMO");
    CreateWindow("BUTTON", prompt, WS_CHILD | WS_VISIBLE,
                 offx, offy+80, 300, 32, hwndDlg , (HMENU)1001, hInst, NULL );


    CreateWindow("BUTTON","Uscita",
      WS_CHILD| WS_VISIBLE,offx,offy+160,300,32, hwndDlg ,(HMENU)2,hInst,NULL );
    
    while (GetMessage(&msg, NULL, 0, 0))
    { 
       TranslateMessage(&msg);
       DispatchMessage(&msg);
    }
    
    return (msg.wParam);
}