/*
 * libjingle
 * Copyright 2003-2007, Google Inc.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *  1. Redistributions of source code must retain the above copyright notice,
 *     this list of conditions and the following disclaimer.
 *  2. Redistributions in binary form must reproduce the above copyright notice,
 *     this list of conditions and the following disclaimer in the documentation
 *     and/or other materials provided with the distribution.
 *  3. The name of the author may not be used to endorse or promote products
 *     derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

// Registry configuration wrappers class
//
// Offers static functions for convenient
// fast access for individual values
//
// Also provides a wrapper class for efficient
// batch operations on values of a given registry key.
//

#ifndef TALK_BASE_WIN32REGKEY_H_
#define TALK_BASE_WIN32REGKEY_H_

#include <string>
#include <vector>

#include "talk/base/basictypes.h"
#include "talk/base/win32.h"

namespace talk_base {

// maximum sizes registry key and value names
const int kMaxKeyNameChars = 255 + 1;
const int kMaxValueNameChars = 16383 + 1;

class RegKey {
 public:
  // constructor
  RegKey();

  // destructor
  ~RegKey();

  // create a reg key
  HRESULT Create(HKEY parent_key, const wchar_t* key_name);

  HRESULT Create(HKEY parent_key,
                 const wchar_t* key_name,
                 wchar_t* reg_class,
                 DWORD options,
                 REGSAM sam_desired,
                 LPSECURITY_ATTRIBUTES lp_sec_attr,
                 LPDWORD lp_disposition);

  // open an existing reg key
  HRESULT Open(HKEY parent_key, const wchar_t* key_name);

  HRESULT Open(HKEY parent_key, const wchar_t* key_name, REGSAM sam_desired);

  // close this reg key
  HRESULT Close();

  // check if the key has a specified value
  bool HasValue(const wchar_t* value_name) const;

  // get the number of values for this key
  uint32 GetValueCount();

  // Called to get the value name for the given value name index
  // Use GetValueCount() to get the total value_name count for this key
  // Returns failure if no key at the specified index
  // If you modify the key while enumerating, the indexes will be out of order.
  // Since the index order is not guaranteed, you need to reset your counting
  // loop.
  // 'type' refers to REG_DWORD, REG_QWORD, etc..
  // 'type' can be NULL if not interested in the value type
  HRESULT GetValueNameAt(int index, std::wstring* value_name, DWORD* type);

  // check if the current key has the specified subkey
  bool HasSubkey(const wchar_t* key_name) const;

  // get the number of subkeys for this key
  uint32 GetSubkeyCount();

  // Called to get the key name for the given key index
  // Use GetSubkeyCount() to get the total count for this key
  // Returns failure if no key at the specified index
  // If you modify the key while enumerating, the indexes will be out of order.
  // Since the index order is not guaranteed, you need to reset your counting
  // loop.
  HRESULT GetSubkeyNameAt(int index, std::wstring* key_name);

  // SETTERS

  // set an int32 value - use when reading multiple values from a key
  HRESULT SetValue(const wchar_t* value_name, DWORD value) const;

  // set an int64 value
  HRESULT SetValue(const wchar_t* value_name, DWORD64 value) const;

  // set a string value
  HRESULT SetValue(const wchar_t* value_name, const wchar_t* value) const;

  // set binary data
  HRESULT SetValue(const wchar_t* value_name,
                   const uint8* value,
                   DWORD byte_count) const;

  // set raw data, including type
  HRESULT SetValue(const wchar_t* value_name,
                   const uint8* value,
                   DWORD byte_count,
                   DWORD type) const;

  // GETTERS

  // get an int32 value
  HRESULT GetValue(const wchar_t* value_name, DWORD* value) const;

  // get an int64 value
  HRESULT GetValue(const wchar_t* value_name, DWORD64* value) const;

  // get a string value - the caller must free the return buffer
  HRESULT GetValue(const wchar_t* value_name, wchar_t** value) const;

  // get a string value
  HRESULT GetValue(const wchar_t* value_name, std::wstring* value) const;

  // get a std::vector<std::wstring> value from REG_MULTI_SZ type
  HRESULT GetValue(const wchar_t* value_name,
                   std::vector<std::wstring>* value) const;

  // get binary data - the caller must free the return buffer
  HRESULT GetValue(const wchar_t* value_name,
                   uint8** value,
                   DWORD* byte_count) const;

  // get raw data, including type - the caller must free the return buffer
  HRESULT GetValue(const wchar_t* value_name,
                   uint8** value,
                   DWORD* byte_count,
                   DWORD* type) const;

  // STATIC VERSIONS

  // flush
  static HRESULT FlushKey(const wchar_t* full_key_name);

  // check if a key exists
  static bool HasKey(const wchar_t* full_key_name);

  // check if the key has a specified value
  static bool HasValue(const wchar_t* full_key_name, const wchar_t* value_name);

  // SETTERS

  // STATIC int32 set
  static HRESULT SetValue(const wchar_t* full_key_name,
                          const wchar_t* value_name,
                          DWORD value);

  // STATIC int64 set
  static HRESULT SetValue(const wchar_t* full_key_name,
                          const wchar_t* value_name,
                          DWORD64 value);

  // STATIC float set
  static HRESULT SetValue(const wchar_t* full_key_name,
                          const wchar_t* value_name,
                          float value);

  // STATIC double set
  static HRESULT SetValue(const wchar_t* full_key_name,
                          const wchar_t* value_name,
                          double value);

  // STATIC string set
  static HRESULT SetValue(const wchar_t* full_key_name,
                          const wchar_t* value_name,
                          const wchar_t* value);

  // STATIC binary data set
  static HRESULT SetValue(const wchar_t* full_key_name,
                          const wchar_t* value_name,
                          const uint8* value,
                          DWORD byte_count);

  // STATIC multi-string set
  static HRESULT SetValueMultiSZ(const wchar_t* full_key_name,
                                 const TCHAR* value_name,
                                 const uint8* value,
                                 DWORD byte_count);

  // GETTERS

  // STATIC int32 get
  static HRESULT GetValue(const wchar_t* full_key_name,
                          const wchar_t* value_name,
                          DWORD* value);

  // STATIC int64 get
  //
  // Note: if you are using time64 you should
  // likely use GetLimitedTimeValue (util.h) instead of this method.
  static HRESULT GetValue(const wchar_t* full_key_name,
                          const wchar_t* value_name,
                          DWORD64* value);

  // STATIC float get
  static HRESULT GetValue(const wchar_t* full_key_name,
                          const wchar_t* value_name,
                          float* value);

  // STATIC double get
  static HRESULT GetValue(const wchar_t* full_key_name,
                          const wchar_t* value_name,
                          double* value);

  // STATIC string get
  // Note: the caller must free the return buffer for wchar_t* version
  static HRESULT GetValue(const wchar_t* full_key_name,
                          const wchar_t* value_name,
                          wchar_t** value);
  static HRESULT GetValue(const wchar_t* full_key_name,
                          const wchar_t* value_name,
                          std::wstring* value);

  // STATIC REG_MULTI_SZ get
  static HRESULT GetValue(const wchar_t* full_key_name,
                          const wchar_t* value_name,
                          std::vector<std::wstring>* value);

  // STATIC get binary data - the caller must free the return buffer
  static HRESULT GetValue(const wchar_t* full_key_name,
                          const wchar_t* value_name,
                          uint8** value,
                          DWORD* byte_count);

  // Get type of a registry value
  static HRESULT GetValueType(const wchar_t* full_key_name,
                              const wchar_t* value_name,
                              DWORD* value_type);

  // delete a subkey of the current key (with no subkeys)
  HRESULT DeleteSubKey(const wchar_t* key_name);

  // recursively delete a sub key of the current key (and all its subkeys)
  HRESULT RecurseDeleteSubKey(const wchar_t* key_name);

  // STATIC version of delete key - handles nested keys also
  // delete a key and all its sub-keys recursively
  // Returns S_FALSE if key didn't exist, S_OK if deletion was successful,
  // and failure otherwise.
  static HRESULT DeleteKey(const wchar_t* full_key_name);

  // STATIC version of delete key
  // delete a key recursively or non-recursively
  // Returns S_FALSE if key didn't exist, S_OK if deletion was successful,
  // and failure otherwise.
  static HRESULT DeleteKey(const wchar_t* full_key_name, bool recursive);

  // delete the specified value
  HRESULT DeleteValue(const wchar_t* value_name);

  // STATIC version of delete value
  // Returns S_FALSE if key didn't exist, S_OK if deletion was successful,
  // and failure otherwise.
  static HRESULT DeleteValue(const wchar_t* full_key_name,
                             const wchar_t* value_name);

  // Peek inside (use a RegKey as a smart wrapper around a registry handle)
  HKEY key() { return h_key_; }

  // helper function to get the HKEY and the root key from a string
  // modifies the argument in place and returns the key name
  // e.g. HKLM\\Software\\Google\... returns HKLM, "Software\\Google\..."
  // Necessary for the static versions that use the full name of the reg key
  static HKEY GetRootKeyInfo(std::wstring* full_key_name);

  // Returns true if this key name is 'safe' for deletion (doesn't specify a key
  // root)
  static bool SafeKeyNameForDeletion(const wchar_t* key_name);

  // save the key and all of its subkeys and values to a file
  static HRESULT Save(const wchar_t* full_key_name, const wchar_t* file_name);

  // restore the key and all of its subkeys and values which are saved into a
  // file
  static HRESULT Restore(const wchar_t* full_key_name,
                         const wchar_t* file_name);

  // Is the key empty: having no sub-keys and values
  static bool IsKeyEmpty(const wchar_t* full_key_name);

 private:

  // helper function to get any value from the registry
  // used when the size of the data is unknown
  HRESULT GetValueHelper(const wchar_t* value_name,
                         DWORD* type, uint8** value,
                         DWORD* byte_count) const;

  // helper function to get the parent key name and the subkey from a string
  // modifies the argument in place and returns the key name
  // Necessary for the static versions that use the full name of the reg key
  static std::wstring GetParentKeyInfo(std::wstring* key_name);

  // common SET Helper for the static case
  static HRESULT SetValueStaticHelper(const wchar_t* full_key_name,
                                      const wchar_t* value_name,
                                      DWORD type,
                                      LPVOID value,
                                      DWORD byte_count = 0);

  // common GET Helper for the static case
  static HRESULT GetValueStaticHelper(const wchar_t* full_key_name,
                                      const wchar_t* value_name,
                                      DWORD type,
                                      LPVOID value,
                                      DWORD* byte_count = NULL);

  // convert REG_MULTI_SZ bytes to string array
  static HRESULT MultiSZBytesToStringArray(const uint8* buffer,
                                           DWORD byte_count,
                                           std::vector<std::wstring>* value);

  // the HKEY for the current key
  HKEY h_key_;

  // for unittest
  friend void RegKeyHelperFunctionsTest();

  DISALLOW_EVIL_CONSTRUCTORS(RegKey);
};

}  // namespace talk_base

#endif  // TALK_BASE_WIN32REGKEY_H_
