"""
IDA Python Plugin to get information about functions from BAP into IDA.

Finds all the locations in the executable that BAP knows to be functions and
marks them as such in IDA.

Keybindings:
    Shift-P : Run BAP and mark code as functions in IDA
"""
import idaapi
import idc

from heapq import heappush, heappop
from bap.utils.run import BapIda


class FunctionFinder(BapIda):
    def __init__(self):
        super(FunctionFinder, self).__init__(symbols=False)
        self.action = 'looking for function starts'
        self.syms = self.tmpfile('syms', mode='r')
        self.args += [
            '--print-symbol-format', 'addr',
            '--dump', 'symbols:{0}'.format(self.syms.name)
        ]

        # we can be a little bit more promiscuous since IDA will ignore
        # function starts that occur in the middle of a function
        if 'byteweight' in self.plugins and not \
           '--no-byteweight' in self.args:
            self.args += [
                '--byteweight-threshold', '0.5',
                '--byteweight-length', '4',
            ]


class BAP_Functions(idaapi.plugin_t):
    """Uses BAP to find missed functions"""

    flags = idaapi.PLUGIN_FIX
    comment = "BAP Functions Plugin"
    help = "BAP Functions Plugin"
    wanted_name = "BAP: Discover functions"
    wanted_hotkey = "Shift-P"

    def mark_functions(self):
        """Run BAP, get functions, and mark them in IDA."""
        analysis = FunctionFinder()
        analysis.on_finish(lambda x: self.add_starts(x))
        analysis.run()

    def add_starts(self, bap):
        syms = []
        for line in bap.syms:
            heappush(syms, int(line, 16))
        for i in range(len(syms)):
            idaapi.add_func(heappop(syms), idaapi.BADADDR)
        idc.Refresh()
        idaapi.refresh_idaview_anyway()

    def init(self):
        """Initialize Plugin."""
        return idaapi.PLUGIN_KEEP

    def term(self):
        """Terminate Plugin."""
        pass

    def run(self, arg):
        self.mark_functions()


def PLUGIN_ENTRY():
    """Install BAP_Functions upon entry."""
    return BAP_Functions()