package ch.uzh.ifi.ce.cabne.BR;

import java.util.List;
import java.util.NavigableMap;
import java.util.TreeMap;

import ch.uzh.ifi.ce.cabne.algorithm.BNESolverContext;
import ch.uzh.ifi.ce.cabne.helpers.UtilityHelpers;
import ch.uzh.ifi.ce.cabne.pointwiseBR.Optimizer;
import ch.uzh.ifi.ce.cabne.strategy.Strategy;
import ch.uzh.ifi.ce.cabne.strategy.UnivariatePWLStrategy;

// This class takes an arbitrary 1d strategy profile and computes PWL BR's, where control points are placed adaptively to
// find kinks in the function.


public class AdaptivePWLBRCalculator implements BRCalculator<Double, Double> {

    private InterpolationPointGenerator<Double, Double> pointGenerator = new UnivariateIterativeInterpolation();
	BNESolverContext<Double, Double> context;
	
	
	public AdaptivePWLBRCalculator(BNESolverContext<Double, Double> context) {
		this.context = context;
	}


	public Result<Double, Double> computeBR(int i, List<Strategy<Double, Double>> s) {
		int nPoints = Integer.parseInt(context.config.get("adaptivegridsize"));
		
		NavigableMap<Double, Double> pointwiseBRs = new TreeMap<>();
        NavigableMap<Double, Double> undampenedPointwiseBRs = new TreeMap<>();
		double epsilonAbs = 0.0;
		double epsilonRel = 0.0;
		
		double vmin = 0.0;
        double vmax = s.get(i).getMaxValue();
        while (!pointGenerator.isConverged(undampenedPointwiseBRs, vmin, vmax, nPoints)) {

            double[] nextInterpolationPoints = pointGenerator.getNextInterpolationPoints(undampenedPointwiseBRs, vmin, vmax, nPoints);
            for (double v : nextInterpolationPoints) {
            	
            	double oldbid = s.get(i).getBid(v);
            	Optimizer.Result<Double> result = context.optimizer.findBR(i, v, oldbid, s);

    			Double newbid = context.updateRule.update(v, oldbid, result.bid, result.oldutility, result.utility);
                pointwiseBRs.put(v, newbid);
                undampenedPointwiseBRs.put(v, result.bid);

    			epsilonAbs = Math.max(epsilonAbs, UtilityHelpers.absoluteLoss(result.oldutility, result.utility));
    			epsilonRel = Math.max(epsilonRel, UtilityHelpers.relativeLoss(result.oldutility, result.utility));
            }


        }
		
		return new Result<Double, Double>(new UnivariatePWLStrategy(pointwiseBRs), epsilonAbs, epsilonRel);
	}

}
