#include "wxinc.h"
#include "utils.h"

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


//metodo per farsi ritornare l'ID di sessione e quindi sapere se si e' in modalita' termserv (win2000,win2003,win2008)
int GetSessionId()
{
  DWORD session = 0;
  ::ProcessIdToSessionId(GetCurrentProcessId(), &session);
  return (int) session;
}

//lista delle versioni di windows
#define WUNKNOWNSTR	_T("unknown Windows version")

#define W95STR		_T("Windows 95")
#define W95SP1STR	_T("Windows 95 SP1")
#define W95OSR2STR	_T("Windows 95 OSR2")
#define W98STR		_T("Windows 98")
#define W98SP1STR	_T("Windows 98 SP1")
#define W98SESTR	_T("Windows 98 SE")
#define WMESTR		_T("Windows ME")

#define WNT351STR	_T("Windows NT 3.51")
#define WNT4STR		_T("Windows NT 4")
#define W2KSTR		_T("Windows 2000")
#define WXPSTR		_T("Windows XP")
#define W2003STR	_T("Windows Server 2003")
#define WVISTASTR	_T("Windows Vista")
#define W2008STR	_T("Windows Server 2008")
#define W7STR	    _T("Windows 7")

#define WCESTR		_T("Windows CE")


//metodo per provare a trovare la versione di windows
bool GetWinVer(LPTSTR lpszVersion, int nVersionSize, int *pnVersion)
{
	int nVersion = WUNKNOWN;
  LPCTSTR cp = WUNKNOWNSTR;

	OSVERSIONINFO osinfo; memset(&osinfo, 0, sizeof(osinfo));
	osinfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
  if (::GetVersionEx(&osinfo))
  {
	  DWORD dwPlatformId   = osinfo.dwPlatformId;
	  DWORD dwMinorVersion = osinfo.dwMinorVersion;
	  DWORD dwMajorVersion = osinfo.dwMajorVersion;
	  DWORD dwBuildNumber  = osinfo.dwBuildNumber & 0xFFFF;	// Win 95 needs this

	  if ((dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) && (dwMajorVersion == 4))
	  {
		  if ((dwMinorVersion < 10) && (dwBuildNumber == 950))
		  {
			  cp = W95STR;
			  nVersion = W95;
		  }
		  else if ((dwMinorVersion < 10) &&
				  ((dwBuildNumber > 950) && (dwBuildNumber <= 1080)))
		  {
			  cp = W95SP1STR;
			  nVersion = W95SP1;
		  }
		  else if ((dwMinorVersion < 10) && (dwBuildNumber > 1080))
		  {
			  cp = W95OSR2STR;
			  nVersion = W95OSR2;
		  }
		  else if ((dwMinorVersion == 10) && (dwBuildNumber == 1998))
		  {
			  cp = W98STR;
			  nVersion = W98;
		  }
		  else if ((dwMinorVersion == 10) &&
				  ((dwBuildNumber > 1998) && (dwBuildNumber < 2183)))
		  {
			  cp = W98SP1STR;
			  nVersion = W98SP1;
		  }
		  else if ((dwMinorVersion == 10) && (dwBuildNumber >= 2183))
		  {
			  cp = W98SESTR;
			  nVersion = W98SE;
		  }
		  else if (dwMinorVersion == 90)
		  {
			  cp = WMESTR;
			  nVersion = WME;
		  }
	  }
	  else if (dwPlatformId == VER_PLATFORM_WIN32_NT)
	  {
		  if (dwMajorVersion == 3)
		  {
			  cp = WNT351STR;
			  nVersion = WNT351;
		  }
		  else if (dwMajorVersion == 4)
		  {
			  cp = WNT4STR;
			  nVersion = WNT4;
		  }
		  else if (dwMajorVersion == 5)
		  {
        switch (dwMinorVersion)
        {
        case  0: cp = W2KSTR; nVersion = W2K; break;
        case  1: cp = WXPSTR; nVersion = WXP; break;
        case  2:
        default: cp = W2003STR;	nVersion = W2003; break;
        }
		  }
		  else if (dwMajorVersion == 6)
		  {
			  cp = WVISTASTR;
			  nVersion = WVISTA;
        // TBI: Windows 2008 Server
		  }
		  else if (dwMajorVersion >= 7)
		  {
			  cp = W7STR;
			  nVersion = W7;
		  }
	  }
/*	  else if (dwPlatformId == VER_PLATFORM_WIN32_CE)
	  {
		  cp = WCESTR;
		  nVersion = WCE;
	  }*/
  }

  if (lpszVersion != NULL && nVersionSize > 0)
    _tcsncpy(lpszVersion, cp, nVersionSize-1);
  if (pnVersion != NULL)
    *pnVersion = nVersion;

  return nVersion == WUNKNOWN;
}

wxString GetWindowsProgramDirectory()
{
  //scelta della directory di installazione di default
  #ifdef WIN32
    TCHAR strFolder[MAX_PATH];
    ::SHGetFolderPath(NULL, CSIDL_PROGRAM_FILES, NULL, SHGFP_TYPE_CURRENT, strFolder);
    return strFolder;
  #endif
}

wxString GetDefaultDestination()
{
  wxString strDest;
  strDest = GetWindowsProgramDirectory();

  if (strDest.IsEmpty())
  {
    // Forse � la cartella base dove � installato Word?
    wxFileType* pDoc = wxTheMimeTypesManager->GetFileTypeFromExtension(wxT("doc"));
    if (pDoc != NULL)
    {
      wxFileName strFilename = pDoc->GetOpenCommand(wxT("pippo.doc"));
      while (strFilename.GetDirCount() > 1)
        strFilename.RemoveLastDir();
      strDest = strFilename.GetPath().After('"');
      if (strDest.StartsWith(wxT("C\\")))
        strDest = wxT("C:") + strDest.Mid(1);
    }
  }

  return strDest;
}


//metodo per avere la lista delle condivisioni remote connesse al computer (preso da MSDN)
size_t ListNetworkDisks(wxArrayString& asList)
{
  DWORD dwResult, dwResultEnum;
  HANDLE hEnum;
  DWORD cbBuffer = 16384;      // 16K is a good size
  DWORD cEntries = -1;         // enumerate all possible entries
  LPNETRESOURCE lpnrLocal;     // pointer to enumerated structures
  DWORD i;
  //
  // Call the WNetOpenEnum function to begin the enumeration.
  //
  dwResult = WNetOpenEnum(RESOURCE_CONNECTED,  // connected resources
                          RESOURCETYPE_DISK,   // only disks
                          0,        // enumerate all resources
                          NULL,     // NULL first time the function is called
                          &hEnum);  // handle to the resource

  if (dwResult != NO_ERROR)
  {  
    //
    // Process errors with an application-defined error handler.
    //
    //NetErrorHandler(hwnd, dwResult, (LPSTR)"WNetOpenEnum");
    //return false;
    return 0;
  }
  //
  // Call the GlobalAlloc function to allocate resources.
  //
  lpnrLocal = (LPNETRESOURCE) GlobalAlloc(GPTR, cbBuffer);
  if (lpnrLocal == NULL) 
      return 0;
  
  do
  {  
    //
    // Initialize the buffer.
    //
    ZeroMemory(lpnrLocal, cbBuffer);
    //
    // Call the WNetEnumResource function to continue
    //  the enumeration.
    //
    dwResultEnum = WNetEnumResource(hEnum,      // resource handle
                                    &cEntries,  // defined locally as -1
                                    lpnrLocal,  // LPNETRESOURCE
                                    &cbBuffer); // buffer size
    //
    // If the call succeeds, loop through the structures.
    //
    if (dwResultEnum == NO_ERROR)
    {
      for(i = 0; i < cEntries; i++)
      {
        asList.Add(lpnrLocal[i].lpLocalName);
      }
    }
    // Process errors.
    //
    else if (dwResultEnum != ERROR_NO_MORE_ITEMS)
    {
      //NetErrorHandler(hwnd, dwResultEnum, (LPSTR)"WNetEnumResource");
      break;
    }
  }
  //
  // End do.
  //
  while(dwResultEnum != ERROR_NO_MORE_ITEMS);
  //
  // Call the GlobalFree function to free the memory.
  //
  GlobalFree((HGLOBAL)lpnrLocal);
  //
  // Call WNetCloseEnum to end the enumeration.
  //
  dwResult = WNetCloseEnum(hEnum);
  
  if(dwResult != NO_ERROR)
  { 
    //
    // Process errors.
    //
    //NetErrorHandler(hwnd, dwResult, (LPSTR)"WNetCloseEnum");
    return 0;
  }

  return asList.GetCount();

}

//metodo per avere un array con la lista dei path delle condivisioni locali
size_t ListSharedDirectories(wxArrayString& asList)
{
#ifdef WIN32
  NET_API_STATUS res = ERROR_MORE_DATA;

  while (res == ERROR_MORE_DATA)
  {
    PSHARE_INFO_502 shi;
    DWORD nEntriesRead = 0, nTotalEntries = 0, nResume = 0;
    res = NetShareEnum(NULL, 502, (LPBYTE *)&shi, -1, &nEntriesRead, &nTotalEntries, &nResume);
    if (res == ERROR_SUCCESS || res == ERROR_MORE_DATA)
    {
      for (DWORD i = 0; i < nEntriesRead; i++)
      {
        wxString strPath(shi[i].shi502_path);
        if (!strPath.IsEmpty())
        {
          strPath.MakeLower();
          asList.Add(strPath);
        }
      } //for(DWORD i...
      NetApiBufferFree(shi);
    } //if(res ==...
  } //while (res==ERROR_MORE_DATA...
#endif
  return asList.GetCount();
}

//Magico metodo per stabilire se una directory e' condivisa ed e' su un disco locale!!
bool IsSharedDirectory (const wxString& strDir)
{
  bool found = false;

  wxString strDataDir = strDir.Lower();
  strDataDir.Replace ("/", "\\");

  wxArrayString asList;
  ListSharedDirectories(asList);

  for (size_t i = 0; i < asList.GetCount(); i++)
  {
    const wxString& strPath = asList[i];
    //se trova la directory di studio condivisa ed e' su disco locale -> server
    if (strPath.Len() > 3 && strDir.StartsWith(strPath))
    {
      found = true;
      break;
    }
  } //for(DWORD i...

  return found;
}

//finestre per messaggi vari
//---------------------------------------------------------------------------------
bool ErrorBox(const wxString str)
{
  wxMessageBox(str, APPNAME, wxOK | wxICON_ERROR);
  return false;
}

bool MessageBox(const wxString str)
{
  wxMessageBox(str, APPNAME, wxOK | wxICON_INFORMATION);
  return false;
}

bool WarningBox(const wxString str)
{
  wxMessageBox(str, APPNAME, wxOK | wxICON_EXCLAMATION);
  return false;
}

bool YesNoBox(const wxString str)
{
  int nAnswer = wxMessageBox(str, APPNAME, wxYES_NO | wxICON_QUESTION);
  if (nAnswer == wxYES)
    return true;
  else
    return false;
}

//classe per gestire i .Ini di campo
//-----------------------------------------------------------------------------------
bool CampoIniFile::GetFirstGroup(wxString& strGroup, long& nIndex)
{
  m_asGroups.Clear();

  char bufferone[1024*16];
  ::GetPrivateProfileSectionNames(bufferone, sizeof(bufferone), m_strIniName);
  const char* inizio = bufferone;
  for (const char* b = bufferone; ; b++) if (*b == '\0')
  {
    if (*inizio)
    {
      m_asGroups.Add(inizio);
      inizio = b+1;
    }
    else
      break;
  }

  nIndex = 0;
  return GetNextGroup(strGroup, nIndex);
}

bool CampoIniFile::GetNextGroup(wxString& strGroup, long& nIndex)
{
  const bool ok = nIndex >= 0 && nIndex < (long)m_asGroups.GetCount();
  if (ok)
    strGroup = m_asGroups[nIndex++];
  return ok;
}

bool CampoIniFile::DeleteGroup(const wxString strGroup)
{
  return WritePrivateProfileString(m_strGroup, NULL, NULL, m_strIniName) != 0;
}

bool CampoIniFile::GetFirstEntry(wxString& strEntry, long& nIndex)
{
  m_asEntries.Clear();

  char bufferone[1024*32];
  ::GetPrivateProfileSection(m_strGroup, bufferone, sizeof(bufferone), m_strIniName);
  const char* pInizio = bufferone;
  for (const char* b = bufferone; ; b++) if (*b == '\0')
  {
    if (*pInizio)
    {
      wxString strWrk = pInizio;
      strWrk = strWrk.BeforeFirst('=');
      strWrk.Trim();
      //prende solo il nome della variabile per completare la lista! dopo l'= ci sarebbe il valore
      m_asEntries.Add(strWrk);
      pInizio = b+1;
    }
    else
      break;
  }

  nIndex = 0;
  return GetNextEntry(strEntry, nIndex);
}

bool CampoIniFile::GetNextEntry(wxString& strEntry, long& nIndex)
{
  const bool ok = nIndex >= 0 && nIndex < (long)m_asEntries.GetCount();
  if (ok)
    strEntry = m_asEntries[nIndex++];
  return ok;
}

wxString CampoIniFile::Get(const wxString strVariable) const
{
  wxString strOutString;
  char* buffer = strOutString.GetWriteBuf(256);
  ::GetPrivateProfileString(m_strGroup, strVariable, "", buffer, 256, m_strIniName);
  strOutString.UngetWriteBuf(); //sblocca la memoria senno' la stringa resta per sempre!!!

  return strOutString;
}

bool CampoIniFile::GetBool(const wxString strVariable) const
{
  const char chVal = Get(strVariable)[0];
  return chVal == 'X' || chVal == 'Y' || chVal == '1';
}

int CampoIniFile::GetInt(const wxString strVariable) const
{
  return atoi(Get(strVariable)); 
}

bool CampoIniFile::Set(const wxString strVariable, const wxString strValue)
{
  return ::WritePrivateProfileString(m_strGroup, strVariable, strValue, m_strIniName) != 0;
}

bool CampoIniFile::Set(const wxString strVariable, const int uValue)
{
  wxString strValue;
  strValue << uValue;
  return ::WritePrivateProfileString(m_strGroup, strVariable, strValue, m_strIniName) != 0;
}

//costruttore
CampoIniFile::CampoIniFile(const wxString strIniPath, wxString strGroup)
            : m_strIniName(strIniPath), m_strGroup(strGroup)
{
}


//metodo per sapere che cavolo di tipo di installazione sta esaminando (serve per leggere e scrivere...
//...correttamente il campo.ini
InstallationType CampoIniFile::GetInstallationType() const
{
  InstallationType nType = (InstallationType)GetInt("Type");
  if (nType < it_standalone || nType > it_client)
  {
    nType = it_standalone;  //di base e' standalone
    const bool bTestDataBase = GetBool("TestDatabase");
    const bool bTestPrograms = GetBool("TestPrograms");
    
    if (bTestDataBase)  //se puo' manipolare i dati e' StandAlone o Server..
    {
      //ma e' Standalone o Server?
      //se la directory dei dati e' condivisa in scrittura e' un server (almeno al 99%)
      const wxString strStudy = Get("Study");
      if (IsSharedDirectory(strStudy))
        nType = it_server;  //e' server
    }
    else                //..senno' e' client
      nType = it_client;
  }
  return nType;
}


//classe per le progind con dimensioni
//--------------------------------------------------------------------------------------
#define CPD_FLAGS wxPD_AUTO_HIDE | wxPD_APP_MODAL | wxPD_CAN_ABORT | wxPD_ELAPSED_TIME | wxPD_ESTIMATED_TIME | wxPD_REMAINING_TIME

CampoProgressDialog::CampoProgressDialog(const wxString& strTitle, int nMaximum, wxWindow* pParent)
                   : wxProgressDialog(strTitle, wxEmptyString, nMaximum, pParent, CPD_FLAGS)
{
  SetSize(wxSize(600, -1));
  Center();
}

//gestione filesystem
//--------------------------------------------------------------------------------------
void CheckAndMakeDir(const wxString& strDir, const wxString& strMsg)
{
  if (!wxDir::Exists(strDir))
  {
    const wxFileName fname(strDir + "\\*.*");
    const wxArrayString& asDirs = fname.GetDirs();
    wxString strCartella = fname.GetVolume();
    strCartella += fname.GetVolumeSeparator();
    for (size_t i = 0; i < asDirs.GetCount(); i++)
    {
      strCartella += "\\";
      strCartella += asDirs[i];
      if (!wxDir::Exists(strCartella) && !wxMkdir(strCartella, 0777))
      {  
        wxString strError = "Impossibile creare la cartella ";
        strError += strMsg;
        strError += " ";
        strError += strCartella;
        strError += "\nAssicurarsi di avere il permesso di scrittura sul disco e che vi sia spazio a sufficienza";
        ErrorBox(strMsg);
        return;
      }
    }
  }
  if (!strMsg.IsEmpty())  //se il messaggio e' vuoto NON deve fare alcun controllo (installazione sistema)
  {
    wxDir dirDir(strDir);
    if(dirDir.HasFiles()) //se la dir di destinazione dovesse esistere gia' (installazione abortita) almeno sia vuota
    {
      ErrorBox("La cartella di destinazione non e' vuota!\nInstallazione interrotta!");
      return;
    }
  }
}

bool CopiaFile(const wxString& strFileSrc, const wxString& strFileDest)
{
  //serve per ritrovare eventuali sottodirectory
  wxFileName fnFileDest(strFileDest);
  if (!fnFileDest.DirExists())
    fnFileDest.Mkdir(0777, wxPATH_MKDIR_FULL);

  bool ok =  ::wxCopyFile(strFileSrc, strFileDest);
  if(ok)
  {
#ifdef WIN32
    DWORD dwFileAttribs = ::GetFileAttributes(strFileDest);
    if (dwFileAttribs & FILE_ATTRIBUTE_READONLY)
    {
      dwFileAttribs &= ~FILE_ATTRIBUTE_READONLY;
      ::SetFileAttributes(strFileDest, dwFileAttribs);
    }
#endif
  }
  else
  {
    wxString strErr = "Impossibile copiare il file ";
    strErr += strFileSrc;
    strErr += " in ";
    strErr += strFileDest;
    strErr += "\nInstallazione interrotta!";
    ErrorBox(strErr);
  }
  return ok;
}


void NormalizeSlash(wxString& strFileName)
{
  strFileName.Replace("\\", "/");  // Trasforma i backslash in slash
  strFileName.Replace("//", "/");  // Elimina i doppi slash
}


//metodi per unzippare i files
//-------------------------------------------------------------------------------
static size_t GetZipList(const char* strZipFile, wxArrayString& aFiles)
{
	wxFFileInputStream fin(strZipFile);
  wxZipInputStream zip(fin);
  for (wxZipEntry* z = zip.GetNextEntry(); z; z = zip.GetNextEntry())
  {
    const wxString str = z->GetInternalName();
    aFiles.Add(str);
  }

	return aFiles.GetCount();
}



bool UnzipFile(const char* strSrcFile, const char* strDestDir)
{
  //se il file da unzippare � su un disco remoto, lo copia in locale per unzipparlo e alla fine elimina la copia..
  //..� importante per velocizzare l'aggiornamento da disco
  wxString strZipFile = strSrcFile;
  const int nDriveType = ::GetDriveType(strZipFile.Left(3));
  bool bTemp = nDriveType == DRIVE_REMOTE;
  if (bTemp)
  {
    wxBusyCursor wait;
    const wxString strFullAppName = PRODUCT + " " + RESELLER;
    wxDialog dlgWait(NULL, wxID_ANY, strFullAppName, wxDefaultPosition, wxSize(480,80), wxCAPTION);
    wxString str; str << "Copia di " << strSrcFile << "\nAttendere prego...";

    wxStaticText* text = new wxStaticText(&dlgWait, wxID_ANY, wxEmptyString, wxPoint(4,4), wxSize(472,72), 
                         wxST_NO_AUTORESIZE || wxST_DOTS_MIDDLE);
    text->SetLabel(str);
    
    dlgWait.Centre();
    dlgWait.Show(true);
    dlgWait.Update();
    strZipFile = wxFileName::CreateTempFileName("tmp");
    if (!wxCopyFile(strSrcFile, strZipFile))
    {
      strZipFile = strSrcFile;
      bTemp = false;
    }
    dlgWait.Show(false);
  }

	wxArrayString aFiles;
	const size_t files = GetZipList(strZipFile, aFiles);

  CampoProgressDialog pi(strZipFile, (int)files);

	for (unsigned int f = 0; f < files; f++)
	{
    const wxString& strFileName = aFiles[f];
    if (!pi.Update(f, strFileName))
      break;

    if (wxEndsWithPathSeparator(strFileName) || strFileName.Find('.') < 0)  // Is dir name
    {
		  wxString strOutDir = strDestDir;
      if (!wxEndsWithPathSeparator(strOutDir))
		    strOutDir += wxFILE_SEP_PATH;
		  strOutDir += strFileName;
      CheckAndMakeDir(strOutDir, wxEmptyString);
    }
    else
    {
		  wxZipInputStream fin(strZipFile, strFileName);

		  wxString strOutFile = strDestDir;
      if (!wxEndsWithPathSeparator(strOutFile) && !wxIsPathSeparator(strFileName[0]))
		    strOutFile += wxFILE_SEP_PATH;
		  strOutFile += strFileName;

		  wxString strPath;
		  ::wxSplitPath(strOutFile, &strPath, NULL, NULL);
      CheckAndMakeDir(strPath, wxEmptyString);
	
      wxFileOutputStream fout(strOutFile);
		  fout.Write(fin);
      fout.Close();

      if (strOutFile.EndsWith("res.zip"))
        UnzipFile(strOutFile , strPath);
    }
	}

  if (bTemp)
    wxRemoveFile(strZipFile);

	return files > 0;
}


// metodi per raccattare il reseller del cd che si sta installando
//---------------------------------------------------------------------------------------
static const char* const encryption_key = "QSECOFR-";

//Ritorna in chiaro la stringa crittata che gli viene passata
static wxString CampoDecode(const wxString& data)
{
  wxString tmp;

  for (size_t i = 0; data[i]; i++)
    tmp << wxChar(data[i] - (i < 8 ? encryption_key[i] : tmp[i - 8]));

  return tmp; 
}

static wxString FindOemIniPath()
{
  wxString strWorkingDir = wxFileName::GetCwd();
  strWorkingDir.MakeLower();
  if (!strWorkingDir.EndsWith("setup"))
    strWorkingDir << "/setup";

  strWorkingDir << "/oem.ini";

  return strWorkingDir;
}

//trova il numero del reseller
int FindReseller()
{
  CampoIniFile iniOemMain(FindOemIniPath(), "MAIN");
  const int nOem = iniOemMain.GetInt("OEM");
  return  nOem;
}

//stringhe globali per avere sempre a disposizione Reseller, Product e Appname in ogni momento (vanno sulle finestre)
static wxString _strReseller, _strProduct, _strAppname, _strLicence, _strLogo, _strIcon, _strTheme;

//lettore del oem.ini che contiene tutte le info necessarie del CD
static void ReadOemIni()
{
  //si basa sul reseller che � la guida di ogni parametro
  if (_strReseller.IsEmpty())
  {
    int oem = FindReseller();
    if (oem < 0)
      oem = 0;

    //trovato il numero del reseller legge il paragrafo corrispondente in oem.ini
    const wxString strOemIniPath = FindOemIniPath();

    wxString strParagraph;
    strParagraph << "OEM_" << oem;
    CampoIniFile iniOem(strOemIniPath, strParagraph);

    //raccatta le informazioni dal paragrafo corretto del produttore
    //prima quelle crittate 
    const wxString strCriptedAppname = iniOem.Get("Name");
    const wxString strCriptedProduct = iniOem.Get("Product");
    const wxString strCriptedReseller = iniOem.Get("Reseller");

    //adesso viene il bello!deve decrittarle....ci vuole ENIGMA!
    //usiamo una stringa del cazzo di appoggio per debuggare in tranquillit�
    wxString strCaz = CampoDecode(strCriptedAppname);
    _strAppname = strCaz;
    strCaz = CampoDecode(strCriptedProduct);
    _strProduct = strCaz;
    strCaz = CampoDecode(strCriptedReseller);
    _strReseller = strCaz;

    //dati non crittati (a disposizione di cani e porci insomma...)
    _strIcon = iniOem.Get("Icon");
    _strLicence = iniOem.Get("Licence");
    _strLogo = iniOem.Get("Logo");
    _strTheme = iniOem.Get("Themes");
    
  }
}

//metodi per ritornare la stringa di competenza;si basano tutti su ReadOemIni
wxString Reseller()
{
  if (_strReseller.IsEmpty())
    ReadOemIni();
  return _strReseller;
}

wxString Product()
{
  if (_strProduct.IsEmpty())
    ReadOemIni();
  return _strProduct;
}

wxString Appname()
{
  if (_strAppname.IsEmpty())
    ReadOemIni();
  return _strAppname;
}

wxString Licence()
{
  if (_strLicence.IsEmpty())
    ReadOemIni();
  return _strLicence;
}

wxString Logo()
{
  if (_strLogo.IsEmpty())
    ReadOemIni();
  return _strLogo;
}

wxString Icon()
{
  if (_strIcon.IsEmpty())
    ReadOemIni();
  return _strIcon;
}

wxString Theme()
{
  if (_strTheme.IsEmpty())
    ReadOemIni();
  return _strTheme;
}