package fanch.multijeu.sudoku.view;

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;

import java.util.ArrayList;

import fanch.multijeu.common.core.Saveable;
import fanch.multijeu.common.core.SerializedObject;
import fanch.multijeu.common.utils.IntUtils;
import fanch.multijeu.common.view.Line;
import fanch.multijeu.sudoku.data.Sudoku;
import fanch.multijeu.sudoku.listener.SudokuDriver;

public class SudokuView extends ViewGroup implements View.OnClickListener, SudokuDriver {

    protected static final int BIG_PADDING =5;
    protected static final int LITTLE_PADDING =2;


    protected static final int[][] BUTTONS = {
        {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 static final int LITTLE_PADDINGS[] = {0,1,2,2,3,4,4,5,6, 6};
    protected static final int BIG_PADDINGS[]    = {1,1,1,2,2,2,3,3,3, 4};

    public static class Attrs extends Saveable {
        public boolean rtCorrect=true;
        public boolean squareGrid=true;
    }

    public static interface SudokuListener {
        void onClick(SudokuCase c, int x, int y);
        void onSudokuDone(boolean ok);
    }

    protected SudokuCase[] mButtons;
    protected SudokuListener mListener;
    protected Line[]  mHLines = new Line[10];
    protected Line[]  mVLines = new Line[10];
    protected Line[]  mBigLines = new Line[4];
    protected Sudoku  mSudoku;
    protected Sudoku  mSudokuDefault;
    protected Sudoku  mSudokuResolved;

    protected Attrs   mAttrs = new Attrs();
    protected boolean mCheat = false;
    protected int     mMaxIndice = 3;
    protected int     mCurrentIndice=0;

    public SudokuView(Context context) {
        super(context);
        init();
    }

    public SudokuView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    private SudokuCase createButton()
    {
        SudokuCase c = new SudokuCase(getContext());
        c.setOnClickListener(this);
        return c;
    }

    public Attrs getAttrs() {
        return mAttrs;
    }

    public void setAttrs(Attrs mAttrs) {
        this.mAttrs = mAttrs;
    }

    private Line createLine()
    {
        Line l = new Line(getContext());
        return l;
    }

    private void init() {
        mButtons=new SudokuCase[81];

        for(int i=0; i<mHLines.length; i++)
        {
            mHLines[i]=createLine();
            mVLines[i]=createLine();
            addView(mHLines[i]);
            addView(mVLines[i]);
        }

        for(int i=0; i<mBigLines.length; i++)
        {
            mBigLines[i]=createLine();
            addView(mBigLines[i]);
        }

        for(int i=0; i<81; i++) {
            mButtons[i] = createButton();
            addView(mButtons[i]);
        }
    }

    public void setSudoku(Sudoku sud)
    {
        mSudoku =sud;
        mSudokuDefault=sud.copy();
        mSudokuResolved= sud.copy();

        boolean ok = mSudokuResolved.resolve();

        if(!ok)
        {
            Log.e("--------", "Sudoku="+mSudokuDefault.toString());
            throw new RuntimeException("Unable to resolve Sudoku");
        }

        mCheat=false;
        mCurrentIndice=0;

        for(int i=0; i<81; i++) {
            final int x = mSudoku.get(i);
            final int def = mSudokuDefault.get(i);
            mButtons[i].setValue(x);
            if(def>0) mButtons[i].setState(SudokuCase.CaseState.LOCKED);
        }
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int w = MeasureSpec.getSize(widthMeasureSpec);
        int h = MeasureSpec.getSize(heightMeasureSpec);
        if(mAttrs.squareGrid)
        {
            if(w<h) h=w;
            if(h<w) w=h;
        }

        setMeasuredDimension(w,h);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        final int _W = r-l- BIG_PADDING *4-6*LITTLE_PADDING;
        final int _H = b-t- BIG_PADDING *4-6*LITTLE_PADDING;
        final int subWidth = _W/3;
        final int subHeight = _H/3;
        final float fsubWidth = (float) (_W/3.0);
        final float fsubHeight = (float) (_H/3.0);

        //draw lines
        for(int i=0; i<10; i++)
            drawLittleLine(i, (float) (_W/9.0), (float) (_H/9.0), r-l-LITTLE_PADDING, b-t-LITTLE_PADDING);

        //draw case
        for(int i=0; i<9; i++)
        {
            subLayout(i, (float) (_W/9.0), (float) (_H/9.0));

        }
    }

    protected void drawLittleLine(int i, float w, float h, int W, int H)
    {
        final float x = LITTLE_PADDINGS[i]*LITTLE_PADDING+BIG_PADDINGS[i]*BIG_PADDING+i*w;
        final float y = LITTLE_PADDINGS[i]*LITTLE_PADDING+BIG_PADDINGS[i]*BIG_PADDING+i*h;
        int d;
        if(i==0 || i==3 || i==6 || i==9)
        {
            d=BIG_PADDING;
        }else{
            d=LITTLE_PADDING;
        }
        childFrame(mHLines[i], 0, y-d, W, d);
        childFrame(mVLines[i], x-d, 0, d, H);
    }



    protected void subLayout(int n, float w, float h)
    {
        final int indices[] = BUTTONS[n];
        final float cw = w/3;
        final float ch = h/3;

        final int dx = n%3;
        final int dy = n/3;
        for(int j=0; j<3; j++)
        {
            for(int i=0; i<3; i++)
            {
                final int di = dx*3+i;
                final int dj = dy*3+j;
                final float xx = LITTLE_PADDINGS[di]*LITTLE_PADDING+
                        BIG_PADDINGS[di]*BIG_PADDING+di*w;
                final float yy = LITTLE_PADDINGS[dj]*LITTLE_PADDING+
                        BIG_PADDINGS[dj]*BIG_PADDING+dj*h;

                childFrame(indices[j*3+i],xx,yy,w,h);
            }
        }
    }

    protected void childFrame(int i, float x, float y, float w, float h)
    {
        mButtons[i].layout((int)x,(int)y,(int)(x+w),(int)(y+h));
    }

    protected void childFrame(View i, float x, float y, float w, float h)
    {
        i.layout((int)x,(int)y,(int)(x+w),(int)(y+h));
    }

    protected SudokuCase button(int i, int j)
    {
        return button(j*9+i);
    }

    protected SudokuCase button(int i)
    {
        return mButtons[i];
    }

    public void setListener(SudokuListener mListener) {
        this.mListener = mListener;
    }


    @Override
    public void onClick(View v) {
        final SudokuCase c = (SudokuCase) v;
        final int id = findId(c);
        final int x = id%9;
        final int y = id/9;

        if(((SudokuCase) v).mState!= SudokuCase.CaseState.LOCKED) {
            if (mListener != null) {
                mListener.onClick(c, x, y);
            }
        }
    }

    public int findId(SudokuCase v)
    {
        for(int i=0; i<mButtons.length; i++)
            if(mButtons[i]==v) return i;
        return -1;
    }

    @Override
    public void setValue(int x, int y, int newVal) {
        button(x,y).setValue(newVal);
        mSudoku.set(x, y, (byte) newVal);
    }

    @Override
    public boolean isError(int x, int y, int val) {
        return !mSudoku.checkCell(x,y, (byte) val);
    }

    public boolean isError(int x, int y) {
        return !mSudoku.checkCell(x,y, (byte) button(x,y).getValue());
    }

    @Override
    public void valueChanged(int x, int y, int val) {
        int count =0;
        for(int i=0; i<81; i++)
            if(button(i).getValue()!=0)
                count++;

        if(count==81){
            int nerr=0;
            for(int i=0; i<81; i++)
            {
                if(button(i).getValue()!= mSudokuResolved.get(i))
                {
                    button(i).setState(SudokuCase.CaseState.ERROR);
                    nerr++;
                }
            }

            if(mListener!=null)
            {
                mListener.onSudokuDone(nerr==0);
            }
        }
    }

    public int[] possibleValues(int x, int y)
    {
        int out[];
        ArrayList<Integer> l = new ArrayList();
        for(int i=1; i<=9; i++)
        {
            if(!isError(x, y, i))
                l.add(i);
        }
        out=new int[l.size()];
        for(int i=0; i<l.size(); i++)
            out[i]=l.get(i);
        return out;
    }



    public void nextIndice()
    {
        if(mMaxIndice>0 && mCurrentIndice+1<=mMaxIndice) {
            mCurrentIndice++;
            int n = 0;
            for (int i = 0; i < mButtons.length; i++)
                if (button(i).getValue() == 0)
                    n++;
            if (n == 0) return;
            int x = IntUtils.rand(0, n - 1);

            for (int i = 0; i < mButtons.length; i++)
                if (button(i).getValue() == 0) {
                    n--;
                    if (n == 0) {
                        button(i).setValue(mSudokuResolved.get(i));
                        button(i).setState(SudokuCase.CaseState.INDICE);
                        valueChanged(i % 9, i / 9, mSudokuResolved.get(i));
                    }
                }
        }

        if(mMaxIndice>0 && mCurrentIndice==mMaxIndice)
        {
            //hide button
        }
    }

    public void resolve()
    {
        mCheat=true;
        for(int i=0; i<mButtons.length; i++)
        {
            if(button(i).getValue()==0){
                button(i).setValue(mSudokuResolved.get(i));
                button(i).setState(SudokuCase.CaseState.INDICE);
                valueChanged(i%9, i/9, mSudokuResolved.get(i));
            }else if(button(i).getState()== SudokuCase.CaseState.VALUE)
            {
                if(mSudokuResolved.get(i)!=button(i).getValue())
                {
                    button(i).setState(SudokuCase.CaseState.ERROR);
                }
            }
        }
    }


}
