#!/usr/bin/env python
# -*- coding: utf-8 -*-

import lief
import unittest
import logging
import tempfile
import shutil
import os
import sys
import stat
import subprocess
import time
import ctypes
import zipfile
import json
import re

from subprocess import Popen

from unittest import TestCase
from utils import get_sample

class TestPe(TestCase):
    def setUp(self):
        self.logger = logging.getLogger(__name__)
        self.maxDiff = None

        self.tmp_dir = tempfile.mkdtemp(suffix='_lief_tests')
        self.logger.debug("temp dir: {}".format(self.tmp_dir))


        if sys.platform.startswith("win"):
            SEM_NOGPFAULTERRORBOX = 0x0002 # From MSDN
            ctypes.windll.kernel32.SetErrorMode(SEM_NOGPFAULTERRORBOX);


    def test_code_view_pdb(self):
        path = get_sample('PE/PE64_x86-64_binary_ConsoleApplication1.exe')
        sample = lief.parse(path)

        self.assertTrue(sample.has_debug)

        debug_code_view = list(filter(lambda deb: deb.has_code_view, sample.debug))
        self.assertTrue(len(debug_code_view) == 1)

        debug = debug_code_view[0]
        code_view = debug.code_view

        self.assertEqual(code_view.cv_signature, lief.PE.CODE_VIEW_SIGNATURES.PDB_70)
        self.assertEqual(code_view.signature, [245, 217, 227, 182, 71, 113, 1, 79, 162, 3, 170, 71, 124, 74, 186, 84])
        self.assertEqual(code_view.age, 1)
        self.assertEqual(code_view.filename, r"c:\users\romain\documents\visual studio 2015\Projects\HelloWorld\x64\Release\ConsoleApplication1.pdb")

        json_view = json.loads(lief.to_json(debug))
        self.assertDictEqual(json_view, {
            'addressof_rawdata': 8996,
            'characteristics': 0,
            'code_view': {
                'age': 1,
                'cv_signature': 'PDB_70',
                'filename': 'c:\\users\\romain\\documents\\visual studio 2015\\Projects\\HelloWorld\\x64\\Release\\ConsoleApplication1.pdb',
                'signature': [245, 217, 227, 182, 71, 113, 1, 79, 162, 3, 170, 71, 124, 74, 186, 84]
            },
            'major_version': 0,
            'minor_version': 0,
            'pointerto_rawdata': 5412,
            'sizeof_data': 125,
            'timestamp': 1459952944,
            'type': 'CODEVIEW'
        })

    def test_remove_section(self):
        path = get_sample('PE/PE64_x86-64_remove_section.exe')
        sample = lief.parse(path)

        output = os.path.join(self.tmp_dir, "section_removed.exe")

        sample.remove_section("lief")
        sample.write(output)

        st = os.stat(output)
        os.chmod(output, st.st_mode | stat.S_IEXEC)

        if sys.platform.startswith("win"):
            subprocess_flags = 0x8000000 # win32con.CREATE_NO_WINDOW?
            p = Popen([output], shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, creationflags=subprocess_flags)

            stdout, _ = p.communicate()
            stdout = stdout.decode("utf8")
            self.logger.debug(stdout)
            self.assertIn("Hello World", stdout)

    def test_unwind(self):

        path = get_sample("PE/PE64_x86-64_binary_cmd.exe")
        sample = lief.parse(path)

        functions = sorted(sample.functions, key=lambda f: f.address)

        self.assertEqual(len(functions), 829)

        self.assertEqual(functions[0].address, 4160)
        self.assertEqual(functions[0].size,    107)
        self.assertEqual(functions[0].name,    "")

        self.assertEqual(functions[-1].address, 163896)
        self.assertEqual(functions[-1].size,    54)
        self.assertEqual(functions[-1].name,    "")

    def test_pgo(self):
        path   = get_sample("PE/PE32_x86_binary_PGO-LTCG.exe")
        sample = lief.parse(path)

        debugs = sample.debug
        self.assertEqual(len(debugs), 3)

        debug_entry = debugs[2]

        self.assertTrue(debug_entry.has_pogo)
        pogo = debug_entry.pogo
        self.assertEqual(pogo.signature, lief.PE.POGO_SIGNATURES.LCTG)

        pogo_entries = pogo.entries
        self.assertEqual(len(pogo_entries), 33)

        self.assertEqual(pogo_entries[23].name,      ".xdata$x")
        self.assertEqual(pogo_entries[23].start_rva, 0x8200)
        self.assertEqual(pogo_entries[23].size,      820)



    def tearDown(self):
        # Delete it
        try:
            if os.path.isdir(self.tmp_dir):
                shutil.rmtree(self.tmp_dir)
        except Exception as e:
            self.logger.error(e)








if __name__ == '__main__':

    root_logger = logging.getLogger()
    root_logger.setLevel(logging.DEBUG)

    ch = logging.StreamHandler()
    ch.setLevel(logging.DEBUG)
    root_logger.addHandler(ch)

    unittest.main(verbosity=2)

