package app.mar.game;

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

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 app.mar.game.challenges.Challenge;
import app.mar.game.challenges.ChallengeReturn;
import app.mar.game.challenges.Treasure;
import app.mar.utils.SensorsManager;
import app.mar.utils.Settings;
import app.mar.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=0;

    protected Player           mPlayer;
    protected Settings         mSettings = new Settings();
    protected String           mMap=null;
    protected Inventaire       mInventaire;

    protected ResourceManager mResources = new ResourceManager();
    protected ChallengeReturn mLastReturn = null;

    protected Bomb            mBomb;


    public Game(String name, Activity act)
    {
        JSONObject root = JSONAssetsManager.load(act, name);
        mName=name;
        mInventaire=new Inventaire();
        mPlayer = new Player(act);
        try {
            JSONArray arr = root.getJSONArray("stages");
            for(int i=0; i<arr.length(); i++)
                mStages.add(new Stage(this, arr.getJSONObject(i), act));
        } catch (JSONException e) {
            e.printStackTrace();
        }

        try {
            mMap=root.getString("map");
        } catch (Exception e) {
            e.printStackTrace();
        }

        try {
            mBomb=new Bomb(root.getString("password"), root.getInt("max_time"));
        } catch (JSONException e) {
            e.printStackTrace();
        }

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

        if(!mPlayer.hasMagneticField() || !mPlayer.hasAcceleromter())
        {
            mSettings.setARMode(false);
            if(!mPlayer.hasMagneticField()) Toast.makeText(act, "Votre dispositif n'a pas de boussole. Le mode RA ne peut fonctionner et a été désactivé", Toast.LENGTH_LONG).show();
            else if(!mPlayer.hasAcceleromter()) Toast.makeText(act, "Votre dispositif n'a pas d'accélerometre. Le mode RA ne peut fonctionner et a été désactivé", Toast.LENGTH_LONG).show();
        }
    }

    public Stage getCurrentStage()
    {
        return mCurrentStage;
    }

    public Challenge getCurrentChallenge()
    {
        if(mCurrentStage==null) return null;
        Challenge c = mCurrentStage.getCurrentChallenge();
        if(c==null)
        {
            ArrayList<Resource> res = mCurrentStage.getResources();
            mInventaire.addResource(res);

            for(int i=0; i<res.size(); i++)
                mResourceQueue.add(res.get(i));

            nextStage();
            return getCurrentChallenge();
        }
        return c;
    }

    public Resource popLastResource()
    {
        if(mResourceQueue.size()>0)
        {
            Resource x = mResourceQueue.get(0);
            mResourceQueue.remove(0);
            return x;
        }
        return null;
    }

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


    public Settings getSettings(){return mSettings;}
    public void setSettings(Settings s){ mSettings=s;}

    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;
    }




    /**
     * 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();
            return obj;
        } catch (Exception e) {
            e.printStackTrace();
            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 void onPause(Activity context)
    {
        save(context);
        mPlayer.onPause();
        removeCached3DModels();
    }


    public void onResume()
    {
        mPlayer.onResume();
    }

    public void setSensorsManager(SensorsManager sm)
    {
        mPlayer.setSensorsManager(sm);
    }

    /**
     * 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);
    }

    /**
     * Charge la ressource 3d quand on entre dans une zone
     * @param contecxt Contexte
     * @return Vrai en cas de succes, faux pour une erreur
     */
    synchronized public void precache3DResource(Activity contecxt)
    {
        Challenge c = getCurrentChallenge();
        if(c instanceof Treasure)
        {

        }
    }




    /**
     *
     * renvoie la liste des ressources sur lesquelles le joueur est présent
     * @return la liste des ressources sur lesquelles le joueur est présent
     */
    public ArrayList<Resource> getResourcesNextToPlayer(Area a)
    {
        ArrayList<Resource> r = new ArrayList<Resource>();
        ArrayList<String> rs = a.getResourceNames();

        for(int i=0; i<rs.size(); i++)
            r.add(findResource(rs.get(i)));

        return r;
    }

    public Player getPlayer()
    {
        return mPlayer;
    }


    public void removeCached3DModels()
    {
        mResources.deleteAll3DModel();
    }

    public void newSensorManager(Activity act)
    {
        mPlayer.newSensorManager(act);
        if(!mPlayer.hasMagneticField() || !mPlayer.hasAcceleromter())
            mSettings.setARMode(false);
    }



    public void restartSensors()
    {
        mPlayer.getSensorsManager().restartSensors();
    }

    public void stopSensors()
    {
        mPlayer.getSensorsManager().stopSensors();
    }



    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() {
        mCurrentStageIndex++;
        if(mCurrentStageIndex<mStages.size())
        {
            ArrayList<Resource> res = mCurrentStage.getResources();
            mInventaire.addResource(res);

            for(int i=0; i<res.size(); i++)
                mResourceQueue.add(res.get(i));

            return mCurrentStage=mStages.get(mCurrentStageIndex);
        }
        return mCurrentStage=null;
    }

    public Bomb getBomb() {
        return mBomb;
    }

    public Inventaire getInventaire() {
        return mInventaire;
    }
}
