#ifndef __PDB_COMMON_H__
#define __PDB_COMMON_H__

#include <typeinf.hpp>

static const char spath_prefix[] = "srv*";
static const char spath_suffix[] = "*http://msdl.microsoft.com/download/symbols";

#define PDB_NODE_NAME             "$ pdb"
#define PDB_DLLBASE_NODE_IDX       0
#define PDB_DLLNAME_NODE_IDX       0

enum PDB_CALLCODE
{
  // user invoked 'load pdb' command, load pdb for the input file.
  // after invocation, result (boolean) is stored in: netnode(PDB_NODE_NAME).altval(PDB_DLLBASE_NODE_IDX)
  PDB_CC_USER = 0,
  // ida decided to call the plugin itself
  PDB_CC_IDA  = 1,
  // load pdb for an additional exe/dll
  //   load_addr: netnode(PDB_NODE_NAME).altval(PDB_DLLBASE_NODE_IDX)
  //   dll_name:  netnode(PDB_NODE_NAME).supstr(PDB_DLLNAME_NODE_IDX)
  PDB_CC_ADDITIONAL = 2
};


// Note: This will return the machine type, as it is known
// by the IDB, which might not be what you think. For example,
// if you need to tell x86 and x64 apart, you're out of luck.
// You may want to consider looking at pdbaccess_t's
// get_machine_type().
uint32 get_machine_from_idb();

#ifdef __NT__
#include "pdbaccess.hpp"
#include "pdblocal.hpp"


//----------------------------------------------------------------------
struct pdb_session_t
{
  HMODULE             dia_hmod;
  int                 refcount;
  local_pdb_access_t *pdb_access;

  pdb_session_t()
    : refcount(1),
      dia_hmod(NULL),
      pdb_access(NULL) {}

  HRESULT open(const char *input_file,
               const char *user_spath = NULL,
               ea_t load_address = BADADDR,
               input_exe_reader_t exe_reader = NULL,
               input_mem_reader_t mem_reader = NULL,
               void *user_data = NULL);

  void    close();

private:
  HRESULT create_dia_source(IDiaDataSource **pSource, int *dia_version);
};


// ---------------------------------------------------------------------------
class pdb_session_ref_t
{
public:
  pdb_session_t *session;

  pdb_session_ref_t(void);
  pdb_session_ref_t(const pdb_session_ref_t &r);
  ~pdb_session_ref_t();

  pdb_session_ref_t &operator=(const pdb_session_ref_t &r);
  void       close();
  const bool opened() const;
  // DWORD      get_machine_type();
  HRESULT    open(const char *input_file,
                  const char *user_spath = NULL,
                  ea_t load_address = BADADDR,
                  input_exe_reader_t exe_reader = NULL,
                  input_mem_reader_t mem_reader = NULL,
                  void *user_data = NULL);

private:
  void destroy();

  // The total number of different PDB sessions; kept track of
  // in order to know when we can safely CoUninitialize().
  static int  sessions_count;

  // Whether COM is initialized in this thread.
  static bool co_initialized;
};


// The 'pdb_access', passed to the handler, will have been
// created using 'new', and it is the responsibility of the
// consumer of this function to delete it.
HRESULT handle_pdb_file(
        const char *input_file,
        const char *user_spath,
        HRESULT handler(pdb_session_ref_t pdb_session_ref, void *ud),
        ea_t load_address=BADADDR,
        input_exe_reader_t exe_reader = NULL,
        input_mem_reader_t mem_reader = NULL,
        void *ud=NULL);
#endif

#endif
