/* **********************************
 *   DerivativesFinder.java
 *     DerivativesFinder
 ************************************/
package utils;

import utils.comparator.SameDerivativeByType;
import edu.neu.ccs.demeterf.TUCombiner;
import edu.neu.ccs.demeterf.demfgen.lib.List;
import gen.*;


/** Class for finding derivatives for various conditions */
public class DerivativesFinder{
    /** Returns a list of types of derivatives that exist in store */
    public static List<Type> findExistingDerTypes(List<Pair<PlayerID, PlayerStore>> store) {
        return TUCombiner.traverse(store, new DerivativeTypes());
    }

    /** Returns a list of the un finished derivatives in the store */
    public static List<Derivative> findActiveDers(List<Pair<PlayerID, PlayerStore>> store) {
        return TUCombiner.traverse(store, new ActiveDerivatives());
    }

    /** Returns a list of derivatives that need raw material */
    public static List<Derivative> findDersThatNeedRM(List<Pair<PlayerID, PlayerStore>> store, Player player) {
        return TUCombiner.traverse(store, new DerivativesThatNeedRM(player.id.id));
    }

    /** Returns a list of derivatives that need finishing */
    public static List<Derivative> findDersThatNeedFinishing(List<Pair<PlayerID, PlayerStore>> store, Player player) {
        return TUCombiner.traverse(store, new DerivativesThatNeedFinishing(player.id.id));
    }

    /** Returns a list of derivatives that are available for sale */
    public static List<Derivative> findDerivativesForSale(List<Pair<PlayerID, PlayerStore>> store) {
        return TUCombiner.traverse(store, new DerivativesForSale());
    }
    
    /** Returns a list of derivatives that are available for sale
     *  By someone other than playerId. */
    public static List<Derivative> findDerivativesForSaleByOthers(List<Pair<PlayerID, PlayerStore>> store, PlayerID playerId){
        return TUCombiner.traverse(store, new DerivsFromOthers(playerId));
    }

    /** Returns a list of unique derivatives from a list of derivatives */
    public static List<Derivative> findUniqueDerivatives(List<Derivative> derivatives){
        List<Derivative> uniqueDers = List.create();
        for (Derivative der: derivatives){
            if(!uniqueDers.contains(new SameDerivativeByType(der)))
                    uniqueDers = uniqueDers.push(der);
            else{
                if(der.price.val<uniqueDers.find(new SameDerivativeByType(der)).price.val){
                    uniqueDers = uniqueDers.replace(new SameDerivativeByType(der), der);
                }
            }
        }
        return uniqueDers;
    }
}    

/** Class for traversal */
class DerivativesForSale extends ListTUCombiner<Derivative>{
    List<Derivative> combine(Derivative der){ return List.create(der); }
    List<Derivative> combine(PlayerStore pStore,  List<Derivative> sDeriv, List<Derivative> bDeriv){
        return sDeriv;
    }
}

/** Class for traversal */
class DerivsFromOthers extends DerivativesForSale{
    PlayerID playerId;
    public DerivsFromOthers(Player player){ this(player.id); }
    public DerivsFromOthers(PlayerID pid){ playerId = pid; }
    
    List<Derivative> combine(Pair<PlayerID, PlayerStore> store){
        if(playerId.equals(store.a))
            return List.create();
        else
            return store.b.forSale;
    }
}

/** Class for traversal */
class DerivativesThatNeedRM extends ListTUCombiner<Derivative>{
    int playerId;
    public DerivativesThatNeedRM(int pid){ playerId = pid; }
    
    List<Derivative> combine(Derivative der){
        if((der.seller.id==this.playerId)&&(der.optbuyer.isSome())&&(!der.optraw.isSome()))
            return List.create(der);  
        else
            return List.create();
    }
    
    @SuppressWarnings("unchecked")
	public List<Derivative> combine(List l){
        List<Derivative> answer = this.combine();
        for(Object o : l)
            answer = this.fold(answer, TUCombiner.traverse(o, this));
        return answer;
    }
}

/** Class for traversal */
class DerivativesThatNeedFinishing extends ListTUCombiner<Derivative>{
    int playerId;
    public DerivativesThatNeedFinishing(int pid){ playerId = pid; }
    
    List<Derivative> combine(Derivative der){
        if(der.optbuyer.isSome() && der.optraw.isSome() && !der.optfinished.isSome()){
            if(der.optbuyer.inner().id == this.playerId)
                return List.create(der);
        }
        return List.create();
    }
}

/** Class for traversal */
class ActiveDerivatives extends ListTUCombiner<Derivative>{
    List<Derivative> combine(Derivative der){
        if(!der.optfinished.isSome())
            return List.create(der);
        return List.create();
    }
}


/** Class for traversal */
class DerivativeTypes extends ListTUCombiner<Type>{
    List<Type> combine(Type type){ return List.create(type); }
}