package jelectrum;

import java.util.TreeMap;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.HashSet;


import org.bitcoinj.core.Sha256Hash;
import org.bitcoinj.core.Address;
import org.bitcoinj.core.Transaction;
import org.bitcoinj.core.TransactionInput;
import org.bitcoinj.core.TransactionOutput;

import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.protobuf.ByteString;

public class TransactionSummary implements java.io.Serializable
{
  private static final long serialVersionUID = 5333844648311484891L;
  private Sha256Hash tx_hash;
  private TreeMap<Integer, TransactionOutSummary> outs;
  private TreeMap<Integer, TransactionInSummary> ins;
  private HashSet<ByteString> involved_scripthashes;
  private long fee;
  private int size;

  public TransactionSummary(Transaction tx, TXUtil tx_util, boolean confirmed, Map<Sha256Hash, Transaction> block_tx_map)
  {
    tx_hash = tx.getHash();
    involved_scripthashes = new HashSet<>();
    ins = new TreeMap<>();
    outs = new TreeMap<>();

    long total_out=0L;
    long total_in=0L;

    for(TransactionOutput tx_out : tx.getOutputs())
    {
      TransactionOutSummary os = new TransactionOutSummary(tx_out, tx_util);

      outs.put( tx_out.getIndex(), os );
      involved_scripthashes.add(os.getScriptHash());
      total_out += os.getValue();
    }
    int idx=0;
    for(TransactionInput tx_in : tx.getInputs())
    {
      TransactionInSummary is = new TransactionInSummary(tx_in, tx_util, confirmed, block_tx_map);

      ByteString addr = is.getScriptHash();
      if (addr != null) involved_scripthashes.add(addr);

      ins.put(idx, is);  
      idx++;
      total_in += is.getValue();
    }
    if (tx.isCoinBase())
    {
      fee = 0L;
    }
    else
    {
      fee = total_in - total_out;
    }
    size = tx.getOptimalEncodingMessageSize();
  }

  public Sha256Hash getHash() { return tx_hash; }
  public Map<Integer, TransactionOutSummary> getOutputs(){ return ImmutableMap.copyOf(outs); }
  public Map<Integer, TransactionInSummary> getInputs(){ return ImmutableMap.copyOf(ins); }
  public Set<ByteString> getScriptHashes(){ return ImmutableSet.copyOf(involved_scripthashes); }
  public long getFee(){return fee; }
  public int getSize(){return size;}

  public String toString()
  {
    StringBuilder sb = new StringBuilder();
    sb.append("TX Summary: " + getHash()); sb.append('\n');
    sb.append("  IN: " + getInputs().toString()); sb.append('\n');
    sb.append("  OUT: " + getOutputs().toString()); sb.append('\n');
    sb.append("  ADDRESSES: " + getScriptHashes().toString()); sb.append('\n');

    return sb.toString();

  }

  public static class TransactionOutSummary implements java.io.Serializable
  {
    private long value;
    private ByteString scripthash;
        
    public TransactionOutSummary(TransactionOutput tx_out, TXUtil tx_util)
    {
      value = tx_out.getValue().getValue();

      scripthash = tx_util.getScriptHashForOutput(tx_out);

    }
    public ByteString getScriptHash()
    {
      return scripthash;
    }
    public long getValue()
    {
      return value;
    }
    public String toString()
    {
      return "" + scripthash + ":" + value;
    }

  }

  public class TransactionInSummary implements java.io.Serializable
  {
    private Sha256Hash tx_out_hash;
    private int tx_out_index;
    private boolean is_coinbase;
    private ByteString scripthash;
    private long value;

    public TransactionInSummary(TransactionInput tx_in, TXUtil tx_util, boolean confirmed, Map<Sha256Hash, Transaction> block_tx_map)
    {
      if (tx_in.isCoinBase())
      {
        is_coinbase=true;
        return;
      }


      tx_out_index = (int) tx_in.getOutpoint().getIndex();
      tx_out_hash = tx_in.getOutpoint().getHash();

      scripthash = tx_util.getScriptHashForInput(tx_in, confirmed, block_tx_map);

      value = tx_util.getValueForInput(tx_in, confirmed, block_tx_map);

    }

    public Sha256Hash getTxOutHash(){return tx_out_hash;}
    public int getTxOutIndex(){return tx_out_index;}
    public boolean isCoinbase(){return is_coinbase;}
    public ByteString getScriptHash(){return scripthash;}
    public long getValue(){ return value;}

    public String toString()
    {
      if (is_coinbase) return "coinbase";

      return  ""+scripthash + " " + tx_out_hash + ":" + tx_out_index;
    }
  }

}