package org.ethereum.jsontestsuite.validators;

import org.ethereum.core.AccountState;
import org.ethereum.db.ContractDetails;
import org.ethereum.vm.DataWord;
import org.spongycastle.util.encoders.Hex;

import java.math.BigInteger;
import java.util.*;

public class AccountValidator {


    public static List<String> valid(String address, AccountState expectedState, ContractDetails expectedDetails,
                             AccountState currentState, ContractDetails currentDetails){

        List<String> results = new ArrayList<>();

        if (currentState == null || currentDetails == null){
            String formattedString = String.format("Account: %s: expected but doesn't exist",
                    address);
            results.add(formattedString);
            return results;
        }

        if (expectedState == null || expectedDetails == null){
            String formattedString = String.format("Account: %s: unexpected account in the repository",
                    address);
            results.add(formattedString);
            return results;
        }


        BigInteger expectedBalance = expectedState.getBalance();
        if (currentState.getBalance().compareTo(expectedBalance) != 0) {
            String formattedString = String.format("Account: %s: has unexpected balance, expected balance: %s found balance: %s",
                    address, expectedBalance.toString(), currentState.getBalance().toString());
            results.add(formattedString);
        }

        BigInteger expectedNonce = expectedState.getNonce();
        if (currentState.getNonce().compareTo(expectedNonce) != 0) {
            String formattedString = String.format("Account: %s: has unexpected nonce, expected nonce: %s found nonce: %s",
                    address, expectedNonce.toString(), currentState.getNonce().toString());
            results.add(formattedString);
        }

        if (!Arrays.equals(expectedDetails.getCode(), currentDetails.getCode())) {
            String formattedString = String.format("Account: %s: has unexpected code, expected code: %s found code: %s",
                    address, Hex.toHexString(expectedDetails.getCode()), Hex.toHexString(currentDetails.getCode()));
            results.add(formattedString);
        }


        // compare storage
        Set<DataWord> currentKeys = currentDetails.getStorage().keySet();
        Set<DataWord> expectedKeys = expectedDetails.getStorage().keySet();
        Set<DataWord> checked = new HashSet<>();

        for (DataWord key : currentKeys) {

            DataWord currentValue = currentDetails.getStorage().get(key);
            DataWord expectedValue = expectedDetails.getStorage().get(key);
            if (expectedValue == null) {

                String formattedString = String.format("Account: %s: has unexpected storage data: %s = %s",
                        address,
                        key,
                        currentValue);

                results.add(formattedString);
                continue;
            }

            if (!expectedValue.equals(currentValue)) {

                String formattedString = String.format("Account: %s: has unexpected value, for key: %s , expectedValue: %s real value: %s",
                        address,
                        key.toString(),
                        expectedValue.toString(), currentValue.toString());
                results.add(formattedString);
                continue;
            }

            checked.add(key);
        }

        for (DataWord key : expectedKeys) {
            if (!checked.contains(key)) {
                String formattedString = String.format("Account: %s: doesn't exist expected storage key: %s",
                        address, key.toString());
                results.add(formattedString);
            }
        }

        return results;
    }
}

@@ -247,16 +247,77 @@
 til.*;%0A%0A
+import static org.ethereum.crypto.HashUtil.EMPTY_DATA_HASH;%0A%0A
 public c
@@ -1962,24 +1962,163 @@
  %7D%0A%0A        
+byte%5B%5D code = Arrays.equals(currentState.getCodeHash(), EMPTY_DATA_HASH) ?%0A                new byte%5B0%5D : currentDetails.getCode();%0A        
 if (!Arrays.
@@ -2136,34 +2136,8 @@
 cted
-Details.getCode(), current
 Deta
@@ -2149,16 +2149,22 @@
 etCode()
+, code
 )) %7B%0A   

Fix contract code comparison in JSON test suite: should take into account codeHash of the state