package fanch.multijeu.numbers.data;

import android.util.Log;

import java.util.ArrayList;

import fanch.multijeu.common.core.Saveable;
import fanch.multijeu.common.utils.IntUtils;

public class ExpressionSetGenerator extends Saveable {

    public static final int VERY_EASY=0;
    public static final int EASY=1;
    public static final int MEDIUM=2;
    public static final int HARD=3;
    public static final int VERY_HARD=4;
    public static final int MAX_OP_FAIL =4;

    protected DifficultyAttrs mDiff[] = initDifficulty();

    protected static class DifficultyAttrs {
        int minNumberCount;
        int maxNumberCount;
        int minOp;
        int maxOp;
        int minNumberValue;
        int maxNumberValue;
        int minResultValue;
        int maxResultValue;
        int addWeight;
        int subWeight;
        int multWeight;

    }

    protected int mDifficulty;
    protected ExpressionSet mOut;
    protected ArrayList<Expression> mPossible = new ArrayList<>();
    protected int mNOp=0;

    protected DifficultyAttrs dif()
    {
        return mDiff[mDifficulty];
    }

    public ExpressionSetGenerator(int difficulty)
    {
        mDifficulty=difficulty;
    }

    public ExpressionSetGenerator()
    {
        mDifficulty=EASY;
    }


    protected boolean next()
    {
        boolean ok = false;
        Expression exp1 = nextNumber();
        Expression exp2 = nextNumber();
        OperationExpr op = genOperation();
        op.mFirst=exp1;
        op.mSecond=exp2;

        if(op.getOperande()==OperationExpr.Operation.SUB)
        {
            if(exp1.compute()<exp2.compute())
            {
                op.mSecond=exp1;
                op.mFirst=exp2;
            }
        }


        int x = op.compute();
        //if(x<=dif().maxNumberValue )
        {
            mNOp++;
            addExpression(op);
            return true;
        } /*else {
            addExpression(exp1);
            addExpression(exp2);
            return false;
        }*/
    }

    public ExpressionSet generate()
    {
        mOut = new ExpressionSet();
        final int numberCount = genNumberCount();
        final int opCount = genOpCount();
        mNOp=0;
        mPossible.clear();

        mOut.allowedNumber=new NumberExpr[numberCount];
        for(int i=0; i<numberCount; i++) {
            int x = genNumberValue();
            mOut.allowedNumber[i]=new NumberExpr(x);
            mPossible.add(new NumberExpr(x));
        }

        while(true) {
            for (int j = 0; j < MAX_OP_FAIL; j++)
                if (next()) break;

            Expression exp = findMaxOpExpression();
            if (exp != null)
            {
                int n = exp.opCount();
                if (n >= opCount && n <= dif().maxOp) {
                    mOut.resultExpr = exp;
                    mOut.resultInt = exp.compute();
                    break;
                }
            }

            if(mPossible.size()==1) {
                Log.e("/////////////", "Fail: "+ mPossible.get(0).compute()+" = "+mPossible.get(0));
                return generate();
            }
        }

        return mOut;
    }

    public ExpressionSet peek()
    {
        return mOut;
    }

    public int genNumberCount()
    {
        return IntUtils.rand(dif().minNumberCount, dif().maxNumberCount);
    }

    public int genNumberValue()
    {
        return IntUtils.rand(dif().minNumberValue, dif().maxNumberValue);
    }

    public int genResultValue()
    {
        return IntUtils.rand(dif().minResultValue, dif().maxResultValue);
    }

    public int genOpCount()
    {
        return IntUtils.rand(dif().minOp, dif().maxOp);
    }

    protected Expression nextNumber()
    {
        int i = IntUtils.rand(0, mPossible.size()-1);
        Expression x = mPossible.get(i);
        mPossible.remove(i);
        return x;
    }

    protected void addExpression(Expression n)
    {
        mPossible.add(n);
    }

    public OperationExpr genOperation()
    {
        int tot = dif().addWeight+dif().multWeight+dif().subWeight;
        int x = IntUtils.rand(0,tot-1);
        if(x<dif().addWeight)
            return new OperationExpr(OperationExpr.Operation.ADD);
        if(x<dif().addWeight+dif().subWeight)
            return new OperationExpr(OperationExpr.Operation.SUB);
        return new OperationExpr(OperationExpr.Operation.MULT);
    }

    protected Expression findMaxOpExpression()
    {
        int max=-1;
        int imax=-1;

        for(int i=mPossible.size()-1; i>=0; i--)
        {
            int x = mPossible.get(i).opCount();
            int n=mPossible.get(i).compute();
            if(x>max && n>=dif().minResultValue && n<=dif().maxResultValue )
            {
                max=x;
                imax=i;
            }
        }
        if(imax==-1) return null;
        return mPossible.get(imax);
    }

    public int getDifficulty() {
        return mDifficulty;
    }

    public void setDifficulty(int mDifficulty) {
        this.mDifficulty = mDifficulty;
    }

    protected static DifficultyAttrs[] initDifficulty()
    {
        DifficultyAttrs[] x = new DifficultyAttrs[5];
        int d;
        for(int i=0; i<x.length; i++) x[i]=new DifficultyAttrs();

        //very easy
        d=VERY_EASY;
        x[d].minNumberCount=4;
        x[d].maxNumberCount=4;
        x[d].minNumberValue=1;
        x[d].maxNumberValue=10;
        x[d].minResultValue=25;
        x[d].maxResultValue=100;
        x[d].minOp=1;
        x[d].maxOp=2;
        x[d].addWeight=2;
        x[d].subWeight=1;
        x[d].multWeight=1;

        //easy
        d=EASY;
        x[d].minNumberCount=5;
        x[d].maxNumberCount=5;
        x[d].minNumberValue=1;
        x[d].maxNumberValue=10;
        x[d].minResultValue=50;
        x[d].maxResultValue=100;
        x[d].minOp=2;
        x[d].maxOp=3;
        x[d].addWeight=1;
        x[d].subWeight=1;
        x[d].multWeight=1;


        //d
        d=MEDIUM;
        x[d].minNumberCount=5;
        x[d].maxNumberCount=6;
        x[d].minNumberValue=1;
        x[d].maxNumberValue=15;
        x[d].minResultValue=50;
        x[d].maxResultValue=250;
        x[d].minOp=3;
        x[d].maxOp=4;
        x[d].addWeight=3;
        x[d].subWeight=1;
        x[d].multWeight=4;

        //d
        d=HARD;
        x[d].minNumberCount=5;
        x[d].maxNumberCount=6;
        x[d].minNumberValue=1;
        x[d].maxNumberValue=25;
        x[d].minResultValue=50;
        x[d].maxResultValue=500;
        x[d].minOp=4;
        x[d].maxOp=5;
        x[d].addWeight=3;
        x[d].subWeight=1;
        x[d].multWeight=4;


        //very d
        d=VERY_HARD;
        x[d].minNumberCount=5;
        x[d].maxNumberCount=6;
        x[d].minNumberValue=1;
        x[d].maxNumberValue=50;
        x[d].minResultValue=50;
        x[d].maxResultValue=1000;
        x[d].minOp=5;
        x[d].maxOp=6;
        x[d].addWeight=3;
        x[d].subWeight=1;
        x[d].multWeight=4;

        return x;
    }
}
