import json
from mythril.ether.soliditycontract import SolidityContract

from mythril.laser.ethereum.svm import GlobalState, MachineState
from mythril.laser.ethereum import svm
from tests import *


SHA256_TEST = [ (0,False) for i in range(6)]

RIPEMD160_TEST = [ (0,False) for i in range(6)]

ECRECOVER_TEST = [ (0,False) for i in range(9)]

IDENTITY_TEST = [ (0, False) for i in range(4)]

SHA256_TEST[0] = (5555555555555555, True)     #These are Random numbers to check whether the 'if condition' is entered or not(True means entered)
SHA256_TEST[1] = (323232325445454546, True)
SHA256_TEST[2] = (34756834765834658, False)
SHA256_TEST[3] = (8756476956956795876987, True)
SHA256_TEST[4] = (5763467587689578369, True)
SHA256_TEST[5] = (948365957658767467857, False)

RIPEMD160_TEST[0] = (1242435356364, True)
RIPEMD160_TEST[1] = (6732648654386435, True)
RIPEMD160_TEST[2] = (97457657536546465, False)
RIPEMD160_TEST[3] = (56436346436456546, True)
RIPEMD160_TEST[4] = (999999999999999999993, True)
RIPEMD160_TEST[5] = (1111111111112, False)


ECRECOVER_TEST[0] = (786428768768632537676, True)
ECRECOVER_TEST[1] = (4897983476979346779638, False)
ECRECOVER_TEST[2] = (674837568743979857398564869, True)
ECRECOVER_TEST[3] = (3487683476979311, False)
ECRECOVER_TEST[4] = (853729594875984769847369, True)
ECRECOVER_TEST[5] = (83579382475972439587, False)
ECRECOVER_TEST[6] = (8437589437695876985769, True)
ECRECOVER_TEST[7] = (9486794873598347697596, False)
ECRECOVER_TEST[8] = (346934876983476, True)

IDENTITY_TEST[0] = (87426857369875698, True)
IDENTITY_TEST[1] = (476934798798347, False)

IDENTITY_TEST[2] = (7346948379483769, True)
IDENTITY_TEST[3] = (83269476937987, False)

def _all_info(laser):
    accounts = {}
    for address, _account in laser.accounts.items():
        account = _account.as_dict()
        account["code"] = account["code"].instruction_list
        account['balance'] = str(account['balance'])
        accounts[address] = account

    nodes = {}
    for uid, node in laser.nodes.items():
        states = []
        for state in node.states:
            if isinstance(state, MachineState):
                states.append(state.as_dict())
            elif isinstance(state, GlobalState):
                environment = state.environment.as_dict()
                environment["active_account"] = environment["active_account"].address
                states.append({
                    'accounts': state.accounts.keys(),
                    'environment': environment,
                    'mstate': state.mstate.as_dict()
                })

        nodes[uid] = {
            'uid': node.uid,
            'contract_name': node.contract_name,
            'start_addr': node.start_addr,
            'states': states,
            'constraints': node.constraints,
            'function_name': node.function_name,
            'flags': str(node.flags)
        }

    edges = [edge.as_dict() for edge in laser.edges]

    return {
        'accounts': accounts,
        'nodes': nodes,
        'edges': edges,
        'total_states': laser.total_states,
        'max_depth': laser.max_depth
    }

def _test_natives(laser_info, test_list, test_name):
    success = 0
    for i,j in test_list:
        if (str(i) in laser_info) == j:
            success+=1
        else:
            print ("Failed: "+str(i)+" "+str(j))
    assert(success == len(test_list))



class NativeTests(BaseTestCase):
    def runTest(self):
        disassembly = SolidityContract('./tests/native_tests.sol').disassembly
        account = svm.Account("0x0000000000000000000000000000000000000000", disassembly)
        accounts = {account.address: account}

        laser = svm.LaserEVM(accounts, max_depth = 100)
        laser.sym_exec(account.address)
        laser_info = str(_all_info(laser))
        print('\n')

        _test_natives(laser_info, SHA256_TEST, 'SHA256')
        _test_natives(laser_info, RIPEMD160_TEST, 'RIPEMD160')
        _test_natives(laser_info, ECRECOVER_TEST, 'ECRECOVER')
        _test_natives(laser_info, IDENTITY_TEST, 'IDENTITY')

