////////////////////////////////////////////////////////////////////////////////
// Win32/Config.cpp -- this file is part of the Emulator Developers Kit
// available at http://ourworld.compuserve.com/homepages/pc64/develop.htm


////////////////////////////////////////////////////////////////////////////////
// use registry for Windows 95 and Windows NT

class ConfigReg32 : public ConfigBase {
  HKEY hkProgram;
public:

  // constructor
  ConfigReg32(const char* pcProgram) {
    hkProgram = NULL;
    reg(RegCreateKeyEx(HKEY_CURRENT_USER, pcProgram, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hkProgram, NULL));
  }

  // destructor
  virtual ~ConfigReg32() {
    if (hkProgram != NULL) {
      reg(RegCloseKey(hkProgram));
      hkProgram = NULL;
    }
  }

  // set string value
  void SetString(const char* pcSection, const char* pcKey, const char* pcValue) {
    assert(hkProgram != NULL);
    HKEY hkSection = hkProgram;
    if (pcSection != NULL) {
      reg(RegCreateKeyEx(hkProgram, pcSection, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hkSection, NULL));
    }
    try {
      reg(RegSetValueEx(hkSection, pcKey, 0, REG_SZ, (const BYTE*)pcValue, strlen(pcValue) + 1));
    } catch (...) {
      if (hkSection != hkProgram) {
        RegCloseKey(hkSection);
      }
      throw;
    }
    if (hkSection != hkProgram) {
      reg(RegCloseKey(hkSection));
    }
  }

  // set integer value
  void SetInt(const char* pcSection, const char* pcKey, int iValue) {
    assert(hkProgram != NULL);
    HKEY hkSection = hkProgram;
    if (pcSection != NULL) {
      reg(RegCreateKeyEx(hkProgram, pcSection, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hkSection, NULL));
    }
    try {
      reg(RegSetValueEx(hkSection, pcKey, 0, REG_DWORD, (const BYTE*)&iValue, 4));
    } catch (...) {
      if (hkSection != hkProgram) {
        RegCloseKey(hkSection);
      }
      throw;
    }
    if (hkSection != hkProgram) {
      reg(RegCloseKey(hkSection));
    }
  }

  // get string value and set it to default if it doesn't exist
  void GetString(const char* pcSection, const char* pcKey, char* pcBuffer, int iSize, const char* pcDefault) {
    assert(hkProgram != NULL);
    assert(pcDefault != pcBuffer);
    HKEY hkSection = hkProgram;
    if (pcSection != NULL) {
      reg(RegCreateKeyEx(hkProgram, pcSection, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hkSection, NULL));
    }
    try {
      DWORD dwType;
      LONG lResult = RegQueryValueEx(hkSection, pcKey, 0, &dwType, (BYTE*)pcBuffer, (DWORD*)&iSize);
      if (lResult == ERROR_FILE_NOT_FOUND) {
        reg(RegSetValueEx(hkSection, pcKey, 0, REG_SZ, (const BYTE*)pcDefault, strlen(pcDefault) + 1));
        strncpy(pcBuffer, pcDefault, iSize);
      } else {
        reg(lResult);
        if (dwType != REG_SZ) {
          error("The setting %s is not of type REG_SZ", pcKey);
        }
      }
    } catch (...) {
      if (hkSection != hkProgram) {
        RegCloseKey(hkSection);
      }
      throw;
    }
    if (hkSection != hkProgram) {
      reg(RegCloseKey(hkSection));
    }
  }

  // get integer value and set it to default if it doesn't exist
  int GetInt(const char* pcSection, const char* pcKey, int iDefault) {
    assert(hkProgram != NULL);
    DWORD dwReturn;
    HKEY hkSection = hkProgram;
    if (pcSection != NULL) {
      reg(RegCreateKeyEx(hkProgram, pcSection, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hkSection, NULL));
    }
    try {
      DWORD dwType = REG_DWORD;
      DWORD dwSize = sizeof DWORD;
      LONG lResult = RegQueryValueEx(hkSection, pcKey, 0, &dwType, (BYTE*)&dwReturn, &dwSize);
      if (lResult == ERROR_FILE_NOT_FOUND) {
        reg(RegSetValueEx(hkSection, pcKey, 0, REG_DWORD, (const BYTE*)&iDefault, 4));
        dwReturn = (DWORD)iDefault;
      } else {
        if (lResult == ERROR_MORE_DATA || dwType != REG_DWORD) {
          error("The setting %s is not of type REG_DWORD", pcKey);
        }
        reg(lResult);
        assert(dwSize == sizeof DWORD);
      }
      if (hkSection != hkProgram) {
        RegCloseKey(hkSection);
      }
    } catch (...) {
      if (hkSection != hkProgram) {
        RegCloseKey(hkSection);
      }
      throw;
    }
    return (int)dwReturn;
  }
};


////////////////////////////////////////////////////////////////////////////////
// use INI file for Win32s

class ConfigIni : public ConfigBase {
  char* pcIni;
public:

  // constructor
  ConfigIni(const char* pcIniFile) {
    verify((pcIni = strdup(GetProgramDirectory(pcIniFile))) != NULL);
  }

  // destructor
  virtual ~ConfigIni() {
    if (pcIni != NULL) {
      MemFree(pcIni);
      pcIni = NULL;
    }
  }

  // set string value
  void SetString(const char* pcSection, const char* pcKey, const char* pcValue) {
    assert(pcIni != NULL);
    if (pcSection == NULL) {
      pcSection = "";
    }
    win(WritePrivateProfileString(pcSection, pcKey, pcValue, pcIni));
  }

  // set integer value
  void SetInt(const char* pcSection, const char* pcKey, int iValue) {
    assert(pcIni != NULL);
    if (pcSection == NULL) {
      pcSection = "";
    }
    char ac[20];
    _itoa(iValue, ac, 10);
    assert(strlen(ac) < sizeof ac);
    win(WritePrivateProfileString(pcSection, pcKey, ac, pcIni));
  }

  // get string value and set it to default if it doesn't exist
  void GetString(const char* pcSection, const char* pcKey, char* pcBuffer, int iSize, const char* pcDefault) {
    assert(pcIni != NULL);
    assert(pcDefault != pcBuffer);
    assert(iSize >= 2);
    if (pcSection == NULL) {
      pcSection = "";
    }
    pcBuffer[0] = 0;
    GetPrivateProfileString(pcSection, pcKey, "\xA0", pcBuffer, iSize, pcIni);
    if (strcmp(pcBuffer, "\xA0") == 0) {
      win(WritePrivateProfileString(pcSection, pcKey, pcDefault, pcIni));
      strncpy(pcBuffer, pcDefault, iSize);
    }
  }

  // get integer value and set it to default if it doesn't exist
  int GetInt(const char* pcSection, const char* pcKey, int iDefault) {
    assert(pcIni != NULL);
    if (pcSection == NULL) {
      pcSection = "";
    }
    char ac[20];
    GetPrivateProfileString(pcSection, pcKey, "", ac, sizeof ac, pcIni);
    if (ac[0] == 0) {
      _itoa(iDefault, ac, 10);
      assert(strlen(ac) < sizeof ac);
      win(WritePrivateProfileString(pcSection, pcKey, ac, pcIni));
      return iDefault;
    }
    return atoi(ac);
  }
};


////////////////////////////////////////////////////////////////////////////////
// main class

// constructor
global Config::Config(const char* pcProgram, const char* pcIni) {
  pConfig = NULL;
  if (IsWin32s()) {
    trace("Config uses file %s", GetProgramDirectory(pcIni));
    pConfig = new ConfigIni(pcIni);
  } else {
    trace("Config base is HKEY_CURRENT_USER\\%s", pcProgram);
    pConfig = new ConfigReg32(pcProgram);
  }
}

// destructor
Config::~Config() {
  if (pConfig != NULL) {
    delete pConfig;
    pConfig = NULL;
  }
}
