package org.ethereum.core;

import org.ethereum.vm.DataWord;
import org.ethereum.vm.LogInfo;
import org.ethereum.vm.program.InternalTransaction;
import org.springframework.util.Assert;

import java.math.BigInteger;
import java.util.List;
import java.util.Map;

import static java.util.Collections.*;
import static org.ethereum.util.BIUtil.toBI;

public class TransactionExecutionSummary {

    private byte[] transactionHash;
    private BigInteger value = BigInteger.ZERO;
    private BigInteger gasPrice = BigInteger.ZERO;
    private BigInteger gasLimit = BigInteger.ZERO;
    private BigInteger gasUsed = BigInteger.ZERO;
    private BigInteger gasLeftover = BigInteger.ZERO;
    private BigInteger gasRefund = BigInteger.ZERO;

    private List<DataWord> deletedAccounts = emptyList();
    private List<InternalTransaction> internalTransactions = emptyList();
    private Map<DataWord, DataWord> storageDiff = emptyMap();

    private byte[] result;
    private List<LogInfo> logs;

    private boolean failed;

    public byte[] getTransactionHash() {
        return transactionHash;
    }

    private BigInteger calcCost(BigInteger gas) {
        return gasPrice.multiply(gas);
    }

    public BigInteger getFee() {
        return calcCost(gasLimit.subtract(gasLeftover.add(gasRefund)));
    }

    public BigInteger getRefund() {
        return calcCost(gasRefund);
    }

    public BigInteger getLeftover() {
        return calcCost(gasLeftover);
    }

    public BigInteger getGasPrice() {
        return gasPrice;
    }

    public BigInteger getGasLimit() {
        return gasLimit;
    }

    public BigInteger getGasUsed() {
        return gasUsed;
    }

    public BigInteger getGasLeftover() {
        return gasLeftover;
    }

    public BigInteger getValue() {
        return value;
    }

    public List<DataWord> getDeletedAccounts() {
        return deletedAccounts;
    }

    public List<InternalTransaction> getInternalTransactions() {
        return internalTransactions;
    }

    public Map<DataWord, DataWord> getStorageDiff() {
        return storageDiff;
    }

    public BigInteger getGasRefund() {
        return gasRefund;
    }

    public boolean isFailed() {
        return failed;
    }

    public byte[] getResult() {
        return result;
    }

    public List<LogInfo> getLogs() {
        return logs;
    }

    public static Builder builderFor(Transaction transaction) {
        return new Builder(transaction);
    }

    public static class Builder {

        private final TransactionExecutionSummary summary;

        Builder(Transaction transaction) {
            Assert.notNull(transaction, "Cannot build TransactionExecutionSummary for null transaction.");

            summary = new TransactionExecutionSummary();
            summary.transactionHash = transaction.getHash();
            summary.gasLimit = toBI(transaction.getGasLimit());
            summary.gasPrice = toBI(transaction.getGasPrice());
            summary.value = toBI(transaction.getValue());
        }

        public Builder gasUsed(BigInteger gasUsed) {
            summary.gasUsed = gasUsed;
            return this;
        }

        public Builder gasLeftover(BigInteger gasLeftover) {
            summary.gasLeftover = gasLeftover;
            return this;
        }

        public Builder gasRefund(BigInteger gasRefund) {
            summary.gasRefund = gasRefund;
            return this;
        }

        public Builder internalTransactions(List<InternalTransaction> internalTransactions) {
            summary.internalTransactions = unmodifiableList(internalTransactions);
            return this;
        }

        public Builder deletedAccounts(List<DataWord> deletedAccounts) {
            summary.deletedAccounts = unmodifiableList(deletedAccounts);
            return this;
        }

        public Builder storageDiff(Map<DataWord, DataWord> storageDiff) {
            summary.storageDiff = unmodifiableMap(storageDiff);
            return this;
        }

        public Builder markAsFailed() {
            summary.failed = true;
            return this;
        }

        public Builder logs(List<LogInfo> logs) {
            summary.logs = logs;
            return this;
        }

        public Builder result(byte[] result) {
            summary.result = result;
            return this;
        }

        public TransactionExecutionSummary build() {
            if (summary.failed) {
                for (InternalTransaction transaction : summary.internalTransactions) {
                    transaction.reject();
                }
            }
            return summary;
        }
    }
}

@@ -399,16 +399,9 @@
 ate 
-byte%5B%5D t
+T
 rans
@@ -402,28 +402,27 @@
  Transaction
-Hash
+ tx
 ;%0A    privat
@@ -1002,16 +1002,84 @@
 ailed;%0A%0A
+    public Transaction getTransaction() %7B%0A        return tx;%0A    %7D%0A%0A
     publ
@@ -1126,17 +1126,20 @@
  return 
-t
+getT
 ransacti
@@ -1140,20 +1140,29 @@
 nsaction
+().get
 Hash
+() 
 ;%0A    %7D%0A
@@ -2863,30 +2863,17 @@
 ummary.t
-ransactionHash
+x
  = trans
@@ -2878,26 +2878,16 @@
 nsaction
-.getHash()
 ;%0A      

Add Transaction to TransactionExecutionSummary