"""
summary: react to decompiler events/notifications

description:
  Shows how to hook to many notifications sent by the decompiler.

  This plugin doesn't really accomplish anything: it just prints
  the parameters.

  The list of notifications handled below should be exhaustive,
  and is there to hint at what is possible to accomplish by
  subclassing `ida_hexrays.Hexrays_Hooks`

see_also: curpos_details

level: intermediate
"""

import inspect

import ida_idaapi
import ida_typeinf
import ida_hexrays

class vds_hooks_t(ida_hexrays.Hexrays_Hooks):
    def __init__(self):
        ida_hexrays.Hexrays_Hooks.__init__(self)
        self.display_shortened_cfuncs = False
        self.display_vdui_curpos = False
        self.inhibit_log = 0;

    def _format_lvar(self, v):
        parts = []
        if v:
            if v.name:
                parts.append("name=%s" % v.name)
            if v.cmt:
                parts.append("cmt=%s" % v.cmt)
            parts.append("width=%s" % v.width)
            parts.append("defblk=%s" % v.defblk)
            parts.append("divisor=%s" % v.divisor)
        return "{%s}" % ", ".join(parts)

    def _format_vdui_curpos(self, v):
        return "cpos={lnnum=%d, x=%d, y=%d}" % (v.cpos.lnnum, v.cpos.x, v.cpos.y)

    def _format_value(self, v):
        if isinstance(v, ida_hexrays.lvar_t):
            v = self._format_lvar(v)
        elif isinstance(v, ida_hexrays.cfunc_t):
            if self.display_shortened_cfuncs:
                self.inhibit_log += 1
                v = str(v)
                if len(v) > 20:
                    v = v[0:20] + "[...snipped...]"
                self.inhibit_log -= 1
            else:
                v = "<cfunc>" # cannot print contents: we'll end up being called recursively
        elif isinstance(v, ida_hexrays.vdui_t) and self.display_vdui_curpos:
            v = str(v) + " " + self._format_vdui_curpos(v)
        return str(v)

    def _log(self):
        if self.inhibit_log <= 0:
            stack = inspect.stack()
            frame, _, _, _, _, _ = stack[1]
            args, _, _, values = inspect.getargvalues(frame)
            method_name = inspect.getframeinfo(frame)[2]
            argstrs = []
            for arg in args[1:]:
                argstrs.append("%s=%s" % (arg, self._format_value(values[arg])))
            print("### %s: %s" % (method_name, ", ".join(argstrs)))
        return 0

    def flowchart(self, fc, mba, reachable_blocks, decomp_flags):
        return self._log()

    def stkpnts(self, mba, stkpnts):
        return self._log()

    def prolog(self, mba, fc, reachable_blocks, decomp_flags):
        return self._log()

    def mba_maturity(self, mba, reqmat):
        return self._log()

    def microcode(self, mba):
        return self._log()

    def preoptimized(self, mba):
        return self._log()

    def locopt(self, mba):
        return self._log()

    def prealloc(self, mba):
        return self._log()

    def glbopt(self, mba):
        return self._log()

    def begin_structural(self, out, cfunc, ctrl_graph):
        return self._log()

    def structural(self, ctrl_graph):
        return self._log()

    def maturity(self, cfunc, maturity):
        return self._log()

    def interr(self, code):
        return self._log()

    def combine(self, blk, insn):
        return self._log()

    def print_func(self, cfunc, printer):
        return self._log()

    def func_printed(self, cfunc):
        return self._log()

    def resolve_stkaddrs(self, mba):
        return self._log()

    def open_pseudocode(self, vu):
        return self._log()

    def switch_pseudocode(self, vu):
        return self._log()

    def refresh_pseudocode(self, vu):
        return self._log()

    def close_pseudocode(self, vu):
        return self._log()

    def keyboard(self, vu, key_code, shift_state):
        return self._log()

    def right_click(self, vu):
        return self._log()

    def double_click(self, vu, shift_state):
        return self._log()

    def curpos(self, vu):
        return self._log()

    def create_hint(self, vu):
        return self._log()

    def text_ready(self, vu):
        return self._log()

    def populating_popup(self, widget, popup, vu):
        return self._log()

    def lvar_name_changed(self, vu, v, name, is_user_name):
        return self._log()

    def lvar_type_changed(self, vu, v, tif):
        return self._log()

    def lvar_cmt_changed(self, vu, v, cmt):
        return self._log()

    def lvar_mapping_changed(self, vu, _from, to):
        return self._log()

    def cmt_changed(self, cfunc, loc, cmt):
        return self._log()

    def build_callinfo(self, blk, type):
        return self._log()

    def callinfo_built(self, blk):
        return self._log()

    def calls_done(self, mba):
        return self._log()

    def begin_inlining(self, cdg, flags):
        return self._log()

    def inlining_func(self, cdg, blk, mbr):
        return self._log()

    def inlined_func(self, cdg, blk, mbr, i1, i2):
        return self._log()

    def pre_structural(self, ctrl_graph, cfunc, cfg):
        return self._log()

    def collect_warnings(self, cfunc):
        return self._log()

vds_hooks = vds_hooks_t()
vds_hooks.hook()
