/*
 *      Interactive disassembler (IDA).
 *      Copyright (c) 1990-2008 Hex-Rays
 *      ALL RIGHTS RESERVED.
 *
 */

#ifndef __MOVES_HPP
#define __MOVES_HPP

#include <sistack.hpp>

#pragma pack(push, 1)           // IDA uses 1 byte alignments!

struct graph_location_info_t
{
  double zoom;          // zoom level, 1.0 == 100%, 0 means auto position
  double orgx;          // graph origin, x coord
  double orgy;          // graph origin, y coord
  graph_location_info_t(void) : zoom(0), orgx(0), orgy(0) {}
  bool operator == (const graph_location_info_t &r) const
    { return zoom == r.zoom && orgx == r.orgx && orgy == r.orgy; }
  bool operator != (const graph_location_info_t &r) const
    { return !(*this == r); }
};

#ifndef SWIG
// Helper functions. Should not be called directly!
class curloc;
class location_t;

struct segm_move_info_t
{
  segm_move_info_t(ea_t _from = 0, ea_t _to = 0, size_t _sz = 0)
    : from(_from), to(_to), size(_sz) {}
  ea_t from, to;
  size_t size;
};
DECLARE_TYPE_AS_MOVABLE(segm_move_info_t);
typedef qvector<segm_move_info_t> segm_move_infos_t;

#define DEFINE_CURLOC_HELPERS(decl) \
decl void ida_export curloc_linkTo   (curloc &, const char *stackName);\
decl void ida_export curloc_jump_push(curloc &, bool try_to_unhide, ea_t ea, int lnnum, int x, int y);\
decl bool ida_export curloc_pop      (curloc &, bool try_tohide);\
decl bool ida_export curloc_get      (curloc &, size_t depth);\
decl bool ida_export curloc_get_entry(curloc *out, const curloc &, size_t depth); \
decl int  ida_export curloc_mark     (const curloc &, int marker,const char *title, const char *desc);\
decl ea_t ida_export curloc_markedpos(const curloc &, int *marker);\
decl bool ida_export curloc_jump     (curloc &, int marker);\
decl ssize_t ida_export curloc_markdesc(const curloc &, int marker, char *buf, size_t bufsize);

#define DEFINE_LOCATION_HELPERS(decl) \
decl void ida_export location_linkTo   (location_t &, const char *name);\
decl void ida_export location_push_and_jump(location_t &, bool try_to_unhide, ea_t ea, int lnnum, int x, int y, const graph_location_info_t *gi);\
decl bool ida_export location_pop      (location_t &, bool try_tohide);\
decl bool ida_export location_get      (location_t &, size_t depth);\
decl bool ida_export location_get_entry(location_t *out, const location_t &, size_t depth); \
decl int  ida_export location_mark     (const location_t &, int marker, const char *title, const char *desc);\
decl bool ida_export location_jump     (location_t &, int marker);\

DEFINE_CURLOC_HELPERS(idaman)
DEFINE_LOCATION_HELPERS(idaman)
#else
#define DEFINE_CURLOC_HELPERS(decl)
#define DEFINE_LOCATION_HELPERS(decl)
#endif // SWIG

#define CURLOC_SISTACK_ITEMS 4
#define CURLOC_LIST "$ curlocs"

class curloc : public sistack_t
{
  void push(void);
  DEFINE_CURLOC_HELPERS(friend)
  void unhide_if_necessary(ea_t ea);
  void hide_if_necessary(void);
protected:
  void toup(ea_t _ea) { ea = _ea; lnnum = 0; x = 0; y = 0; flags = 0; target = BADADDR; };
public:
  ea_t ea;                // Address
  ushort x,y;             // coords on the screen
#define DEFAULT_CURSOR_Y 0xFFFF
  ushort lnnum;           // number of line for the current address
#define DEFAULT_LNNUM 0xFFFF
  ushort flags;           // unhid something?
#define UNHID_SEGM 0x0001 // unhid a segment at 'target'
#define UNHID_FUNC 0x0002 // unhid a function at 'target'
#define UNHID_AREA 0x0004 // unhid an area at 'target'
  ea_t target;

  curloc(void)                   { ea = target = BADADDR; flags = 0; x = 0; y = 0; }
  curloc(const char *stackName)  { linkTo(stackName); }
  void linkTo(const char *stackName)
    { curloc_linkTo(*this, stackName); }
  void setx(int xx)              { x  = ushort(xx); }
  void jump_push(bool try_to_unhide, ea_t _ea=BADADDR, int _lnnum=0, int _x=0, int _y=0)
    { curloc_jump_push(*this, try_to_unhide, _ea, _lnnum, _x, _y); }
  bool pop(bool try_tohide)
    { return curloc_pop(*this, try_tohide); }
  bool get(size_t depth)
    { return curloc_get(*this, depth); }
  bool get_entry(curloc *out, size_t depth) const
    { return curloc_get_entry(out, *this, depth); }
  size_t size(void) const        { return sistack_t::size()/CURLOC_SISTACK_ITEMS; }
  void copy_current_location(const curloc &loc)
  {
    ea    = loc.ea;
    lnnum = loc.lnnum;
    x     = loc.x;
    y     = loc.y;
  }

  // Mark/unmark position
  // marker - the marked position number (1..MAX_MARK_SLOT)
  //          if specified as <=0: ask the user to select the mark slot.
  // title  - if marker<=0, then the window caption of the dialog which
  //          will appear on the screen. title==NULL will lead to the default
  //          caption: "please select a mark slot"
  // desc   - description of the marked position. If NULL, IDA will show a dialog
  //          box asking the user to enter the description.
  //          If desc is specified as "", then the specified marked position
  //          will be deleted and the remaining positions will be shifted.
  // returns used marker number (<=0 - none)
  int mark(int marker, const char *title, const char *desc) const
        { return curloc_mark(*this, marker, title, desc); }

  ea_t markedpos(int *marker) const     // get address of marked location
        { return curloc_markedpos(*this, marker); }
  bool jump(int marker)                 // Jump to marker
        { return curloc_jump(*this, marker); }
  ssize_t markdesc(int marker, char *buf, size_t bufsize) const
        { return curloc_markdesc(*this, marker, buf, bufsize); }
  static void rebase_stack(nodeidx_t node, const segm_move_infos_t &infos);
};

#define MAX_MARK_SLOT   1024     // Max number of marked locations

// Since we can not modify the curloc class (for compatibility reasons)
// we create a derived class and will exclusively use it in IDA GUI
class location_t : public curloc
{
  typedef curloc inherited;
  DEFINE_LOCATION_HELPERS(friend)
public:
  graph_location_info_t gli;
  location_t(void) {}
  location_t(const char *name) { linkTo(name); }
  void linkTo(const char *name) { location_linkTo(*this, name); }

  void push_and_jump(bool try_to_unhide, ea_t _ea=BADADDR, int _lnnum=0,
                int _x=0, int _y=0,  graph_location_info_t *_gli=NULL)
    { location_push_and_jump(*this, try_to_unhide, _ea, _lnnum, _x, _y, _gli); }
  bool pop(bool try_tohide)
    { return location_pop(*this, try_tohide); }
  bool get(size_t depth)
    { return location_get(*this, depth); }
  bool get_entry(location_t *out, size_t depth) const
    { return location_get_entry(out, *this, depth); }
  void copy_current_location(const location_t &loc)
  {
    inherited::copy_current_location(loc);
    gli = loc.gli;
  }

  void jump_nopush(/*bool try_to_unhide,*/ ea_t _ea=BADADDR, int _lnnum=0,
                int _x=0, int _y=0,  graph_location_info_t *_gli=NULL)
  {
    flags = 0;
    target = BADADDR;
    if ( _ea == BADADDR )
      return;
    //if ( try_to_unhide )
    //  unhide_if_necessary(_ea);
    ea    = _ea;
    lnnum = uint16(_lnnum);
    x = uint16(_x);
    y = uint16(_y);
    if ( _gli != NULL )
      gli = *_gli;
  }

  int mark(int marker, const char *title, const char *desc) const
    { return location_mark(*this, marker, title, desc); }
  bool jump(int marker)
    { return location_jump(*this, marker); }
};

//----------------------------------------------------------------------
// functions for the kernel only:
void init_marks(void);
void term_marks(void);
void change_jumps_stack_format(void);
void move_marks(ea_t from, ea_t to, asize_t size);
void curloc_after_segments_moved(const segm_move_infos_t &infos);

static const char loc_gtag = 'G'; // graph location information tag

#pragma pack(pop)
#endif // __MOVES_HPP

