//Il sorgente va scritto in notazione ungherese.
//Le variabili seguono la seguente regola: 'acronimo tipo'+'nome variabile cammellata' 
//Es. wxArrayString 'as' + 'AcceptRefuse' -> asAcceptRefuse
#include "wxinc.h"

#ifdef WIN32
#include <shlobj.h>
#endif

#include <wx/protocol/http.h>
#include <wx/snglinst.h>

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

///////////////////////////////////////////////////////////
//  CampoFrame
///////////////////////////////////////////////////////////

class CampoFrame : public wxFrame
{
protected:
  DECLARE_EVENT_TABLE();
  virtual void OnErase(wxEraseEvent& e);
public:
  CampoFrame();
};

BEGIN_EVENT_TABLE(CampoFrame, wxFrame)
  EVT_ERASE_BACKGROUND(CampoFrame::OnErase)
END_EVENT_TABLE()

void CampoFrame::OnErase(wxEraseEvent& e)
{
  //preparazione background
  wxDC& dc = *e.GetDC();
  const wxRect rect = GetClientSize();
  wxColour c0 = wxSystemSettings::GetColour(wxSYS_COLOUR_3DLIGHT);
  wxColour c1 = wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE);
  wxColour c2 = wxSystemSettings::GetColour(wxSYS_COLOUR_3DDKSHADOW);
  
  wxRect rect1 = rect; rect1.SetBottom(rect.GetBottom() / 2);
  dc.GradientFillLinear(rect1, c0, c1, wxSOUTH);

  wxRect rect2 = rect; rect2.SetTop(rect.GetBottom() / 2);
  dc.GradientFillLinear(rect2, c1, c2, wxDOWN);

  int nHeight = rect.GetHeight()/14;
  wxFont* pFont = wxTheFontList->FindOrCreateFont(nHeight, wxFONTFAMILY_SWISS, wxFONTSTYLE_ITALIC, wxFONTWEIGHT_BOLD);
  dc.SetFont(*pFont);
  dc.SetBackgroundMode(wxTRANSPARENT);


  int w, h;
  dc.GetTextExtent(RESELLER, &w, &h);
  while (w > rect.width)
  {
    nHeight = nHeight * rect.width / w;
    pFont = wxTheFontList->FindOrCreateFont(nHeight, wxFONTFAMILY_SWISS, wxFONTSTYLE_ITALIC, wxFONTWEIGHT_BOLD);
    dc.SetFont(*pFont);
    dc.GetTextExtent(RESELLER, &w, &h);
  }

  const int k = nHeight / 16 + 1;

  dc.SetTextForeground(c2);
  dc.DrawText(RESELLER, rect.GetRight()-w-k/2, rect.GetHeight()-h-k/2);
  dc.SetTextForeground(c1);
  dc.DrawText(RESELLER, rect.GetRight()-w-k, rect.GetHeight()-h-k);

  dc.SetTextForeground(c2);
  dc.DrawText(PRODUCT, k, k);
  dc.SetTextForeground(c1);
  dc.DrawText(PRODUCT, k/2, k/2);

  wxFont* pSmallFont = wxTheFontList->FindOrCreateFont(nHeight/6, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_LIGHT);
  dc.SetFont(*pSmallFont);
  const wxString strTime = __TIMESTAMP__;
  dc.GetTextExtent(strTime, &w, &h);
  dc.SetTextForeground(c2);
  dc.DrawText(strTime, rect.GetRight()-w, 0);
 
}

CampoFrame::CampoFrame()
       : wxFrame(NULL, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0)
{
  ShowFullScreen(true);
}

///////////////////////////////////////////////////////////
// CampoSetup
///////////////////////////////////////////////////////////
//classe principale, ovvero applicazione
class CampoSetup : public wxApp
{
  CampoFrame* m_pMainFrame;
  wxLocale m_locale;
  wxString m_strSetupPath;
  bool m_bLurchWasRunning;

  CampoWizard* m_pWizard;

private:
  bool WriteRegistryKey(HKEY hBaseKey, const wxString strKey, const wxString& strValue) const;
  wxString ReadRegistryKey(HKEY hBaseKey, const wxString strKey) const;
  wxString GetFolderPath(unsigned int uPathID) const;

protected:
  DECLARE_EVENT_TABLE()

  //metodi principali dell'applicazione
  void OnTimer(wxTimerEvent& e);
  void DiskUpdate();
  void InternetUpdate();
  void ClientUpdate();
  void NormalSetup();
  bool SystemRunning(const wxString& strAppName, wxString strMsg) const;
  bool LurchRunning() const;
  bool CampoRunning() const;

  //metodi di interfaccia con Windows
  bool CreateIcon(unsigned int csidl, const wxFileName& strExeFile, const wxString& strLinkName) const;
  void AssociateExtension(const wxFileName& strExeFile, const wxString strExt);
  //...per i servers
  bool CreateAutostartMode(const LurchMode iSrvAutostartMode, const wxString& strPath);
  bool StopLurch() const;
  bool StartLurch(const wxString& strPrgLocPath) const;
  bool LurchWasRunning() const { return m_bLurchWasRunning; }
  
  //metodi di interfaccia con il registry di Windows
  wxString ReadRootRegistryKey(const wxString strKey) const;
  wxString ReadLocalMachineRegistryKey(const wxString strKey) const;
  bool WriteRootRegistryKey(const wxString strKey, const wxString& strValue) const;
  bool WriteLocalMachineRegistryKey(const wxString strKey, const wxString& strValue) const;

  //metodi di utility per i vari modi di aggiornamento
  const wxString GetSourceDir(const wxString strDirName) const;
  void EmptyOutDir(const wxString& strDir) const;
  bool UnzipModule(const wxString& strPrgLocPath, const wxString& strSrc, const wxString& strModule) const;
  void CopyDir(const wxString& strSourceDir, const wxString& strDestDir) const;
  bool CopyFilesAndDirs(const wxString& FilesListI, wxString strFileCurr, const bool bIni) const;  
  void UpdateInstallIni(const wxString strSourcePath, const wxString strDestPath, const wxString& strModule) const;
  bool HTTPGet(const wxString& strLocalPath, const wxString& strWebPath) const;

  //metodi di aggiornamento non standard (client, da disco e via web con installazione moduli)
  int ClientUpdateModule(const wxString& strLocalPath, const wxString& strRemotePath, const wxString strModule) const;
  bool DiskUpdateModule(const wxString& strLocalPath, const wxString& strWebPath, const wxString strModule) const;
  bool InternetUpdateModule(const wxString& strLocalPath, const wxString& strWebPath, const wxString strModule) const;

public:
  virtual bool OnInit();
};

IMPLEMENT_APP(CampoSetup)

BEGIN_EVENT_TABLE(CampoSetup, wxApp)
  EVT_TIMER(883, OnTimer)
END_EVENT_TABLE()

//----------------------------------------------------------------
// Metodi di lettura/scrittura registry di Windows (fantastici!)
//----------------------------------------------------------------
wxString CampoSetup::ReadRegistryKey(HKEY hBaseKey, const wxString strKey) const
{
  wxString strValue;
  HKEY hKey = NULL;
  wxString strPath, strName;
  wxFileName::SplitPath(strKey, &strPath, &strName, NULL);

  bool ok = ::RegOpenKeyEx(hBaseKey, strPath, 0, KEY_READ, &hKey) == ERROR_SUCCESS;
  if (ok)
  {
    BYTE buff[512];
    DWORD type = REG_SZ;
    DWORD dw = sizeof(buff);
    ok = ::RegQueryValueEx(hKey, strName, NULL, &type, buff, &dw) == ERROR_SUCCESS;
    if (ok)
      strValue = buff;
    ::RegCloseKey(hKey);
  }
  return strValue;
}

wxString CampoSetup::ReadRootRegistryKey(const wxString strKey) const
{
  return ReadRegistryKey(HKEY_CLASSES_ROOT, strKey);
}

wxString CampoSetup::ReadLocalMachineRegistryKey(const wxString strKey) const
{
  return ReadRegistryKey(HKEY_LOCAL_MACHINE, strKey);
}

bool CampoSetup::WriteRegistryKey(HKEY hBaseKey, const wxString strKey, const wxString& strValue) const
{
  HKEY hKey = NULL;
  DWORD dw = 0;
  //splitta la stringa in path e valore
  wxString strPath, strName;
  wxFileName::SplitPath(strKey, &strPath, &strName, NULL);

  bool ok = ::RegCreateKeyEx(hBaseKey, strPath, 0, REG_NONE,
				     REG_OPTION_NON_VOLATILE, KEY_WRITE|KEY_READ, NULL, &hKey, &dw) == ERROR_SUCCESS;
  if (ok)
  {
    ok = ::RegSetValueEx(hKey, strName, 0, REG_SZ, 
           (BYTE*)(const wxChar*)strValue, DWORD(2*strValue.Len()+2)) == ERROR_SUCCESS;
    ::RegCloseKey(hKey);
  }
  return ok;
}

bool CampoSetup::WriteRootRegistryKey(const wxString strKey, const wxString& strValue) const
{
  return WriteRegistryKey(HKEY_CLASSES_ROOT, strKey, strValue);
}

bool CampoSetup::WriteLocalMachineRegistryKey(const wxString strKey, const wxString& strValue) const
{
  return WriteRegistryKey(HKEY_LOCAL_MACHINE, strKey, strValue);
}

//----------------------------------
// Metodi di interfaccia con Windows
//----------------------------------
wxString CampoSetup::GetFolderPath(unsigned int uPathID) const
{
  TCHAR szPath[MAX_PATH] = wxT("");
	HRESULT hres = ::SHGetFolderPath(NULL, uPathID, NULL, SHGFP_TYPE_CURRENT, szPath);
  return szPath;
}


void CampoSetup::AssociateExtension(const wxFileName& strExeFile, const wxString strExt)
{
  // Register icon and application
  WriteRootRegistryKey(strExt, APPNAME);

  wxString str = strExeFile.GetFullPath(); str += wxT(",0");
  WriteRootRegistryKey(APPNAME + wxT("\\DefaultIcon"), str);

  str = strExeFile.GetFullPath(); str += wxT(" \"%1\"");
  WriteRootRegistryKey(APPNAME + wxT("\\shell\\open\\command"), str);
}

bool CampoSetup::CreateIcon(unsigned int csidl, const wxFileName& strExeFile, const wxString& strLinkName) const
{
  //csidl = CSIDL_COMMON_DESKTOPDIRECTORY = desktop
  //csidl = CSIDL_COMMON_STARTUP = all users esecuzione automatica
  wxString strDesktopPath = GetFolderPath(csidl);

  if (!strDesktopPath.IsEmpty())
  {
    CoInitialize(NULL);

    // Get a pointer to the IShellLink interface. 
	  IShellLink* psl; 
    HRESULT hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (void**)&psl); 
    if (SUCCEEDED(hres)) 
	  {  
      // Set the path to the shortcut target and add the description. 
      psl->SetPath(strExeFile.GetFullPath());
		  psl->SetWorkingDirectory(strExeFile.GetPath());
      psl->SetDescription(APPNAME);

      //sceglie l'icona da quelle possibili legate al .exe in base al tipo di installazione
      const InstallationType uInstallationType = m_pWizard->GetInstallationType();

      //anche l'icona deve essere scelta in base al reseller
      wxString strIcon = Icon();
      //path completo del file icona, in modo da legarlo al link sul desktop;comincia con il path di Campo
      wxString strPath = m_pWizard->GetPrgLocPath();

      if (!strIcon.IsEmpty())
      {
        strPath += "/setup/";   //se esiste l'icona del reseller sta in setup
      }
      else
      {
        strPath += "/res/";   //se non trova l'icona del reseller prende quella standard in res
      }

      const int nPos = strIcon.Len() - 4;
      switch (uInstallationType)
      {
      case it_server: strIcon.insert(nPos, "Srv"); break;
      case it_client: strIcon.insert(nPos, "Cln"); break;
      default: break;
      }

      strPath << strIcon;

      //controlla se � in un sistema windows che richiede icone a 256 colori (win2000, win2003 termserv)
      int nVersion = 0;
      GetWinVer(NULL, 0, &nVersion);
      if (nVersion == W2K || nVersion == W2003)
      {
        const int nPosCol = strIcon.Len() - 4;
        strIcon.insert(nPosCol, "_256");
      }

      psl->SetIconLocation(strPath, 0);

     // Query IShellLink for the IPersistFile interface for saving the 
     // shortcut in persistent storage.
      IPersistFile* ppf;
      hres = psl->QueryInterface(IID_IPersistFile, (void**)&ppf); 

      if (SUCCEEDED(hres)) 
		  { 
        wxFileName fnLnk;
        wxString strName = strLinkName + ".lnk";
        fnLnk.Assign(strDesktopPath, strName);

        // Save the link by calling IPersistFile::Save.
        wxString strPath = fnLnk.GetFullPath();
        wchar_t buff[_MAX_PATH];
        wxConvLibc.ToWChar(buff, _MAX_PATH, strPath);
        hres = ppf->Save(buff, true); 
        ppf->Release(); 
      } 
      psl->Release();
    } 
    CoUninitialize();
  }
  return true;
}


//-------------------------------------------
//  ..per i servers di Campo
//-------------------------------------------
bool CampoSetup::StopLurch() const
{
  bool ok = false;

  //cerca e ferma authoriz e lurch come processi
  for (int i = 0; i < 2; i++)
  {
    wxHTTP scServer;
    if (scServer.Connect("127.0.0.1", i == 0 ? 1883 : 10000))
    {
      //if (scServer.BuildRequest("/stop.cgi", 0))
      wxInputStream* isIs = scServer.GetInputStream("/stop.cgi");
      if (isIs)
      {
        delete isIs;
        wxSleep(3);
        ok = true;
        break;
      }
    }
  }

  //se lurch e' un servizio lo stoppa
  const wxString strValue = ReadLocalMachineRegistryKey("SYSTEM\\CurrentControlSet\\Services\\Lurch\\Parameters\\Application");
  if (!strValue.IsEmpty())
  {
    wxString strExecService = GetFolderPath(CSIDL_SYSTEM);
    strExecService += "\\NET STOP Lurch";
    wxExecute(strExecService, wxEXEC_SYNC);
  }

  return ok;
}

bool CampoSetup::StartLurch(const wxString& strPrgLocPath) const
{
  const wxString strValue = ReadLocalMachineRegistryKey("SYSTEM\\CurrentControlSet\\Services\\Lurch\\Parameters\\Application");
  bool ok = false;
  if (!strValue.IsEmpty())
  {
    wxString strExecService = GetFolderPath(CSIDL_SYSTEM);
    strExecService += "\\NET START Lurch";
    ok = wxExecute(strExecService, wxEXEC_SYNC) > 0;
  }
  else
  {
    wxString strPath = strPrgLocPath;
    strPath += "/servers/lurch.exe";
    ok = wxExecute(strPath, wxEXEC_ASYNC) > 0;
  }

  return ok;
}

bool CampoSetup::CreateAutostartMode(const LurchMode iSrvAutostartMode, const wxString& strPath)
{
  //creazione dei filenames e dei path necessari in giro per la funzione
  //filename (con path quindi) di Lurch.exe
  wxFileName strExe(strPath, wxT("lurch.exe"));
  strExe.AppendDir("servers");
  //stringa path della cartella Servers in base alle scelte dell'utente
  const wxString strSrvPath = strExe.GetPath();

  //1) Cerca di eliminare Lurch se presente come servizio
  const wxString strValue = ReadLocalMachineRegistryKey("SYSTEM\\CurrentControlSet\\Services\\Lurch\\Parameters\\Application");
  if (!strValue.IsEmpty())
  {
    //elimina un eventuale servizio precedente (senno' col cavolo che lo riesce a modificare!)
    //utilizza il programma instsrv.exe dentro la cartella servers installata
    wxString strRemove = strSrvPath + "\\instsrv Lurch REMOVE";
    const long lRemove = wxExecute(strRemove, wxEXEC_SYNC);
  }
  //2) Cerca di fermare Lurch se presente come programma in esecuzione automatica
  else
  {
    if (LurchRunning())
      StopLurch();
  }

  switch (iSrvAutostartMode)
  {
  //esecuzione server come servizi (magia!)
  case lm_service:
    {
      //aggiunge la voce ai servizi di windows (dentro strumenti di amministrazione)
      //utilizza i programmi instrv.exe e srvany.exe dentro la cartella servers installata
      wxString strCreateService = strSrvPath + "\\instsrv Lurch ";
      strCreateService += strSrvPath;
      strCreateService += "\\srvany.exe";
      //esegue il programma di installazione servizi di Windows creando la voce
      const long lExec = wxExecute(strCreateService, wxEXEC_SYNC);
      if (lExec >= 0)
      {
        //crea le voci nel registry di windows
        WriteLocalMachineRegistryKey("SYSTEM\\CurrentControlSet\\Services\\Lurch\\Parameters\\Application", strExe.GetFullPath());
        wxString strExecService = GetFolderPath(CSIDL_SYSTEM);
        strExecService += "\\NET START Lurch";
        //avvia il servizio
        const long lExecLurch = wxExecute(strExecService, wxEXEC_SYNC);
        if (lExecLurch < 0)
          WarningBox("Impossibile eseguire il servizio del gestore applicazioni.\nLanciarlo manualmente dagli strumenti di amministrazione nel pannello di controllo!");
      }
      else
        WarningBox("Impossibile registrare il servizio per l'esecuzione automatica del gestore applicazioni.\nSelezionare una diversa modalita' di esecuzione!");

    }
    break;
  //link nella cartella esecuzione automatica
  case lm_autostart:
    {
      CreateIcon(CSIDL_COMMON_STARTUP, strExe, "lurch");
    }
    break;
  default:
    break;
  }
  return true;
}


//----------------------------------------------------------------
// Metodi di utility per i vari tipi di aggiornamento
//----------------------------------------------------------------
static int PatchCompare(const wxString& first, const wxString& second)
{
  const wxFileName fn1(first);
  const wxFileName fn2(second);
  const wxString strName1 = fn1.GetName().Lower();
  const wxString strName2 = fn2.GetName().Lower();
  if (strName1 == "syinst1")
    return -1;
  if (strName2 == "syinst1")
    return 1;

  return strName1.CompareTo(strName2);
}


const wxString CampoSetup::GetSourceDir(const wxString strDirName) const
{
  wxFileName strSourcePath(m_strSetupPath, "pippo.txt");
  strSourcePath.AppendDir("..");
  strSourcePath.AppendDir("..");
  strSourcePath.AppendDir(strDirName);
  strSourcePath.MakeAbsolute();
  const wxString strSrc = strSourcePath.GetPath();
  return strSrc;
 }

//metodo per aggiornare il modulo SY in install.ini
void CampoSetup::UpdateInstallIni(const wxString strSourcePath, const wxString strDestPath, const wxString& strModule) const
{
  for (int j = 0; j < 2; j++)
  {
    wxString strParagraph = strModule;
    if (j > 0)
      strParagraph << j;
    CampoIniFile InstallIniSource(strSourcePath, strParagraph);
    CampoIniFile InstallIniDest(strDestPath, strParagraph);

    wxString strWrk;
    long nIndex;
    for (bool ok = InstallIniSource.GetFirstEntry(strWrk, nIndex); ok; ok = InstallIniSource.GetNextEntry(strWrk, nIndex))
    {
      InstallIniDest.Set(strWrk, InstallIniSource.Get(strWrk));
    }
  }
}

bool CampoSetup::UnzipModule(const wxString& strPrgLocPath, const wxString& strSrc, const wxString& strModule) const
{
  bool ok = false;
  wxArrayString asFilesList;
  size_t uFilesToCopy;
  //..contando anche quanti sono e memorizzandoli in un array asFileList
  if(wxDir::Exists(strSrc))
  {
    uFilesToCopy = wxDir::GetAllFiles(strSrc, &asFilesList, strModule+"*.zip");
    if (uFilesToCopy > 0)
    {
      //ordina le patch per numero crescente ma mette davanti a tutte il pacco (sysinst1)
      asFilesList.Sort(PatchCompare);
      for (size_t i = 0; i < uFilesToCopy; i++)
      {  
        UnzipFile(asFilesList[i] , strPrgLocPath);
      }

      //aggiorna l'install.ini in base al numero di patch e versione dell'ultimo file zip di sistema
      //ci vuole un giro allucinogeno per trasformare il .zip in .ini e togliere quel cazzo di 1 in fondo
      wxFileName strLastFile(asFilesList[uFilesToCopy - 1]);    //zip con path completo
      wxString strWrk = strLastFile.GetName();
      strWrk.Truncate(strWrk.Len() - 1);
      strLastFile.SetName(strWrk);
      strLastFile.SetExt("ini");
      strWrk = strLastFile.GetFullPath();

      //adesso che ha trovato quello sporco ultimo .ini prende versione e patch e le copia nell'install.ini..
      //..nella dir dei programmi; lo fa paragrafo x paragrafo ([sy],[sy1]..)
      //potrebbe sembrare assurdo un for, ma e' per futuri sottomoduli con numero > 1
      UpdateInstallIni(strWrk, strPrgLocPath + "/install.ini", strModule);
    }
  }

  //Adesso puo' far ripartire il server di chiavi
  const bool bLurch = strModule == "sr" && LurchWasRunning();
  if (bLurch)
    StartLurch(strPrgLocPath);

  return uFilesToCopy > 0;
}

bool CampoSetup::CopyFilesAndDirs(const wxString& FilesListI, wxString strFileCurr, const bool bIni) const
{
  bool ok = true;

  //i files .ini vanno trattati con i guanti (se esistono gia' non vanno copiati!!!)
  //parte da eseguire solo in caso sia indicata la trattazione degli ini (bIni true)
  if (bIni)
  {
    strFileCurr.MakeLower();  //minuscolizzazione di sicurezza
    const wxFileName strFileName(strFileCurr);
    if (strFileName.GetExt() == "ini")
    {
      const wxString strName = strFileName.GetName();
      //campo.ini e install.ini vanno lasciati stare se esistono (aggiornamento)!!!
      if ((strName == "campo" || strName == "install") && strFileName.FileExists())
        ok = false;
    }
  }

  if (ok)
    ok = CopiaFile(FilesListI, strFileCurr);

  return ok;
}

//metodo per copiare una directory e tutti i files che contiene
void CampoSetup::CopyDir(const wxString& strSourceDir, const wxString& strDestDir) const
{
  if (wxDir::Exists(strSourceDir))
  {
    wxArrayString asFilesList;
    const size_t uFilesToCopy = wxDir::GetAllFiles(strSourceDir, &asFilesList, "*.*");
    const size_t uFrom = strSourceDir.Len();
    for (size_t i = 0; i < uFilesToCopy; i++)
    {
      wxString strFileDest = strDestDir + asFilesList[i].Mid(uFrom);
      CopiaFile(asFilesList[i], strFileDest);
    }
  }
}

//metodo per accoppare tutti i files di una directory
void CampoSetup::EmptyOutDir(const wxString& strDir) const
{
  if (wxFileName::DirExists(strDir))
  {
    wxArrayString asFilesList;
    const size_t uFilesToKill = wxDir::GetAllFiles(strDir, &asFilesList, "*.*");
    for (size_t i = 0; i < uFilesToKill; i++)
      ::wxRemoveFile(asFilesList[i]);
  }
}


//-----------------------------------------------------
//  METODI BASSO LIVELLO PER AGGIORNAMENTI NON STANDARD
//-----------------------------------------------------
//----------------------------
//  1) AGGIORNAMENTO CLIENT
//----------------------------
//Aggiornatore client. Copia i files del programma da una directory remota su un server campo nella directory...
//...locale di campo
int CampoSetup::ClientUpdateModule(const wxString& strLocalPath, const wxString& strRemotePath, const wxString strModule) const
{
  int nLocVer, nLocPatch, nRemVer, nRemPatch;
  {
    CampoIniFile iniLocalInstall(strLocalPath + "/install.ini", strModule);
    nLocVer = iniLocalInstall.GetInt("Versione");
    nLocPatch = iniLocalInstall.GetInt("Patch");

    CampoIniFile iniRemoteInstall(strRemotePath + "/install.ini", strModule);
    nRemVer = iniRemoteInstall.GetInt("Versione");
    nRemPatch = iniRemoteInstall.GetInt("Patch");
  }
  int cmp = nLocVer - nRemVer;
  if (cmp == 0)
    cmp = nLocPatch - nRemPatch;

  //il client e' piu' indietro e quindi va aggiornato!
  if (cmp < 0)
  {
    bool bOk = true;

    CampoProgressDialog pi("Aggiornamento elenco files dal server...", 100, m_pWizard); 
    
    wxArrayString asGroups;
    {
      wxString strGroup;
      long nIndex;
      CampoIniFile iniRemoteInstall(strRemotePath + "/install.ini", "");
      for (bool ok = iniRemoteInstall.GetFirstGroup(strGroup, nIndex); ok; ok = iniRemoteInstall.GetNextGroup(strGroup, nIndex))
      {
        if(strGroup.Len() > 2 && strGroup.StartsWith(strModule))
          asGroups.Add(strGroup);
      }
    }

    //controlla tutti i sottomoduli del modulo [sy1],[sy2]...
    for (size_t i = 0; i < asGroups.GetCount() && bOk; i++)
    {
      const wxString& strParagraph = asGroups[i];
      CampoIniFile iniRemoteInstallModule(strRemotePath + "/install.ini", strParagraph);

      //install.ini locale che deve essere aggiornato durante la copia dei files!
      CampoIniFile iniLocalInstall(strLocalPath + "/install.ini", strParagraph);

      //ogni file dell'elenco del sottomodulo corrente va copiato
      for (int j = 0;; j++)
      {
        pi.Pulse(); //magica barra modello supercar!

        const wxString strVarName = wxString::Format("File(%d)", j);
        wxString strCurrFile = iniRemoteInstallModule.Get(strVarName);
        if (strCurrFile.IsEmpty())
           break;

        strCurrFile = strCurrFile.BeforeFirst('|');
   
        const wxString strSrcPath = strRemotePath + "/" + strCurrFile;
        const wxString strDstPath = strLocalPath + "/" + strCurrFile;

        //copia il file remoto di origine sul file locale di destinazione (overwrite=true di default)
        if (!CopiaFile(strSrcPath, strDstPath))
        {
          wxString strError;
          strError += "\nAssicurarsi che il client locale di ";
          strError += APPNAME;
          strError += " non sia in funzione.\n";
          strError += "Assicurarsi che il server di ";
          strError += APPNAME;
          strError += " sia in funzione e raggiungibile in rete.\n";
          strError += "Aggiornamento interrotto!";
          ErrorBox(strError);
          bOk = false;
          break;
        }

        //se ci sono zip deve scompattarli (per ora esiste solo res.zip)
        if (strCurrFile.EndsWith("res.zip"))
        {
          UnzipFile(strDstPath, strLocalPath + "/res");
        }

        //se riesce la copia del file aggiorna l'install.ini sul file (se non lo facesse non comparirebbero...
        //...eventuali nuovi files
        iniLocalInstall.Set(strVarName, strCurrFile);

      } //for(int j...

      //una volta termiata la copia dei files del sottmodulo[i] scatta l'aggiornamento dell'install.ini locale...
      //...al livello versione/patch appena copiate sempre del sottomodulo[i]
      iniLocalInstall.Set("Versione", nRemVer);
      iniLocalInstall.Set("Patch", nRemPatch);

    } //for(int i...

    //una volta termiata la copia dei files dell'intero modulo scatta l'aggiornamento dell'install.ini locale al..
    //..livello versione/patch appena copiate
    CampoIniFile iniLocalInstall(strLocalPath + "/install.ini", strModule);
    iniLocalInstall.Set("Versione", nRemVer);
    iniLocalInstall.Set("Patch", nRemPatch);

  } //if(cmp<0...

  return cmp;
}


//----------------------------
//  2) AGGIORNAMENTO DA DISCO
//----------------------------
//Aggiornatore da disco. Copia i files delle patch da una directory su disco in una directory di appoggio...
//...temporanea dove esegue lo scompattamento con destinazione la directory locale
bool CampoSetup::DiskUpdateModule(const wxString& strLocalPath, const wxString& strDiskPath, const wxString strModule) const
{
  //puo' chiamare direttamente il metodo che scompatta tutte le patch del modulo a partire dal pacco iniziale...
  //...fino all'ultima;il metodo le ordina gia' in modo crescente di numero d'ordine
  return UnzipModule(strLocalPath, strDiskPath, strModule);
}

//-------------------------------
//  3) AGGIORNAMENTO DA INTERNET
//-------------------------------
bool CampoSetup::HTTPGet(const wxString& strLocalPath, const wxString& strWebSite) const
{
  wxString strWebPath = strWebSite;
  if (strWebPath.StartsWith("http://"))
    strWebPath.Remove(0, 7);
  int nSlash = strWebPath.First('/');
  wxString strServer = strWebPath.Left(nSlash);
  wxString strPath = strWebPath.Mid(nSlash);

  wxHTTP http;
  //connessione al server web con le patch
  if (http.Connect(strServer))
  {
    wxInputStream* pStream = http.GetInputStream(strPath);
    if (pStream != NULL)
    {
      //compila un file di testo temporaneo con l'elenco dei files che trova sul server
      wxFileOutputStream fos(strLocalPath);
      fos.Write(*pStream);
      delete pStream;
      return true;
    }
  }
  return false;
}

//Aggiornatore via web. Copia i files delle patch da una directory web remota in una directory di appoggio...
//...temporanea dove esegue lo scompattamento con destinazione la directory locale
bool CampoSetup::InternetUpdateModule(const wxString& strLocalPath, const wxString& strWebPath, const wxString strModule) const
{
  bool ok = false;
  int nLocVer, nLocPatch;
  {
    CampoIniFile iniLocalInstall(strLocalPath + "/install.ini", strModule);
    nLocVer = iniLocalInstall.GetInt("Versione");
    nLocPatch = iniLocalInstall.GetInt("Patch");
  }

  //Svuota,elimina e ricrea una directory temporanea di appoggio ove mettere i files delle patch da scompattare
  //E' necessaria questa operazione per non trovarsi ancora files di vecchie patch parcheggiati nella dir!
  const wxString strTempDir = wxFileName::GetTempDir() + "/setup/";
  if (wxDir::Exists(strTempDir))
  {
    EmptyOutDir(strTempDir);    //deve svuotare la directory dai files per poterla eliminare!!!
    ::wxRmdir(strTempDir);  //elimina la dir adesso vuota
  }
  CheckAndMakeDir(strTempDir, wxEmptyString);   //la ricrea con i permessi dell'utente windows corrente!!

  const wxString strTempFile = strTempDir + "httpdir.txt";
  if (HTTPGet(strTempFile, strWebPath))
  {
    //partendo dal file temporaneo ne compila un secondo con i soli nomi dei file validi da scaricare
    //i criteri di selezione sono il nome del modulo ed il livello patch
    wxTextFile tfFilesList;
    if (tfFilesList.Open((strTempFile)))
    {
      CampoProgressDialog pi("Aggiornamento elenco files dal server...", (int)tfFilesList.GetLineCount(), m_pWizard); 
      for (wxString strWrk = tfFilesList.GetFirstLine(); !tfFilesList.Eof(); strWrk = tfFilesList.GetNextLine())
      {
        int nPos = strWrk.Find("href");
        if (nPos > 0)
        {
          wxString strHref = strWrk.Mid(nPos + 5, 16);
          strHref = strHref.AfterFirst('"');
          strHref = strHref.BeforeLast('"');
          
          if (!pi.Update((int)tfFilesList.GetCurrentLine(), strHref))
            break;

          //lavora solo con i files del modulo in questione
          if (strHref.StartsWith(strModule))
          {
            const wxString strPatch = strHref.Mid(2, 4).Lower();
            const wxString strExt = strHref.Right(4).Lower();
            //  1) e' un pacco
            //se e' un pacco devo sapere se e' piu' recente della installazione corrente (potrebbe essere un pacco nuovo..
            //..ricompattato) oppure se e' piu' vecchio e non va toccato (e' ad esempio il pacco originale)
            if (strPatch == "inst")
            {
              if (strExt == ".ini")  //operazioni sull'ini x sapere versione e patch
              {
                HTTPGet(strTempDir+strHref, strWebPath+strHref);
                CampoIniFile iniInst(strTempDir+strHref, strModule);
                const int nRemoteVer = iniInst.GetInt("Versione");
                const int nRemotePatch = iniInst.GetInt("Patch");

                //se deve aggiornarsi con il pacco scarica pure lo zip cosi' e' a posto
                if ((nRemoteVer > nLocVer) || ((nRemoteVer == nLocVer) && (nRemotePatch > nLocPatch)))
                {
                  const int nDot = strHref.Find('.', true);
                  strHref = strHref.Left(nDot) + "1.zip";
                  HTTPGet(strTempDir+strHref, strWebPath+strHref);
                }
                else  //se non deve aggiornarsi elimina l'ini in modo da non avere una mezza patch che farebbe incazzare l'unzipper
                  wxRemoveFile(strTempDir+strHref);
              } //if(strHref.EndsWith("ini")...
            } //if(strPatch == "inst")...
            //  2) e' una patch
            else  //se invece e' una normale patch... else di if(strPatch=="inst"..
            {
              //controlla che sia pi� avanti del suo livello patch e che sia un file utile (servono solo .ini e .zip)
              if (atoi(strPatch) > nLocPatch && (strExt==".ini" || strExt==".zip"))
                HTTPGet(strTempDir+strHref, strWebPath+strHref);
            }  
          } //if(strHref.StartsWith(strModule))...
        } //if(nPos>0)...
      } //for(wxString strWrk = tf...
      //chiude il file temporaneo
      tfFilesList.Close();
    } //if(tfFilesList...
    
    //scompatta le patch di sistema e aggiorna l'install.ini locale
    ok = UnzipModule(strLocalPath, strTempDir, strModule);
  } //if(HTTPGet(...
  return ok;
}

//-----------------------------------------------------
//  METODI ALTO LIVELLO PER AGGIORNAMENTI NON STANDARD
//-----------------------------------------------------
//---------------------------------------------------------------
// 1) metodo per l'aggiornamento dei client di rete in automatico
//---------------------------------------------------------------
void CampoSetup::ClientUpdate()
{
  wxFileName fn(m_strSetupPath);
  fn.AppendDir("..");
  fn.MakeAbsolute();
  fn.SetFullName("install.ini");
  const wxString strLocalPath = fn.GetPath();

  wxString strRemotePath;
  {
    CampoIniFile iniLocalInstall(fn.GetFullPath(), "Main");
    strRemotePath = iniLocalInstall.Get("DiskPath");
  }

  ClientUpdateModule(strLocalPath, strRemotePath, "sy");

  wxSetWorkingDirectory(strLocalPath);
  wxExecute("ba1 -6 /uADMIN");
}

//-----------------------------------------------------------------------------------------------
// 2) metodo per l'aggiornamento da disco attraverso il menu di Manutenzione/Installazione moduli
//-----------------------------------------------------------------------------------------------
void CampoSetup::DiskUpdate()
{
  wxFileName fn(m_strSetupPath);
  fn.AppendDir("..");
  fn.MakeAbsolute();
  fn.SetFullName("install.ini");
  const wxString strLocalPath = fn.GetPath();

  wxString strRemotePath;
  {
    CampoIniFile iniLocalInstall(fn.GetFullPath(), "Main");
    strRemotePath = iniLocalInstall.Get("DiskPath");
  }

  DiskUpdateModule(strLocalPath, strRemotePath, "sy");
  DiskUpdateModule(strLocalPath, strRemotePath, "sr");

  wxSetWorkingDirectory(strLocalPath);
  wxExecute("ba1 -6 /uADMIN");
}

//----------------------------------------------------------------------------------------
// 3) metodo per l'aggiornamento via internet attraverso Manutenzione/Installazione moduli
//----------------------------------------------------------------------------------------
void CampoSetup::InternetUpdate()
{
  //install.ini locale dove setup.exe e' in esecuzione
  wxFileName fn(m_strSetupPath);
  fn.AppendDir("..");
  fn.MakeAbsolute();
  fn.SetFullName("install.ini");
  const wxString strLocalPath = fn.GetPath();

  //quale e' il path web da cui aggiornarmi?
  wxString strRemotePath;
  {
    //Install.ini locale da cui leggere il path di dove sta il mio server web
    CampoIniFile iniLocalInstall(fn.GetFullPath(), "Main");
    strRemotePath = iniLocalInstall.Get("WebPath");
  }

  //aggiornamento dei moduli SY e SR via web
  InternetUpdateModule(strLocalPath, strRemotePath, "sy");
  InternetUpdateModule(strLocalPath, strRemotePath, "sr");

  //lanciare ba1.exe -6 in uscita
  wxSetWorkingDirectory(strLocalPath);
  wxExecute("ba1 -6 /uADMIN");
}

//--------------------------------------------------------------------------
//metodo per tutte le installazioni e gli aggiornamenti automatici in locale
//--------------------------------------------------------------------------
void CampoSetup::NormalSetup()
{
  //creazione del CampoWizard, ovvero dell'insieme di finestre per gestire installazione/aggiornamento
  m_pWizard = new CampoWizard(m_pMainFrame);

  if (m_pWizard->Run())
  {
    //e' una DEMO o una versione normale?
    const bool bInstallDemoVersion = m_pWizard->GetInstDemoVersion();

    // 0) INSTALLAZIONE VERSIONE DEMO (SIETE PAZZI?)
    //----------------------------------------------
    if (bInstallDemoVersion)
    {
      const wxString& strPrgLocPath = "c:/campodemo";
      const wxString& strDataPath = strPrgLocPath + "/dati";
      const wxString& strHelpPath = strPrgLocPath + "/htmlhelp";
      //creazione delle directories necessarie alla installazione DEMO
      CheckAndMakeDir(strPrgLocPath, "programmi");
      CheckAndMakeDir(strDataPath, "dati");
      CheckAndMakeDir(strHelpPath, "help");

      //copia della campodemo sull'hard disk in c:\campodemo
      wxArrayString asDemoList;
      const wxString strSrc = GetSourceDir("campodemo");
      
      if (wxDir::Exists(strSrc))
      {
        const size_t uFilesToCopy = wxDir::GetAllFiles(strSrc, &asDemoList);
        wxString strFileCurr;
        const size_t nPathLenght = strSrc.Len();
        CampoProgressDialog pi("Installazione Versione Demo...", (int)uFilesToCopy, m_pWizard);      
        for (size_t i = 0; i < uFilesToCopy; i++)
        {
          if (!pi.Update((int)i, asDemoList[i]))
            break;

          asDemoList[i].Lower();
          strFileCurr = strPrgLocPath;
          strFileCurr += asDemoList[i].Mid(nPathLenght);
                 
          if (!strFileCurr.IsEmpty())
          {
            if (!CopyFilesAndDirs(asDemoList[i], strFileCurr, false))
              break;
          }
        }

        //icona sul desktop
        const bool bDesktopShortcut = m_pWizard->GetDesktopShortcut();
        if (bDesktopShortcut)
        {
          const wxFileName strExe("c:/campodemo", wxT("ba0.exe"));
          CreateIcon(CSIDL_COMMON_DESKTOPDIRECTORY, strExe, "CampoDEMO");
        }

        //lanciare ba0.exe in uscita
        wxSetWorkingDirectory(strPrgLocPath);
        wxExecute("ba0");
      }
    }
    else    //tutti i casi normali (std,server,client,aggiornamento)
    {
      // 1) RACCOLTA PARAMETRI GENERALI INSTALLAZIONE (tipo,path,cartelle,servers,...)
      //------------------------------------------------------------------------------
      //tipo di installazione/aggiornamento
      const InstallationType uInstallationType = m_pWizard->GetInstallationType();
      const bool bNewInstallation = uInstallationType != it_upgrade;
      //installazione servers? solo per server di campo
      const bool bInstallLurch = uInstallationType == it_server && (m_pWizard->GetInstUseAuth() || m_pWizard->GetInstUseDict());
      //uso servers? sarebbe solo per i client ma lo teniamo buono per tutti
      const bool bUseLurch = uInstallationType != it_server && (!m_pWizard->GetSrvAuth().IsEmpty() || !m_pWizard->GetSrvDict().IsEmpty());
      //installazione datidemo? (oddio speriamo di no!; comunque vale solo per installazione standard)
      const bool bInstallDemoData = uInstallationType == it_standalone && m_pWizard->GetInstDemoData();
      //cartelle selezionate dall'utente
      const wxString& strPrgLocPath = m_pWizard->GetPrgLocPath();
      const wxString& strDataPath = m_pWizard->GetDataPath();

      //se nuova installazione deve anche creare la directory di destinazione
      if (bNewInstallation)
      {
        //creazione della directory dei programmi (compreso l'intero albero directory)
        CheckAndMakeDir(strPrgLocPath, "programmi");
        //creazione della directory dei dati (compreso l'intero albero directory)
        //un client NON installa i dati!! (senno' e' una installazione locale semplice)
        if (uInstallationType != it_client)
          CheckAndMakeDir(strDataPath, "dati");
      }
   

      // 2) COPIA DEI FILES DI INSTALLAZIONE DALLA CARTELLA CAMPO (E SUBDIRS) (SU CD) ALLA CARTELLA DESTINAZIONE
      //--------------------------------------------------------------------------------------------------------
      //copia del contenuto della cartella campo nella cartella di destinazione (installaz/aggiornam)
      //per prima cosa cerca la cartella dei files sorgente...
      wxArrayString asFilesList;
      wxFileName strSourcePath(m_strSetupPath, "*.*");
      strSourcePath.AppendDir("..");
      strSourcePath.AppendDir("..");
      strSourcePath.AppendDir("campo"); // NOME fisso della cartella del CD, non mettere APPNAME
      strSourcePath.MakeAbsolute();
      //stringa inutile al programma ma decisiva per il programmatore
      const wxString strSrc = strSourcePath.GetPath();
      //..contando anche quanti sono e memorizzandoli in un array asFileList
      const size_t uFilesToCopy = wxDir::GetAllFiles(strSrc, &asFilesList);

      const size_t nPathLenght = strSrc.Len();
      //progress bar
      CampoProgressDialog pi("Installazione Dati e Programmi di base...", (int)uFilesToCopy, m_pWizard);
      
      for (size_t i = 0; i < uFilesToCopy; i++)
      { 
        //per ogni file da copiare controlla i path sorgente e destinazione(problema con sottodirectory tipo..
        //..dati); strFileCurr va lasciato qui perche' DEVE ESSERE AZZERATO ad ogni cambio file!!!!!!
        wxString strFileCurr;

        //controlla se il file corrente e' dentro una sottodirectory (tipo dati,servers,setup...) oppure e' al..
        //..primo livello (quindi e' un file di programma)
        wxString strSourceFile = asFilesList[i].Lower();
        NormalizeSlash(strSourceFile);

        //e' in una subdir se la lunghezza del suo path prima dell'ultimo '/' e' > della lunghezza del path di root
        const bool bIsSubDir = strSourceFile.Find('/', true) > (int)nPathLenght;

        //2A) files con subdirectory
        if (bIsSubDir)
        {
          //files dei dati standard! solo in caso di nuova installazione!!
          if (strSourceFile.Find("/dati/") > 0)
          {
            //i DATI NON VANNO MAI installati in caso di aggiornamento!!!
            //i DATI NON VANNO MAI installati in caso di installazione client!!!
            //e nemmeno in caso si scelga di installare i dati demo (senno' sporca la dir dati e i datidemo non si installano)
            //Ricordare che c'e' gia' stato il controllo nella Forward della pagina di selezione, quindi la cartella..
            //..di destinazione e' comunque vuota
            if (bNewInstallation && uInstallationType != it_client && !bInstallDemoData)
            {
              strFileCurr = strDataPath;
              strFileCurr += asFilesList[i].Mid(nPathLenght + 5);
              //Ulteriore controllo di sicurezza: se trova il file di destinazione gia' presente..
              //..NON lo sovrascrive (lo mette vuoto cosi' non lo copia)
              if (wxFileName::FileExists(strFileCurr))
                strFileCurr = "";
            }
          } else
          if (strSourceFile.Find("/servers/") > 0) //che fare con i servers? copiare la directory...
          {
            if (bInstallLurch)  //..ma solo se devi installare i servers
            {
              strFileCurr = strPrgLocPath;
              strFileCurr += "/servers";
              strFileCurr += asFilesList[i].Mid(nPathLenght + 8);
            }
          }
          else  //files vari di altre subdirectory che si installano sempre (es. setup)
          {
            strFileCurr = strPrgLocPath;
            strFileCurr += asFilesList[i].Mid(nPathLenght);
          }
        }
        //2B) files senza subdirectory (programmi!)
        else
        {
          strFileCurr = strPrgLocPath;
          strFileCurr += asFilesList[i].Mid(nPathLenght);
        }
        //copia i files nella cartella di destinazione (programmi,dati,cazzi e mazzi);se il nome del file..
        //..di destinazione e' vuoto significa che non lo deve copiare!! (es. dati in aggiornamento)
        if (!strFileCurr.IsEmpty())
        {
          //aggiorna la progind
          if (!pi.Update((int)i, asFilesList[i]))
            break;
          //normalizza per sicurezza il nome (completo di path) del file (visto che ci sono metodi cui piace..
          //..aggiungere / o \ a piacere..
          NormalizeSlash(strFileCurr);

          //eventuali sottodirectory le crea (solo se hanno un nome) e poi copia fisicamente i files
          //se un file non si copia interrompe l'installazione con un ErrorBox
          if (!CopyFilesAndDirs(asFilesList[i], strFileCurr, true))
            break;
        } //if (!strFileCurr.IsEmpty()..
      } //for(size_t...
   
      UpdateInstallIni(strSrc + "/install.ini", strPrgLocPath + "/install.ini", "sy");

      // 3) INSTALLAZIONE DATI DEMO
      //---------------------------
      //installazione dei dati dimostrativi (che schifo!).Non si puo' fare in aggiornamento!
/*      if (bInstallDemoData && uInstallationType < 3)
      {
        //trova la cartella datidemo da copiare
        wxArrayString asDataList;
        const wxString strSrc = GetSourceDir("datidemo");
        //conta i files da copiare e si lancia nell'operazione di copiatura...
        //Vale quanto detto per l'installazione dell'area dati
        const size_t uFilesToCopy = wxDir::GetAllFiles(strSrc, &asDataList);
        wxString strFileCurr;
        const size_t nPathLenght = strSrc.Len();
        CampoProgressDialog pi("Installazione Dati Demo...", (int)uFilesToCopy, m_pWizard);      
        for (size_t i = 0; i < uFilesToCopy; i++)
        {
          if (!pi.Update((int)i, asDataList[i]))
            break;

          asDataList[i].Lower();
          strFileCurr = strDataPath;
          strFileCurr += asDataList[i].Mid(nPathLenght);
                 
          if (!strFileCurr.IsEmpty())
          {
            //eventuali sottodirectory le crea (solo se hanno un nome) e poi copia fisicamente i files
            //se un file non si copia interrompe l'installazione con un ErrorBox
            if (!CopyFilesAndDirs(asDataList[i], strFileCurr, false))
              break;
          } //if (!strFileCurr.IsEmpty()..
        } //for (size_t i = 0...
      } //if(bInstallDemoData... */


      // 4) COMPILAZIONE CAMPO.INI CON CONFIGURAZIONE NUOVA INSTALLAZIONE
      //-----------------------------------------------------------------
      //adesso deve compilare il campo.ini se nuova installazione..
      if (bNewInstallation)
      {
        { //parentesi necessaria per la scrittura immediata (non cancellare! serve per debug)
          //paragrafo [Main]
          CampoIniFile CampoIniMain(strPrgLocPath + "/campo.ini", "Main");
          const unsigned int uDongleType = m_pWizard->GetDongleType();
          CampoIniMain.Set("Donglehw", uDongleType);
          CampoIniMain.Set("Study", strDataPath);
          CampoIniMain.Set("Firm", "com");
          //server,client o standalone?
          CampoIniMain.Set("Type", uInstallationType);
        }
        //paragrafo [Server]
        if (bInstallLurch || bUseLurch)
        {
          CampoIniFile CampoIniSrv(strPrgLocPath + "/campo.ini", "Server");
          CampoIniSrv.Set("Dongle", m_pWizard->GetSrvAuth());
          CampoIniSrv.Set("Dictionary", m_pWizard->GetSrvDict());
        }
      }  //if(bNewInstallation...
      else  //e' un aggiornamento! scrive il type
      {
        CampoIniFile CampoIniMain(strPrgLocPath + "/campo.ini", "Main");
        //SOLO se sta aggiornando una versione antecedente alla 10.0 scrive la variabile Type nel campo.ini..
        //..ovvero deve testare se Type = 0
        if (CampoIniMain.GetInt("Type") == 0)
          CampoIniMain.Set("Type", CampoIniMain.GetInstallationType());
      }

      // 5) COMPILAZIONE\AGGIORNAMENTO INSTALL.INI CON DISKPATH
      //-------------------------------------------------------
      //..e modificare comunque l'install.ini aggiornando eventualmente l'origine dei programmi
      //parentesi necessaria per la scrittura immediata (non cancellare! serve per debug)
      {
        CampoIniFile CampoInstall(strPrgLocPath + "/install.ini", "Main");
        if (uInstallationType == it_client) //client: directory origine sul server
          CampoInstall.Set("DiskPath", m_pWizard->GetPrgNetPath());
        else  //e' il path assoluto dell'install.ini che sta in 'program' (es. D:\program)
        {
          const wxString strSrc = GetSourceDir("program");
          CampoInstall.Set("DiskPath", strSrc);
        }
      }
      
      // 6) AGGIORNAMENTO DI ADMIN.INI CON IL TEMA DEL RESELLER
      //---------------------------------------------------------
      //carica il tema predefinito del reseller dentro l'admin.ini che sta in cartella programmi\dati\config..
      //..in modo da vederli subito quando lancer� ba1 (e successivamente ba0)
      if (bNewInstallation)
      {
        //file sorgente
        const wxString strThemeFileName = Theme();
        wxString strSrcThemePath = m_strSetupPath;
        strSrcThemePath << strThemeFileName;
        CampoIniFile CampoTheme(strSrcThemePath, "Standard");

        //file di destinazione
        CampoIniFile CampoDestAdmin(strDataPath + "/config/admin.ini", "Colors");

        //copia del tema standard nell'admin.ini di destinazione
        long nIndex;
        wxString strKey;
        //ciclo su tutte le variabili del paragrafo di origine
        for (bool ok = CampoTheme.GetFirstEntry(strKey, nIndex); ok; ok = CampoTheme.GetNextEntry(strKey, nIndex))
        {
          //copia nel corrispondente paragrafo di destinazione
          CampoDestAdmin.Set(strKey, CampoTheme.Get(strKey));
        }
      }


      // 7) AGGIORNAMENTO DEL MODULO SY CON EVENTUALI PATCH PRESENTI IN PROGRAM DEL CD E AGGIORNAMENTO INSTALL.INI
      //----------------------------------------------------------------------------------------------------------
      //procede poi al caricamento delle patch eventuali di sistema all'interno della dir 'program' e al..
      //..conseguente riaggiornamento del livello versione/patch di SY ed SR in install.ini
      const wxString strPrgCDPath = GetSourceDir("program");
      if (wxFileName::DirExists(strPrgCDPath))
      {
        const wxString strSetupCDPath = strPrgCDPath + "/setup";
        const wxString strSetupLocPath = strPrgLocPath + "/setup";
        //Procedura di salvataggio del corrente OEM (reseller) del prg gi� installato; in questo modo anche..
        //..mandando un CD AGA (che generalmente funziona!) ad un cliente di un distributore non AGA, il reseller..
        //..a video non cambia. Per cambiare reseller si dovr� intervenire a mano sull'oem.ini cambiando il valore..
        //..di OEM dopo l'installazione
        const wxString strOemLocPath = strSetupLocPath + "/oem.ini";
        CampoIniFile CampoIniOem(strOemLocPath, "MAIN");
        const int nOldOem = CampoIniOem.GetInt("OEM");
        //svuota proprio la directory setup in modo da non coservare files indesiderati
        EmptyOutDir(strOemLocPath);
        //copia la directory setup da CD a locale
        CopyDir(strSetupCDPath, strSetupLocPath);
        //rimette a posto la candela...cio�, il reseller
        CampoIniOem.Set("OEM", nOldOem);
        //..Fine dell'aggiornamento della cartella setup

        UnzipModule(strPrgLocPath, strPrgCDPath, "sy");
        UnzipModule(strPrgLocPath, strPrgCDPath, "sr");
      }


      // 8) CREAZIONE AUTOSTART DEI SERVERS (SE CI SONO)
      //------------------------------------------------
      //solo se sta installando campo in postazione server e deve installare un gestore di servizi..
      //..avvia la procedura della creazione dell'autostart(un casino). Questa procedura non puo'..
      //..valere per l'aggiornamento, che agisce in modo diverso
      if (bNewInstallation)
      {
        const LurchMode iSrvAutostartMode = m_pWizard->GetSrvAutostartMode();
        if (iSrvAutostartMode != lm_none)
        {
          CreateAutostartMode(iSrvAutostartMode, strPrgLocPath);
        }
      }


      // 9) CREAZIONE DELL'ICONA SUL DESKTOP
      //------------------------------------
      const bool bDesktopShortcut = m_pWizard->GetDesktopShortcut();
      if (bDesktopShortcut)
      {
        const wxFileName strExe(strPrgLocPath, wxT("ba0.exe"));

        CampoIniFile CampoIniMain(strPrgLocPath + "/campo.ini", "Main");
        InstallationType nInstType = CampoIniMain.GetInstallationType();
        
        wxString strLnk = Product();
        switch (nInstType)
        {
        case it_server: strLnk += " (Server)"; break;
        case it_client: strLnk += " (Client)"; break;
        default: break;
        }

        //crea comunque l'icona
        CreateIcon(CSIDL_COMMON_DESKTOPDIRECTORY, strExe, strLnk);
      }


      // 10) AGGIORNAMENTO CAMPO.STP
      //---------------------------
      if (bNewInstallation)
      {
        //deve aggiungere la nuova installazione al campo.stp
        for (int i = 1;; i++)
        { 
          wxString strGroup;
          strGroup << i;
          CampoIniFile CampoStp("C:\\campo.stp", strGroup);
          wxString strPath = CampoStp.Get("Program");
          if (strPath.IsEmpty())
          {
            CampoStp.Set("Program", strPrgLocPath);
            break;
          }
        }
      }
      

      // 11) CHIUDE IL SETUP LANCIANDO BA1 -6 PER INSTALLAZIONE MODULI
      //-------------------------------------------------------------
      //lanciare ba1.exe -6 in uscita
      if (wxSetWorkingDirectory(strPrgLocPath))
        wxExecute("ba1 -6 /uADMIN");
      else
        ErrorBox("Impossibile installare i moduli. Cartella di destinazione irraggiungibile!");

    }

  }  //if (m_pWizard->Run())...
  m_pWizard->Destroy();
}

//------------------------------------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////////
//  Metodi per il controllo preventivo di eventuali cazzate in procinto di accadere
////////////////////////////////////////////////////////////////////////////////////
bool CampoSetup::SystemRunning(const wxString& strAppName, wxString strMsg) const
{
  wxSingleInstanceChecker sicProgram(strAppName);

  strMsg.Replace(wxT("PRODUCT"), PRODUCT);

  int i;
  for (i = 10; i > 0 && sicProgram.IsAnotherRunning(); i--)
    wxSleep(1);

  if (i <= 0 && !strMsg.IsEmpty())
    ErrorBox(strMsg);

  return i <= 0;
}


bool CampoSetup::LurchRunning() const
{
  //cerca Lurch che funziona come programma (esecuzione in modalita' avvio automatico)...
  bool ok = SystemRunning("Lurch", wxEmptyString);
  if (!ok)
  {
    //...se non lo trova cerca il solo Authoriz sempre come programma...
    ok = SystemRunning("Authorization", wxEmptyString);
    //se non trova nulla cerca Lurch come servizio...
    if (!ok)
    {
      int year = 0;
      wxString strSrv = "127.0.0.1";
      ok = ServerLogin(year, strSrv) != 0xFFFF;
    }
  }
  return ok;
}


bool CampoSetup::CampoRunning() const
{
  //ricerca di campo.aut per avere la lista dei moduli
  //prima tenta con aggiornamento da disco...
  wxString strInstallPath = "../install.ini";
  //...poi con aggiornamento da CD
  if (!wxFileName::FileExists(strInstallPath))
    strInstallPath = "../../campo/install.ini";
 
  //fa un giro sull'install.ini alla ricerca di tutti i moduli (sono i paragrafi lunghi 2) installati (se aggiornamento..
  //..da disco) o da installare (se sta installando da CD)
  wxArrayString asModuli;
  if (wxFileName::FileExists(strInstallPath))
  {
    wxString strModulo;
    long nIndex;
    CampoIniFile iniInstall(strInstallPath, strModulo);
    for (bool ok = iniInstall.GetFirstGroup(strModulo, nIndex); ok; ok = iniInstall.GetNextGroup(strModulo, nIndex))
    {
      if (strModulo.Len() == 2)
        asModuli.Add(strModulo);  //aggiunge all'array tutti i moduli che trova in install.ini
    }
  }
  else  //non trova nessun install.ini (non dovrebbe mai accadere) e tenta i moduli pi� importanti
  {
    asModuli.Add("ba");
    asModuli.Add("cg");
    asModuli.Add("ve");
    asModuli.Add("mg");
    asModuli.Add("lv");
  }
  
  //scandisce l'array dei moduli alla ricerca di un eventuale programma di campo ancora running a sua insaputa
  wxString strAppName;
  for (size_t i = 0; i < asModuli.GetCount(); i++)
  {
    for (size_t j = 0; j <= 9; j++)
    {
      strAppName = asModuli[i];
      strAppName += ('0'+j);
      wxSingleInstanceChecker sicProgram(strAppName);

      if (sicProgram.IsAnotherRunning())
      {
        strAppName << " � in esecuzione! E' necessario terminarlo utilizzando il Task Manager di Windows";
        ErrorBox(strAppName);
        return true;
      }

    }
  }
  return false; //campo non sta andando
}

//metodo principale che sceglie la modalita' di lancio del programma
void CampoSetup::OnTimer(wxTimerEvent& WXUNUSED(e))
{
  //controllo di un eventuale setup gi� lanciato (tipico di utonti e della premiata ditta bib� & bib�)
   wxSingleInstanceChecker sicSetup("setup");
   if (sicSetup.IsAnotherRunning())
   {
      wxString msg;
      msg << "Una installazione di " << PRODUCT << " � gi� in esecuzione!";
      ErrorBox(msg);
      m_pMainFrame->Destroy();
     return;
   }

  //controllo di eventuali Campo o servers attivi e conseguente interruzione dell'installazione
  m_bLurchWasRunning = LurchRunning();
  if (m_bLurchWasRunning)
  {
    //cazzone
    const unsigned short sCazzoni = ServerLoggedUsers("127.0.0.1");
    //fine cazzone
    wxString msg;
    msg << "Per poter aggiornare/installare il programma " << PRODUCT   
        << " deve temporaneamente essere disattivato il gestore delle licenze!\n"
        << "Prima di disattivare il gestore delle licenze accertarsi che tutti gli utenti spengano il programma!!\n"
        << "Proseguire con la disattivazione?";
    if (YesNoBox(msg))
      StopLurch();
    else
    {
      m_pMainFrame->Destroy();
      return;
    }
  }

  if (SystemRunning("ba0", "Uscire dal programma PRODUCT prima di procedere con l'aggiornamento/installazione!") ||
      SystemRunning("ba1", "Uscire dal programma di manutenzione di PRODUCT\n prima di procedere con l'aggiornamento/installazione!"))
  {
    m_pMainFrame->Destroy();
    return;
  }

  if (CampoRunning())
  {
    wxString msg;
    msg << "Un applicativo di " << PRODUCT << " � ancora in esecuzione!\n"
        << "L'installazione sar� interrotta immediatamente";
    ErrorBox(msg);
    m_pMainFrame->Destroy();
    return;
  }

  //path del programma setup.exe in esecuzione; serve in seguito in quanto alcuni metodi (tipo la GetAllFiles)..
  //..fanno perdere questo path
  //attenzione!!! il path DEVE terminare con "\" senn� in base al tipo di chiamata (-uw,-uc,-ud) i rispettivi..
  //..metodi funzionano a casaccio!!!
  const wxString strCommand = argv[1];

  if (strCommand.IsEmpty())
  {
    wxFileName strPath(argv[0]);
    strPath.MakeAbsolute();
    strPath.SetCwd();
    m_strSetupPath = strPath.GetPath();
    if (!m_strSetupPath.EndsWith(wxT("\\")))
      m_strSetupPath << '\\';

    //installazione normale da CD
    NormalSetup();
  }
  else
  {
    //attenzione: modifica richiesta perch� la cartella di esecuzione di setup � diversa nel caso sia un..
    //..aggiornamento o una installazione da CD
    m_strSetupPath = wxGetCwd();
    m_strSetupPath.MakeLower();
    if (!m_strSetupPath.EndsWith("setup"))
    { 
      if (!m_strSetupPath.EndsWith(wxT("\\")))
        m_strSetupPath << '\\';
      m_strSetupPath << "setup";
      wxSetWorkingDirectory(m_strSetupPath);
    }
    if (!m_strSetupPath.EndsWith(wxT("\\")))
      m_strSetupPath << '\\';

    //aggiornamento da disco,client,web
    if (strCommand == "-ud")
      DiskUpdate();
    if (strCommand == "-uc")
      ClientUpdate();
    if (strCommand == "-uw")
      InternetUpdate();
  }
    //finestrina x chiudere a mano il programma (sconsigliata causa lancio ba1.exe)
//  ::wxMessageBox(wxT("Installazione terminata"), APPNAME,  wxOK | wxICON_INFORMATION);
  m_pMainFrame->Destroy();
}


bool CampoSetup::OnInit()
{
  wxInitAllImageHandlers();
  m_locale.Init();

  m_pWizard = NULL; //finestra di programma nulla;deve esistere solo per il NormalSetup dove viene inizializzata
  m_pMainFrame = new CampoFrame;
  SetTopWindow(m_pMainFrame);

  wxTimerEvent e(883);
  AddPendingEvent(e);   
  
  return true;
}