# -*- coding: utf-8 -*-

from __future__ import unicode_literals
from __future__ import print_function
from __future__ import division
from __future__ import absolute_import

from ....utils.ida_wrapper import idaapi
from ....utils.ida_wrapper import idc
from .instruction import Instruction
from .... import models


class Function(models.Function):

    def __init__(self, address):
        self._address = address
        self._func_t
        self.file_path = idc.GetInputFilePath()
        self.file_hash = idc.GetInputMD5()

    @property
    def _func_t(self):
        func_t = idaapi.get_func(self._address)
        if not func_t:
            raise ValueError(
                "Can't find any function at 0x{:X}".format(self._address))
        return func_t

    @property
    def start_address(self):
        return self._func_t.startEA

    @property
    def end_address(self):
        return self._func_t.endEA

    def __len__(self):
        return self.size

    @property
    def size(self):
        """ Return the size in bytes of the area
        """
        return self._func_t.size()

    @property
    def name(self):
        name = idaapi.demangle_name(idaapi.get_func_name(self.start_address),
            idc.INF_SHORT_DN)
        if not name:
            name = idaapi.get_func_name(self.start_address)
        if not name:
            raise ValueError("{!r} Can't get the name".format(self))
        return name.decode()

    @property
    def instructions(self):
        fi_iter = idaapi.func_item_iterator_t()
        ok = fi_iter.set(self._func_t)
        # FIXME: Improve error check
        assert ok
        ok = fi_iter.first()
        # FIXME: Improve error check
        assert ok
        while ok:
            yield Instruction(fi_iter.current())
            ok = fi_iter.next_code()

    @property
    def instructions_reversed(self):
        fi_iter = idaapi.func_item_iterator_t()
        ok = fi_iter.set(self._func_t)
        # FIXME: Improve error check
        assert ok
        ok = fi_iter.last()
        # FIXME: Improve error check
        assert ok
        ok = fi_iter.prev_code()
        # FIXME: Improve error check
        assert ok
        while ok:
            yield Instruction(fi_iter.current())
            ok = fi_iter.prev_code()

    @property
    def is_library(self):
        return self._func_t.flags & idc.FUNC_LIB != 0

    @property
    def is_thunk(self):
        return self._func_t.flags & idc.FUNC_THUNK != 0

    @property
    def is_public(self):
        return idaapi.is_public_name(self.start_address)

    @property
    def is_extern(self):
        return idaapi.getseg(self.start_address).type == idaapi.SEG_XTRN

