#include "wxinc.h"
#include <wx/socket.h>

#include "key.h"
#include "utils.h"

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

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

///////////////////////////////
// Gestione chiave Hardlock  //
///////////////////////////////
bool HardlockGarble(unsigned short* data)
{
  HL_CODE(data, 1);
  return true;
}

unsigned short HardlockLogin(int& year)
{
  unsigned short serno = 0xFFFF;
  unsigned char REFKEY[16] = "CAMPOKEY";
  unsigned char VERKEY[16] = "ìpÙˆ¬cê<";
  if (HL_LOGIN(26952, LOCAL_DEVICE, REFKEY, VERKEY) == STATUS_OK)
  { 
    unsigned short eprom[64]; memset(eprom, 0, sizeof(eprom));
    HL_READBL((unsigned char*)eprom);
    unsigned short 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];

    HL_LOGOUT();
  }
  return serno;
}

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

unsigned short EutronLogin(int& year)
{
  unsigned short serno = 0xFFFF;
  
  const char* const login[2] = { "AGA.CAMPO", "25EBAI" };
  for (int i = 0; i < 2; i++)
  {
    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* const chiaro = login[i];
    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;
      short* pointer = (short*)(&eutron_key.data[0]);
      short* number  = (short*)(&eutron_key.data[2]);
      *pointer = 0; // Posizione in cui leggere
      *number = 8;  // Words da leggere
      eutron_key.status = -1;
      smartlink(&eutron_key);
      if (eutron_key.status == ST_OK)
      {
        serno = (unsigned short)atol((const char*)eutron_key.data+4);
        if (i == 0)
        {
          const unsigned short y = *(unsigned short*)(eutron_key.data+12);
          if (y > 2000 && y < 3000)
            year = y;
        }
        else
          year = ThisYear();
        
        eutron_key.net_command = NET_KEY_CLOSE;
        smartlink(&eutron_key);

        break;
      }
    }
  }  
  
  return serno;
}

//////////////////////////////////
// Gestione Server di chiavi    //
//////////////////////////////////

class TBordello
{
  WX_DECLARE_STRING_HASH_MAP(wxSocketClient*, TBrothel);
  TBrothel m_map;

public:
  wxSocketClient& Zoccola(wxIPV4address& ip);
  wxSocketClient* QuellaBuona();
  void Chiudi();
  virtual ~TBordello();
} m_bordello;

wxSocketClient& TBordello::Zoccola(wxIPV4address& ip)
{
  const wxString strIP = ip.IPAddress();
  if (m_map[strIP] == NULL)
  {
    m_map[strIP] = new wxSocketClient;
    m_map[strIP]->SetTimeout(2);
  }
  return *m_map[strIP];
}

wxSocketClient* TBordello::QuellaBuona()
{
  wxSocketClient* good = NULL;
  for(TBrothel::iterator it = m_map.begin(); it != m_map.end(); ++it )
  {
    wxSocketClient* sc = (wxSocketClient*)it->second;
    if (sc != NULL)
    {
      sc->WaitOnConnect(0, 1);
      if (sc->IsConnected())
      {
        good = sc;
        break;
      }
    }
  }
  return good;
}

void TBordello::Chiudi()
{
  for(TBrothel::iterator it = m_map.begin(); it != m_map.end(); ++it )
  {
    wxSocketClient* sc = (wxSocketClient*)it->second;
    if (sc != NULL)
      sc->Close();
  }
}


TBordello::~TBordello()
{
/* Non funziona per oggetti statici
  for(TBrothel::iterator it = m_map.begin(); it != m_map.end(); ++it )
  {
    wxSocketClient* sc = (wxSocketClient*)it->second;
    sc->Destroy();
  }
*/
  m_map.clear();
}

static unsigned short InquireSocketNumber(wxSocketClient& sc, const wxString& strCommand)
{
  sc.Discard();
  sc.Write(strCommand, (wxUint32)strCommand.Len());
  long number[2] = { 0L, 0L };
  sc.Read(number, sizeof(number));
  return (unsigned short)number[1];
}

//metodo per sapere dal server di chiave quanti sono gli utonti connessi;non funziona finchè ai servers non..
//..viene aggiunto il metodo NumberActiveUsers (è qui come segnaposto futuro)
unsigned short InquireActiveUsers(const wxString& strSrvName)
{
  unsigned short nUtonti = 0;
  wxIPV4address ip;
  if (ip.Hostname(strSrvName) && ip.Service("1883")) //riesce a connettersi
  {
    wxSocketClient& sc = m_bordello.Zoccola(ip);
    if (sc.IsConnected() || sc.Connect(ip))  //il computer remoto risponde 
    {
      // quanti utonti sono appesi?
      nUtonti = InquireSocketNumber(sc, "NumberActiveUsers()");
    }
  }
  return nUtonti;
}

static unsigned short InquireServer(const wxString& strSrvName, int& year, const bool bMsg)
{
  unsigned short serno = 0xFFFF;
  year = 0;

  wxIPV4address ip;
  if (!ip.Hostname(strSrvName) || !ip.Service("1883")) //indirizzo NON valido o porta scazzata!
  {
    if (bMsg)
      ErrorBox("Indirizzo IP errato!");
    return serno;
  }

  wxSocketClient& sc = m_bordello.Zoccola(ip);
  if (sc.IsConnected() || sc.Connect(ip))  //il computer remoto risponde 
  {
    // ma il server e' davvero acceso o spento ?
    serno = InquireSocketNumber(sc, "DongleNumber()");
    year = InquireSocketNumber(sc, "DongleYear()");
  }
  else
  {
    if (bMsg)
    {
      wxString strMsg;
      strMsg << "Il server " << strSrvName << " non e' raggiungibile!";
      ErrorBox(strMsg);
    }
  }

  return serno;
}

unsigned short ServerLogin(int& year, wxString& strSrvName)
{
  unsigned short serno = 0xFFFF;

  //se non specificato un nome/indirizzo di server, lo cerca tra i miei "cpu fratelli" in rete 
  if (strSrvName.IsEmpty())
  {
    //intanto deve trovare il suo IP di rete
    wxIPV4address ipLocal;
    ipLocal.AnyAddress();
    wxString strMyName = ipLocal.Hostname();
    ipLocal.Hostname(strMyName);
    const wxString strMyIP = ipLocal.IPAddress();

    if (strMyIP.Len() > 7)
    {
      const unsigned short MaxSck = 256;
      for (int i = 0; i < MaxSck; i++)
      {
        //deve sostituire l'ultima cifra dell'IP con tutti i numeri che vanno da 0 a 255 alla ricerca del server
        wxString strSrvIP = strMyIP.BeforeLast('.');
        strSrvIP << "." << i; //ip del computer remoto
        if (strSrvIP != strMyIP)
        {
          wxIPV4address ipRemote;
          if (ipRemote.Hostname(strSrvIP) && ipRemote.Service("1883"))
          {
            wxSocketClient& sc = m_bordello.Zoccola(ipRemote);
            if (!sc.IsConnected())
              sc.Connect(ipRemote, ipLocal, false);
          }
        }
      }
      
      wxSleep(2);
      wxSocketClient* sc = m_bordello.QuellaBuona();
      if (sc != NULL)
      {
        wxIPV4address ipRemote;
        sc->GetPeer(ipRemote);
        strSrvName = ipRemote.Hostname();
      }
    } //if(strMyIP.Len()...
  } //if(strSrvName.Is...
  
  if (!strSrvName.IsEmpty())
    serno = InquireServer(strSrvName, year, false);
  
  return serno;
}


bool DictionaryLogin(const wxString& strSrvName)
{
  return false;
}