/* **********************************
 *   AccountUpdater.java
 *     AccountUpdater
 * **********************************/
package sdg.admin.utils;

import edu.neu.ccs.demeterf.*;
import edu.neu.ccs.demeterf.demfgen.lib.List;
import gen.*;
import sdg.local.utils.ComputeQuality;
import sdg.local.utils.DerivativesFinder;
import sdg.local.utils.ListTUCombiner;

/** Class for updating the accounts */
public class AccountUpdater{

    /** Returns a list of new accounts that are updated after a round */
    public static Accounts updateAccounts(Accounts old, Store store, PlayerTransaction trans){
        List<Pair<PlayerID, Double>> change = changes(trans.transactions, store, trans.player);
        return change.fold(new List.Fold<Pair<PlayerID,Double>, Accounts>(){
            public Accounts fold(Pair<PlayerID,Double> ch, Accounts accs){ return accs.update(ch); }
        }, old);
    }
    /** Simple helper method... */
    static List<Pair<PlayerID, Double>> changes(List<Transaction> trans, Store s, Player p){
        return TUCombiner.traverse(trans, new AccountChanges(s, p),
                Control.builtins(Transaction.class,Derivative.class));
    }
    /** Create a Pair for the Store/Accounts */
    static Pair<PlayerID,Double> pair(PlayerID pid, Double d){ return new Pair<PlayerID,Double>(pid,d); }

    /** Calculate the Account changes for each Transaction */
    static class AccountChanges extends ListTUCombiner<Pair<PlayerID, Double>>{
        Player player;
        Store store;
        AccountChanges(Store s, Player p){ player = p; store = s; }
        List<Pair<PlayerID, Double>> combines(Transaction trans) { return combine(); }
        
        @SuppressWarnings("unchecked")
		List<Pair<PlayerID, Double>> combine(BuyTrans trans) {
            //deduct from account of buyer, add to the account of seller
        	Derivative d = DerivativesFinder.derByName(store, trans.derivName);
            return List.create(
                    pair(player.id, -d.price.val),
                    pair(d.seller, d.price.val));
        }

        @SuppressWarnings("unchecked")
		List<Pair<PlayerID, Double>> combine(FinishTrans trans){
        	Derivative d = DerivativesFinder.derByName(store, trans.derivName);
        	if(new Classic().equals(d.type.kind))
        	{
        		//add to the account of buyer, deduct from the account of seller
        		return List.create(
                    pair(player.id, trans.finish.quality.val),
                    pair(d.seller, -trans.finish.quality.val));
        	}
        	else if(new Secret().equals(d.type.kind))
        	{
        		double sq = ComputeQuality.quality(d.optraw.inner(), 
        				d.optraw.inner().instance.secret.inner().assign.inner()).val;
        		double win = d.optfinished.inner().quality.val - sq * d.price.val;
        		double payout = 0;
        		if(win >= 0) { payout = d.price.val + win; }
        		return List.create(
        			pair(player.id, payout),
        			pair(d.seller, -payout));
        	}
        	return List.create();
        }
    }
}