
#ifndef TILBUILD_HPP
#define TILBUILD_HPP

// ---------------------------------------------------------------------------
class til_builder_t
{
public:

  // ---------------------------------------------------------------------------
  enum cvt_code_t
  {
    cvt_failed,
    cvt_ok,
    cvt_typedef           // conversion resulted in a typedef to a named type
  };

  // ---------------------------------------------------------------------------
  struct tpinfo_t
  {
    cvt_code_t cvt_code;
    qtype type;
    qtype fields;
    til_t *ti;
    tpinfo_t(void) : cvt_code(cvt_ok), ti(NULL) {}
    tpinfo_t(til_t *_ti, const qtype &t) : cvt_code(cvt_ok), type(t), ti(_ti) {}
    const char *dstr(void) const
    {
      if ( cvt_code == cvt_failed )
        return "#cvt_failed";

      static qstring res;
      if ( print_type_to_qstring(&res, NULL, 2, 0, PRTYPE_1LINE|PRTYPE_TYPE,
                                 ti, type.begin(), NULL, NULL, fields.begin()) <= 0 )
        res = "#print_failed";
      return res.c_str();
    }
  };

  // ---------------------------------------------------------------------------
  struct meminfo_t
  {
    qstring name;
    qtype type;
    qtype fields;
    uval_t offset;
    uint32 size;
    int bitnum;           // for bitfields: starting bit number. otherwise 0
    uint64 width;         // in bits
    bool is_bf(void) const { return (size != (uint32) -1) && (width < uint64(size)*8); }
    bool operator<(const meminfo_t &r) const
    {
      return offset  < r.offset
         || (offset == r.offset && bitnum < r.bitnum);
    }
    uint64 begin(void) const { return uint64(offset)*8+bitnum; }
    uint64 end(void) const { return begin()+width; }
  };

  til_builder_t(til_t *_ti, pdb_access_t *_pa)
    : unnamed_idx(0),
      ti(_ti),
      pdb_access(NULL),
      enregistered_bug(false)
  {
    set_pdb_access(_pa);
  }

  virtual ~til_builder_t()
  {
    typemap.clear();
    idmap.clear();
    tpdefs.clear();
    handled.clear();
    creating.clear();
    longnames.clear();
    unnamed_types.clear();
  }

  void set_pdb_access(pdb_access_t *_pdb_access)
  {
    pdb_access = _pdb_access;
  }

  typedef qvector<meminfo_t> members_t;
  typedef std::map<DWORD, tpinfo_t> typemap_t;
  typedef std::map<DWORD, uint32> idmap_t;
  typedef std::map<DWORD, qstring> tpdefs_t;
  typedef std::set<DWORD> idset_t;
  typedef std::map<qstring, int> creating_t;
  typedef std::map<qstring, qstring> longnames_t;
  typedef std::set<uint32> unnamed_t;

  //      remove `anonymous-namespace'::
  // also remove `anonymous namespace'::
  void       remove_anonymous_namespaces(qstring &storage);

  // very long type names can be stored in tils but can not be converted to
  // idb structs and enums. Truncate such names and, if necessary, add
  // a numeric suffix to distinguish them.
  void       convert_to_idb_name(qstring &storage);

  bool       get_symbol_type(pdb_sym_t &sym, qtype &itp, qtype *fields, int *p_id);
  bool       retrieve_type(pdb_sym_t &sym, pdb_sym_t *parent, qtype &itp, qtype *fields, int *p_id);
  cvt_code_t append_basetype(DWORD baseType, int size, qtype &res) const;
  bool       retrieve_children_types_names(pdb_sym_t &sym, func_type_info_t &fi, pdb_sym_t *funcSym);
  cm_t       convert_cc(DWORD cc0) const;
  uint32     get_variant_long_value(pdb_sym_t &sym) const;
  bool       is_member_func(pdb_sym_t &typeSym, qtype &owner, qtype *fields, pdb_sym_t *funcSym);
  bool       is_frame_reg(int regnum) const;
  int        get_symbol_funcarg_info(pdb_sym_t &sym, funcarg_info_t &storage, DWORD dwDataKind, DWORD locType, int stack_off);
  void       enum_function_args(pdb_sym_t &sym, func_type_info_t &args);
  int        handle_gap(size_t saved, size_t off) const;
  bool       verify_alignment(members_t &mems,
                              size_t total_size,
                              int align,
                              bool maymodify,
                              intvec_t &fillers) const;
  bool       create_gap_filler(qtype &itp, qtype *fields, size_t off, int size) const;
  cvt_code_t verify_union(members_t::iterator p1,
                          members_t::const_iterator p2,
                          members_t &out) const;
  cvt_code_t create_union(members_t::iterator p1, members_t::const_iterator p2, qtype &itp) const;
  void       remove_bitfields(members_t &mems) const;
  cvt_code_t make_vtable_struct(pdb_sym_t &sym, qtype &itp, qtype *fields);
  cvt_code_t convert_udt(pdb_sym_t &sym, DWORD64 size, qtype &itp, qtype *fields);
  void       append_member_type(qtype *itp, const meminfo_t &mem) const;
  cvt_code_t create_udt(members_t &mems, int udtKind, uint64 size, qtype &itp, qtype *fields) const;
  cvt_code_t create_udt_ref(members_t &mems, int udt_kind, qtype &itp) const;
  bool       is_complex_return(pdb_sym_t &sym) const;
  bool       is_unnamed_tag_typedef(const type_t *ptr) const;
  cvt_code_t really_convert_type(pdb_sym_t &sym, pdb_sym_t *parent, DWORD tag, qtype &itp, qtype *fields);
  cvt_code_t convert_type(pdb_sym_t &sym, pdb_sym_t *parent, DWORD type, DWORD tag, qtype &itp, qtype *fields);
  uint32     begin_creation(DWORD tag, const qstring &name);
  uint32     end_creation(const qstring &name);
  cvt_code_t handle_overlapping_members(members_t &mems) const;
  // Will iterate on children, and call handle_function_type_child()
  HRESULT    handle_symbols(pdb_sym_t &pGlobal);
  HRESULT    handle_publics(pdb_sym_t &pGlobal);
  HRESULT    handle_types(pdb_sym_t &pGlobal);
  HRESULT    build(pdb_sym_t &pGlobal);
  int        after_iterating();
  ea_t       get_load_address() const { return pdb_access->get_base_address(); };
  bool       handle_symbol(pdb_sym_t &sym);
  bool       is_arm(DWORD machine_type) const;
  size_t     get_symbol_type_length(pdb_sym_t &sym) const;

  virtual int     before_iterating();
  virtual bool    get_symbol_name(pdb_sym_t &sym, qstring &storage);
  virtual bool    handle_symbol_at_ea(pdb_sym_t &sym, DWORD tag, DWORD id, ea_t ea, qstring &name);
  virtual void    type_created(ea_t /*ea*/, int /*id*/, const char * /*name*/, const type_t * /*ptr*/) const;
  virtual void    handle_function_type(pdb_sym_t &fun_sym, ea_t ea);
  virtual HRESULT handle_function_type_child(pdb_sym_t &fun_sym,
                                             ea_t ea,
                                             pdb_sym_t &child_sym,
                                             DWORD child_tag,
                                             DWORD child_loc_type);
  virtual cvt_code_t handle_unnamed_overlapping_member(members_t & /*mems*/, qstack<qstring> & /*union_names*/, qstring & /*name*/) const { return cvt_ok; }

protected:
  typemap_t   typemap;              // id -> type info
  idmap_t     idmap;                // id -> type ordinal
  tpdefs_t    tpdefs;               // id -> enum type defined in base til
  idset_t     handled;              // set of handled symbols
  creating_t  creating;
  longnames_t longnames;
  unnamed_t   unnamed_types;
  int         unnamed_idx;

public:
  til_t        *ti;
  pdb_access_t *pdb_access;
  bool          enregistered_bug;
};

#endif // TILBUILD_HPP
