package fanch.multijeu.sudoku.data;

import android.util.Log;

import fanch.multijeu.common.utils.TimeUtils;
//import fanch.multijeu.sudoku.data.OldSudoku;

public class SimpleSudokuResolver {

    protected static final int[][] INDEXES = {
            {0,1,2,9,10,11,18,19,20}, {3,4,5,12,13,14,21,22,23}, {6,7,8,15,16,17,24,25,26},
            {27,28,29,36,37,38,45,46,47}, {30,31,32,39,40,41,48,49,50}, {33,34,35,42,43,44,51,52,53},
            {54,55,56,63,64,65,72,73,74}, {57,58,59,66,67,68,75,76,77}, {60,61,62,69,70,71,78,79,80}
    };

    protected Sudoku mSudoku;
    protected long mStart=0;

    protected SudokuValue[] mValues = new SudokuValue[81];

    public SimpleSudokuResolver()
    {
    }

    public SimpleSudokuResolver(Sudoku s)
    {
        init(s);
    }

    public void init(Sudoku s)
    {
        mStart= TimeUtils.timeUs();
        mValues = new SudokuValue[81];
        for(int i=0; i<81; i++)
            mValues[i]=SudokuValue.filled();
        mSudoku=s;

        for(int i=0; i<81; i++)
        {
            final int x = mSudoku.get(i);
            if(x>0)
                mValues[i]=new SudokuValue(x);
        }
    }

    public void populate()
    {

    }


/*
                        int N =  mValues[x].size();
                        for(int k=0; k<N; k++)
                        {
                            final int val = mValues[x].get(k);
                            if(hasOneNotPresent(i,j,val))
                            {
                                mValues[x].set(val);
                                ok++;
                            }
                        }*/



    public void lastChoiceDeduce(int i, int j)
    {
        final int x = j*9+i;
        final int N =  mValues[x].size();
        for(int k=0; k<N; k++)
        {
            final int val = mValues[x].get(k);
            if(hasOneNotPresent(i,j,val)) {
                mValues[x].set(val);
                return;
            }
        }
    }


    public void updatePossiblilities()
    {
        int modified=0, n=0, ok=0;
        do {
            Log.e("////////", "Pass: "+n+" ("+modified+","+ok+") "+": "+toString());
            modified = 0;
            ok=0;

            for (int j = 0; j < 9; j++) {
                for (int i = 0; i < 9; i++) {
                    int x = j * 9 + i;
                    int  old = mValues[x].hash();
                    if(x==32)
                    {
                        int t=0;
                        t++;
                    }

                    // appel des deducteur
                    lastChoiceDeduce(i,j);
                    simpleDeduce(i,j);


                    if(old != mValues[x].hash()) {
                        modified++;
                        if(mValues[x].size()==1)
                        {
                            ok++;
                            mSudoku.set(i,j, (byte) mValues[x].get(0));
                        }
                    }
                }
            }

            n++;

            if(modified==0) {
                int tt=0;
                tt++;
                Log.e("///////","Deadlok");
                if(!tryRemoveDeadlock())
                    return;
            }

        }while (true);
    }

    public boolean isResolved()
    {
        for(int i=0; i<mValues.length; i++)
            if(mValues.length!=1)
                return false;
        return true;
    }

    public SudokuValue valueForLine(int x, int y)
    {
        SudokuValue val = SudokuValue.filled();
        for(int i=0; i<9; i++)
        {
            if(i!=x) val.remove(mSudoku.get(i, y));
        }
        return val;
    }

    public SudokuValue valueForColumn(int x, int y)
    {
        SudokuValue val = SudokuValue.filled();
        for(int i=0; i<9; i++)
        {
            if(i!=y)
                val.remove(mSudoku.get(x, i));
        }
        return val;
    }

    public SudokuValue valueForSquare(int x, int y)
    {

        SudokuValue val = SudokuValue.filled();
        int square = (y/3)*3+(x/3);
        int offset = (y*9)+x;
        final int arr[] = INDEXES[square];
        for(int i=0; i<arr.length; i++) {
            if(arr[i]!=offset)
                val.remove(mSudoku.get(arr[i]));
        }
        return val;
    }

    /**
     * Enleve toutes les possibilités déja présente dans les lignes, colonnes, square
     * @param i L'indice [0-8] de labicsse de la case
     * @param j L'indice [0-8] de l'ordonée
     * @return true si une information a été éliminé
     */
    public void simpleDeduce(int i, int j)
    {
        if(j*9+i==16 || j*9+i==13)
        {
            int t=0;
            t++;
        }
        mValues[j*9+i].intersect(valueForColumn(i,j));
        mValues[j*9+i].intersect(valueForLine(i,j));
        mValues[j*9+i].intersect(valueForSquare(i,j));
    }

    public String toString()
    {
        String s = "";
        for(int j=0; j<9; j++)
        {
            for(int i=0; i<9; i++)
            {
                s+=mValues[j*9+i]+" ";
            }
            s+="\n";
        }
        return s;
    }

    public boolean hasNoPresentInLine(int x, int y, int val)
    {
        for(int i=0; i<9; i++)
            if(x!=i)
                if(mValues[y*9+i].has(val))
                    return false;
        return true;
    }

    public boolean hasNoPresentInColumn(int x, int y, int val)
    {
        for(int i=0; i<9; i++)
            if(y!=i)
                if(mValues[i*9+x].has(val))
                    return false;
        return true;
    }

    public boolean hasNoPresentSquare(int x, int y, int val)
    {
        int square = (y/3)*3+(x/3);
        int offset = (y*9)+x;
        final int arr[] = INDEXES[square];
        for(int i=0; i<arr.length; i++) {
            if(arr[i]!=offset)
                if(mValues[arr[i]].has(val))
                    return false;

        }
        return true;
    }


    public boolean hasOneNotPresent(int x, int y, int val)
    {
        return hasNoPresentInLine(x,y,val) || hasNoPresentInColumn(x,y,val)
                    || hasNoPresentSquare(x,y,val);
    }

    public void resolve() {
        updatePossiblilities();
        long s = TimeUtils.timeUs()-mStart;
        Log.e("///////", "Time: "+s+" us");
    }

    public SudokuValue[] getLine(int y)
    {
        SudokuValue val[] = new SudokuValue[9];
        for(int i=0; i<9; i++)
            val[i]=mValues[y*9+i];
        return val;
    }

    public int[] getLineIndexes(int y)
    {
        int val[] = new int[9];
        for(int i=0; i<9; i++)
            val[i]=y*9+i;
        return val;
    }

    public SudokuValue[] getColumn(int x)
    {
        SudokuValue val[] = new SudokuValue[9];
        for(int i=0; i<9; i++)
            val[i]=mValues[i*9+x];
        return val;
    }

    public int[] getColumnIndexes(int x)
    {
        int val[] = new int[9];
        for(int i=0; i<9; i++)
            val[i]=i*9+x;
        return val;
    }

    public SudokuValue[] getSquare(int n)
    {
        SudokuValue val[] = new SudokuValue[9];
        int indexes[] = INDEXES[n];
        for(int i=0; i<9; i++)
            val[i]=mValues[indexes[i]];
        return val;
    }

    public int[] getSquareIndexes(int n)
    {
        return INDEXES[n];
    }

    public boolean checkDeadlock(SudokuValue[] values, int[] indexes)
    {
        int N = values.length;
        for(int i=0; i<N; i++)
        {
            final SudokuValue v = values[i];
            if(v.size()<2) continue;

            int n=0;
            for(int j=0; j<N; j++)
            {
                if(v.equals(values[j]))
                    n++;
            }
            if(n==v.size())
            {
                boolean ok=true;
                for(int k=0; k<v.size() && ok; k++)
                {
                    for(int l=0; l<N && ok; l++)
                    {
                        if(!v.equals(values[l]) && values[l].has(v.get(k)))
                            ok=false;
                    }
                }
                if(ok)
                {
                    v.set(v.get());
                    mSudoku.set(indexes[i], v.get());
                    return true;
                }
            }
        }
        return false;
    }

    public boolean tryRemoveDeadlock()
    {
        for(int i=0; i<9; i++)
        {
            SudokuValue[] v = getLine(i);
            int[] vi = getLineIndexes(i);

            SudokuValue[] v2 = getColumn(i);
            int[] v2i = getColumnIndexes(i);

            SudokuValue[] v3 = getSquare(i);
            int[] v3i = getSquareIndexes(i);

            if(checkDeadlock(v, vi))  return true;
            if(checkDeadlock(v2, v2i))  return true;
            if(checkDeadlock(v3, v3i))  return true;
        }
        return false;
    }
}
