package fanch.multijeu.findwords.data.checker;

import android.support.annotation.NonNull;

import java.util.ArrayList;

import fanch.multijeu.common.core.Saveable;
import fanch.multijeu.common.core.Word;
import fanch.multijeu.common.exceptions.BadParametterException;
import fanch.multijeu.findwords.data.dictionaire.Dictionaire;
import fanch.multijeu.findwords.data.LetterGenerator;
import fanch.multijeu.findwords.data.TryState;
import fanch.multijeu.findwords.data.WordPair;
import fanch.multijeu.findwords.data.WordSet;
import fanch.multijeu.findwords.data.WordState;
import fanch.multijeu.common.utils.IntUtils;

public class WordChecker extends Saveable {

    public static class Attrs extends Saveable
    {
        public int minWordSize=2;
        public int maxWordSize=7;
        public int maxWordsCount=8;
        public int minWordsCount=1;
        public boolean accentSensitive=true;
    }


    protected String      m_letters;
    protected WordSet     m_bonus;
    protected WordSet     m_expected;
    protected int         m_found=0;
    private   Attrs       mAttrs = new Attrs();
    private transient     Object  m_lock = new Object();




    public WordChecker(Dictionaire d, String letters, WordSet expected)
    {
        reset(d, letters, expected);
    }

    public WordChecker(Dictionaire d, String letters)
    {
        reset(d, letters, null);
    }


    public WordChecker(Dictionaire d, String letters, WordSet expected, Attrs a)
    {
        mAttrs=a;
        reset(d, letters, expected);
    }

    public WordChecker(Dictionaire d, String letters, Attrs a)
    {
        mAttrs=a;
        reset(d, letters, null);
    }


    public String getLetters()
    {
        return m_letters;
    }

    public void addSolution(Word s)
    {
        m_expected.add(s);
    }

    public WordSet getExpected()
    {
        return m_expected;
    }

    public WordSet getBonus()
    {
        return m_bonus;
    }


    public synchronized void reset(Dictionaire dict, String letters, WordSet exp)
    {
        m_letters = letters.toLowerCase();
        m_expected = exp;
        if(mAttrs.accentSensitive)
            m_bonus = new WordSet(dict.constructWith(m_letters));
        else
            m_bonus = new WordSet(dict.constructWithIgnoreAccent(m_letters));

        synchronized (m_bonus) {
            if(m_expected==null)
                m_expected=m_bonus;
            else
                m_bonus.exclude(m_expected);
        }
    }

    public int expectedLeft()
    {
        return m_expected.size()-m_found;
    }

    public synchronized TryState tryWord(Word s)
    {
        synchronized (m_bonus)
        {
            TryState t = TryState.NO;
            WordPair pair;
            if(m_expected.contains(s,mAttrs.accentSensitive))
            {
                ArrayList<WordPair> p = m_expected.getAll(s, mAttrs.accentSensitive);
                for(int i=0; i<p.size(); i++)
                {
                    pair=p.get(i);
                    if(pair.second==WordState.NOT_FOUND)
                    {
                        m_expected.found(s, mAttrs.accentSensitive);
                        m_found++;
                        t=TryState.YES;
                    }else
                    {
                        if(t==TryState.NO) t=TryState.YES_AGAIN;
                    }
                }
                return t;
            }
            else if( m_bonus.contains(s,mAttrs.accentSensitive))
            {
                ArrayList<WordPair> p = m_bonus.getAll(s, mAttrs.accentSensitive);
                for(int i=0; i<p.size(); i++)
                {
                    pair=p.get(i);
                    if(pair.second==WordState.NOT_FOUND)
                    {
                        m_bonus.found(s, mAttrs.accentSensitive);
                        t=TryState.BONUS;
                    }else
                    {
                        if(t==TryState.NO) t=TryState.BONUS_AGAIN;
                    }
                }
                return t;
            }
            else return TryState.NO;
        }
    }



    public  void setMinCharcters(int min)
    {
        synchronized(m_bonus) {
            m_bonus.setMin(min);
        }
    }

    public synchronized void setMaxCharcters(int max)
    {
        synchronized(m_bonus) {
            m_bonus.setMax(max);
        }
    }

    public synchronized void setMinMaxCharcters(int min, int max)
    {
        synchronized(m_bonus) {
            m_bonus.setMinMax(min, max);
        }
    }

    public synchronized void setMaxWordCount(int maxWordsCount) {
        while(m_expected.size()>maxWordsCount)
        {
            m_expected.remove(IntUtils.rand(0, m_expected.size()-1));
        }
    }

    /*
     * Generate
     */

    protected static void generateCheck(LetterGenerator.Attrs a)
    {
        if(a.minWordSize>0 && a.maxWordSize>0 && a.minWordSize>a.maxWordSize)
            throw new BadParametterException("WordChecker::generate : minWordSize>maxWordSize "
                    +"("+a.minWordSize+">"+a.maxWordSize+")");

        if(a.minVoyellesCount>0 && a.maxLettersCount>0 && a.minVoyellesCount>a.maxLettersCount)
            throw new BadParametterException("WordChecker::generate : minVoyellesCount>maxLettersCount "
                    +"("+a.minVoyellesCount+">"+a.maxLettersCount+")");

        if(a.minConsonnesCount>0 && a.maxLettersCount>0 && a.minConsonnesCount>a.maxLettersCount)
            throw new BadParametterException("WordChecker::generate : minConsonnesCount>maxLettersCount "
                    +"("+a.minConsonnesCount+">"+a.maxLettersCount+")");

        if(a.minWordSize>0 && a.maxLettersCount>0 && a.minWordSize>a.maxLettersCount)
            throw new BadParametterException("WordChecker::generate : minWordSize>maxLettersCount "
                    +"("+a.minWordSize+">"+a.maxLettersCount+")");

        if(a.minLettersCount>0 && a.maxWordSize>0 && a.minLettersCount>a.maxWordSize)
            throw new BadParametterException("WordChecker::generate : minLettersCount>maxWordSize "
                    +"("+a.minLettersCount+">"+a.maxWordSize+")");

        if(a.minLettersCount>0 && a.maxLettersCount>0 && a.minLettersCount>a.maxLettersCount)
            throw new BadParametterException("WordChecker::generate : minLettersCount>maxLettersCount "
                    +"("+a.minLettersCount+">"+a.maxLettersCount+")");


        if(a.minWordsCount>0 && a.maxWordsCount>0 && a.minWordsCount>a.maxWordsCount)
            throw new BadParametterException("WordChecker::generate : minWordsCount>maxWordsCount "
                    +"("+a.minWordsCount+">"+a.maxWordsCount+")");
    }


    public static WordChecker generate(@NonNull Dictionaire d, LetterGenerator.Attrs a)
    {
        String s="";
        WordChecker chk;
        LetterGenerator gen = new LetterGenerator(a);

        if(a==null) a=new LetterGenerator.Attrs();
        generateCheck(a);




        do{
            s=gen.generate();
            chk=new WordChecker(d,s);

            if(a.minWordSize>=0 && a.maxWordSize>=0)
                chk.setMinMaxCharcters(a.minWordSize, a.maxWordSize);
            else if(a.minWordSize>=0)
                chk.setMinCharcters(a.minWordSize);
            else if(a.maxWordSize>=0)
                chk.setMaxCharcters(a.maxWordSize);

            if(a.maxWordsCount>0)
                chk.setMaxWordCount(a.maxWordsCount);
        }while(a.minWordsCount>0 && chk.getExpected().size()<a.minWordsCount);

        return chk;
    }

    public static WordChecker generate(@NonNull Dictionaire d) {
        return generate(d,null);
    }

    public String toString()
    {
        StringBuilder builder = new StringBuilder();
        builder.append("Letters: ");
        builder.append(m_letters);
        builder.append("\nExpected: ");
        builder.append(m_expected);
        builder.append("\nBonus: ");
        builder.append(m_bonus);
        builder.append("\nFound: ");
        builder.append(m_found);
        builder.append("\n");

        return builder.toString();
    }

}
