package macampcorp.macamp.game;

import android.app.Activity;
import android.content.Context;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.ArrayList;

import macampcorp.macamp.activities.HostActivity;
import macampcorp.macamp.exceptions.BadGameConfigException;
import macampcorp.macamp.game.challenges.Challenge;
import macampcorp.macamp.game.challenges.ChallengeReturn;
import macampcorp.macamp.game.scheduler.Scheduler;
import macampcorp.macamp.game.scheduler.SpecialTask;
import macampcorp.macamp.utils.L;
import macampcorp.macamp.utils.files.JSONAssetsManager;

/**
 * Created by ptitcois on 19/08/16.
 */
public class Game  implements Serializable {
    private static Game gGame = null;

    protected String           mName;
    protected ArrayList<Resource>   mResourceQueue = new ArrayList<Resource>();
    protected ArrayList<Stage> mStages = new ArrayList<Stage>();
    protected Stage            mCurrentStage=null;
    protected int              mCurrentStageIndex=-1;

    protected Stage            mOnWin=null;
    protected Stage            mOnLoose=null;

    protected Inventaire       mInventaire;
    protected Scheduler        mSched = new Scheduler();


    protected ResourceManager mResources = ResourceManager.newInstance();
    protected ChallengeReturn mLastReturn = null;

    protected Bomb            mBomb;
    protected boolean         mIsLoad=false;


    public Game(String name, Activity act)
    {
        JSONObject root = JSONAssetsManager.load(act, name);
        mName=name;
        mInventaire=new Inventaire();

        if(root==null)
        {
            throw new BadGameConfigException("Le fichier JSON n'a pas pu être parsé");
        }

        try {
            JSONArray arr = root.getJSONArray("stages");
            if(arr==null) throw new JSONException("Not defined");
            for(int i=0; i<arr.length(); i++)
                mStages.add(new Stage(this, arr.getJSONObject(i), act));
        } catch (JSONException e) {
            e.printStackTrace();
            L.error("Erreur le nombre d'étape n'est pas renseigné:");
            L.error(e.getMessage());
            L.error(e.getStackTrace().toString());
            throw new BadGameConfigException("Bad parameter 'stages'");
        }


        try {
            mBomb=new Bomb(root.getString("password"), root.getInt("max_time"));
        } catch (JSONException e) {
            e.printStackTrace();
            L.error("Erreur le password:");
            L.error(e.getMessage());
            L.error(e.getStackTrace().toString());
            throw new BadGameConfigException("Bad parameter 'password'");
        }

        try {
            JSONObject arr = root.getJSONObject("win");
            mOnWin=new Stage(this, arr, act);
        } catch (JSONException e) {
            L.warn("Le champs 'win' n'est pas renseigné");
            throw  new BadGameConfigException("Le champs 'win' n'est pas renseigné");
        }


        try {
            JSONObject arr = root.getJSONObject("loose");
            mOnLoose=new Stage(this, arr, act);
        } catch (JSONException e) {
            L.warn("Le champs 'loose' n'est pas renseigné");
            throw  new BadGameConfigException("Le champs 'win' n'est pas renseigné");
        }

        mCurrentStageIndex=-1;
        if(mStages.size()>0)
            mCurrentStage=mStages.get(0);

    }

    public Stage getCurrentStage()
    {
        return mCurrentStage;
    }

    public Stage getOnWin() { return mOnWin; }
    public Stage getOnLoose() {return mOnLoose; }

    public Challenge getCurrentChallenge()
    {
        if(mCurrentStage==null) return null;
        Challenge c = mCurrentStage.getCurrentChallenge();
        return c;
    }

    public Challenge nextChallenge(HostActivity cd)
    {
        if(mCurrentStage==null) return null;
        mCurrentStage.nextChallenge(cd, this);
        Challenge c = getCurrentChallenge();
        if(c==null)
        {
            nextStage(cd);
            return null;
        }
        return c;
    }

    public Resource getResourceByName(String res)
    {
        return mResources.get(res);
    }


    public void registerResource(String name, Activity act)
    {
        mResources.addResource(name, act);
    }

    public String getName() {
        return mName;
    }



    /**
     * Récupere une ressource par son nom, sans besoin de localisation ou d'orientation
     * @param name Le nom de la ressource
     * @return  true si l'étape est finie, sinon false
     */
    public boolean pickResource(String name)
    {
        return true;
    }


    public boolean isFinished()
    {
        return mCurrentStageIndex>=mStages.size();
    }

    /**
     * Sauvegarde la partie (l'objet game)
     * @param context Le contexte
     * @return Vrai en cas de succes, faux en cas d'erreur
     */
    public boolean save(Activity context)
    {
        try {
            FileOutputStream fos = context.openFileOutput("data", Context.MODE_PRIVATE);
            ObjectOutputStream os = new ObjectOutputStream(fos);
                os.writeObject(this);
            os.close();
            fos.close();
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     * Charge une partie
     * @param context Le contexte
     * @return L'objet Game en cas de succes, null en cas d'erreur
     */
    public static Game load(Activity context)
    {
        try {
            FileInputStream fis = context.openFileInput("data");
            ObjectInputStream is = new ObjectInputStream(fis);
            Game obj = (Game) is.readObject();
            is.close();
            fis.close();
            obj.mIsLoad=true;
            obj.mResources.setInstance();
            return obj;
        } catch (Exception e) {
            return null;
        }
    }


    /**
     * Teste si le jeu est déja sauvé
     * @param context Le contexte
     * @return Vrai ou faux
     */
    public static boolean isSaved(Activity context)
    {

        boolean b = false;
        try {
            FileInputStream fis = context.openFileInput("data");
            if(fis!=null)
                b=true;
            fis.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return b;
    }


    public static void erase(Activity context)
    {
        context.deleteFile("data");
    }

    public void onPause(Activity context)
    {
        save(context);
    }


    /**
     * Permet de recuperer une ressource par son nom
     * @param name Le nom de la ressource
     * @return La ressource ou null (en cas d'erre)
     */
    public Resource findResource(String name)
    {
        return mResources.get(name);
    }



    public static void setGame(Game m)
    {
        gGame=m;
    }

    public static Game game()
    {
        return gGame;
    }



    public boolean hasResource(String name)
    {
        return mResources.contains(name);
    }
    

    public ChallengeReturn getReturn() {
        return mLastReturn;
    }

    public void setReturn(ChallengeReturn mLastReturn) {
        this.mLastReturn = mLastReturn;
    }

    public Stage nextStage(HostActivity cd) {
        mCurrentStageIndex++;
        if(mCurrentStageIndex<=mStages.size())
        {
            //MAJ currentStage
            if(mStages.size()==mCurrentStageIndex) return mCurrentStage=null;
            mCurrentStage=mStages.get(mCurrentStageIndex);


            //ajoute des taches du stage precedents
            if(mCurrentStageIndex>0) {
                //ajout des ressources gagnés
                ArrayList<Resource> res = mStages.get(mCurrentStageIndex - 1).getResources();
                mInventaire.addResource(res);
                mSched.append(res);

                //ajout des events de fin
                mSched.append(mStages.get(mCurrentStageIndex - 1).getEndEvent());
            }

            //Ajout de la tache
            mSched.append( mStages.get(mCurrentStageIndex).getStartEvent());

            //si l'étape n'a pas de challenge on relance la fonction
            if(mCurrentStage.getStepCount()==0)
                return nextStage(cd);
            else //sinon on ajoute le prmeier challenge
            {
                nextChallenge(cd);
                mSched.append(getCurrentChallenge());
            }
            return mCurrentStage;
        }
        mSched.append(SpecialTask.FINISHED_SUCESS);
        return mCurrentStage=null;
    }

    public Bomb getBomb() {
        return mBomb;
    }

    public Inventaire getInventaire() {
        return mInventaire;
    }

    public int getCurrentStageIndex() {
        return mCurrentStageIndex;
    }

    public Scheduler getScheduler(){
        return mSched;
    }



}
