package com.virtual.pollentia.WalkingTour.controlador;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Color;
import android.location.Location;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.Uri;
import android.os.Handler;
import android.preference.PreferenceManager;
import android.support.v4.view.ViewPager;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import com.google.android.gms.maps.CameraUpdate;
import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.model.BitmapDescriptorFactory;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.MarkerOptions;
import com.google.android.gms.maps.model.PolylineOptions;
import com.viewpagerindicator.CirclePageIndicator;
import com.virtual.pollentia.WalkingTour.modelo.AdminSQLiteOpenHelper;
import com.virtual.pollentia.WalkingTour.modelo.CopyFilesToAssets;
import com.virtual.pollentia.WalkingTour.modelo.FullScreenImageAdapter;
import com.virtual.pollentia.WalkingTour.modelo.GPSTracker;
import com.virtual.pollentia.WalkingTour.modelo.ImageUtils;
import com.virtual.pollentia.WalkingTour.modelo.PuntoGps;
import com.virtual.pollentia.WalkingTour.vista.FullScreenViewActivity;
import com.virtual.pollentia.WalkingTour.vista.MainActivity;
import com.virtual.pollentia.WalkingTour.vista.SplashScreen;
import com.virtual.pollentia.pollentia_tour.R;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Timer;
import java.util.TimerTask;

/**
 * Created by Roger Gomez
 */
public class Controlador {

    private static Controlador singletonInstance;
    final Handler handler = new Handler();
    private GPSTracker gps;
    private Context contexto;
    private AdminSQLiteOpenHelper admin;
    private Timer timer;
    private TimerTask timerTask;
    private TextView texto;
    private boolean enRuta = false;
    private ArrayList<PuntoGps> puntosRuta;
    private PuntoGps puntoActual;
    private int out = 0;
    private float currentDegree = 0f;
    private String videoPuntoActual = "";
    private String textoPuntoActual = "";
    private String audioPuntoActual ="";
    private ArrayList<String> photosPuntoActual = null;
    private ArrayList<String> textosPhotosPuntoActual = null;
    private double distanciaMax = 5000.0;
    private int contadorCarga = 0;
    private int sentido = 1;
    private int lastPoint = 0;
    private Location locationTemp = null;
    private int buscandoPosicion = 0;
    private MediaPlayer mp=null;
    private boolean audioPlaying=false;
    SharedPreferences prefs = null;
    private int intervaloCaptura=0;
    private int tiempoMaxPosicion = 6;
    private int timeout = 60;

    /**
     * Constructor de la clase
     *
     * @param context
     */
    public Controlador(Context context, TextView text) {
        texto = text;
        contexto = context;

    }


    /**
     * Constructor de la clase
     */
    public Controlador(Context context) {

        contexto = context;
        prefs = PreferenceManager.getDefaultSharedPreferences(contexto.getApplicationContext());
    }


    /**
     * Comprueba si existe o no una instancia de este objeto creado por alguna clase, en función de si estava creado o no, lo crea o le añade nuevos
     * elementos necesarios para controlar la nueva Activity.
     *
     * @param context
     * @param text
     * @return
     */
    public static Controlador getSingletonInstance(Context context, TextView text) {
        if (null == singletonInstance) {
            if (text != null)
                singletonInstance = new Controlador(context, text);
            else
                singletonInstance = new Controlador(context);
        } else {
            if (text != null) {
                singletonInstance.contexto = context;
                singletonInstance.texto = text;
            }
        }
        return singletonInstance;
    }

    /**
     * Destruye el controlador
     */

    public void destruirControlador() {
        this.pararNuevaRuta();
        singletonInstance = null;

    }


    /**
     * Iniciamos la BD
     */
    public void crearBd() {
        admin = new AdminSQLiteOpenHelper(contexto);
        try {
            admin.create();
        } catch (IOException ioe) {
            throw new Error("Unable to create database");
        }
    }

    /**
     * cogemos todos los puntos gps de la base de datos
     */
    public void leerPuntos() {
        this.puntosRuta = admin.getLocations();
        lastPoint = this.puntosRuta.get(this.puntosRuta.size() - 1).getId();
    }

    /**
     * Avisa a la activitySplash de que se cargaron correctamente los datos y debe cerrarse.
     */
    public void finishSplash() {
        ((SplashScreen) contexto).nextActivity();
    }


    /**
     * Metodo que inicializa el objeto GPS, comprueba si el GPS esta habilitado y hace la llamada para iniciar el timer y la timerTask.
     */

    public void puntoCercano() {

        if (gps == null) {
            iniGps();
        }
        if (checkActiveGps()) {
            this.enRuta = false;
            this.buscandoPosicion=0;
            startTimerLocation();
        }

    }


    /**
     * Inicaliza objeto del gps*
     */
    public void iniGps() {
        gps = new GPSTracker(contexto);
    }



    /**
     * Comprueba si el GPS esta habilitado.
     *
     * @return boolean que indica si esta o no habilitado el GPS.
     */

    public boolean checkActiveGps() {

        boolean continuar = false;

        gps.getLocation();

        if (gps.canGetLocation()) {
            continuar = true;
            this.out=0;
            this.intervaloCaptura=Integer.valueOf(prefs.getString("tiempo", "1"));
            this.tiempoMaxPosicion = this.tiempoMaxPosicion / intervaloCaptura;
            this.timeout=60;
            this.timeout = this.timeout / intervaloCaptura;
            View v = ((Activity) contexto).findViewById(R.id.main_layout);
            Button play=(Button) v.findViewById(R.id.play_tour);
            ((MainActivity) contexto).setEstado(1);
            play.setText("DETENER EL TOUR");

        } else {

            gps.showSettingsAlert();
        }

        return continuar;

    }

    /**
     * Crea un timer de x segundos, y hace la llamada para iniciar la timerTask que se ejecutara.
     */
    public void startTimerLocation() {
        //set a new Timer
        timer = new Timer();
        //initialize the TimerTask's job
        initializeTimerTask();
        //schedule the timer, after the first 2000ms the TimerTask will run every 1000ms
        int tiempo = Integer.valueOf(prefs.getString("tiempo", "1"));
        timer.schedule(timerTask, 1000, tiempo*1000); //
    }


    /**
     * Inicializa la timerTask que hace peticiones al GPS.
     */
    public void initializeTimerTask() {

        timerTask = new TimerTask() {
            public void run() {
                handler.post(new Runnable() {
                    public void run() {
                        getCoordenadas();
                    }
                });
            }
        };
    }


    /**
     * Detiene la timerTask que hace peticiones al GPS.
     */

    public void stopTimerTask() {
        if (timer != null) {
            timer.cancel();
            timer = null;
        }
    }


    /**
     * recoge las coordenadas del gps.
     */
    public synchronized void getCoordenadas() {
        this.texto.setText("Buscando posición");
        Location location = gps.getLocation();
        if (gps.canGetLocation()) {
            if (location != null) {

                this.texto.setText("");
                this.texto.append("Latitud:" + location.getLatitude() + "\n");
                this.texto.append("Longitud:" + location.getLongitude() + "\n");
                if (buscandoPosicion < this.tiempoMaxPosicion) {
                    locationTemp = location;
                    buscandoPosicion += 1;
                    this.out = 0;
                } else {
                    if (location.distanceTo(locationTemp) < 5) {
                        this.out = 0;
                        if (!this.enRuta) {
                            stopTimerTask();
                            stopGPS();
                        }

                        puntoMasCercano(location);
                    } else {
                        locationTemp = location;
                        comprobandoIntentos();
                    }
                }

            } else {
                comprobandoIntentos();
            }

        } else {
            Toast.makeText(this.contexto, (String) "Fallo con el GPS",
                    Toast.LENGTH_LONG).show();
            stopTimerTask();
            stopGPS();

        }
    }

    /**
     * Comprueba el tiempode intentos que llegamos para mostrar un error a los 60 segundos
     */
    public void comprobandoIntentos(){
        this.out += 1;
        this.texto.setText("");
        this.texto.append("Espera buscando posicion\n");
        this.texto.append("Tiempo buscando:" + out*intervaloCaptura);
        if (out >= this.timeout) {
            Toast.makeText(this.contexto, (String) "el GPS no es capaz de encontrar tu localización",
                    Toast.LENGTH_LONG).show();
            this.texto.setText("el GPS no es capaz de encontrar tu localización");
            out = 0;
            stopTimerTask();
            stopGPS();
            View v = ((Activity) contexto).findViewById(R.id.main_layout);
            Button siguiente = (Button) v.findViewById(R.id.botonInicial);
            Button play=(Button) v.findViewById(R.id.play_tour);
            Button info = (Button) v.findViewById(R.id.botonInfo);
            siguiente.setVisibility(View.GONE);
            info.setVisibility(View.GONE);
            play.setText("INICIAR EL TOUR");
            ((MainActivity) contexto).setEstado(0);


        }
    }

    /**
     * Funcion que determina cual es el punto mas cercano a la ruta des de nuestra posición actual.
     *
     * @param locationActual
     */
    public void puntoMasCercano(Location locationActual) {
        double distanciaMinima = this.distanciaMax;
        double direccion = 0;

        if (enRuta) {
            Location loc = new Location("");
            loc.setLongitude(puntoActual.getLongitud());
            loc.setLatitude(puntoActual.getLatitud());
            float distance = locationActual.distanceTo(loc);
            direccion = locationActual.bearingTo(loc);
            currentDegree = locationActual.bearingTo(loc);
            distanciaMinima = distance;
            this.texto.setText("");
            this.texto.append("distancia minima:" + (int)distanciaMinima + "\n");
            this.texto.append("direccion al punto:" + String.valueOf(direccion) + "\n");
            this.texto.append("Punto cercano encontrado:" + puntoActual.getId() + "\n");
            if (distanciaMinima <= 10) {
                if (this.sentido == -1) {
                    if (puntoActual.getId() == 1) {
                        this.sentido = 1;
                        activarInfo();
                    } else {
                    }
                    getNextPoint(locationActual);
                } else {

                    if (!puntoActual.getDescripcion().equals("")) {
                        activarInfo();

                    } else {

                    }
                    getNextPoint(locationActual);
                }


            }


        } else {
            for (PuntoGps point : puntosRuta) {

                //Buscamos el punto mas Cercano
                Location loc = new Location("");
                loc.setLongitude(point.getLongitud());
                loc.setLatitude(point.getLatitud());
                loc.setBearing(point.getGrados());
                float distance = locationActual.distanceTo(loc);
                if (distanciaMinima > distance) {
                    distanciaMinima = distance;
                    puntoActual = point;
                    //((MainActivity) contexto).setRotateImage(1);



                }


            }
            //si la distancia minima es la misma que al inicio ,estamos demasiado lejos de nuestra Ruta
            if (distanciaMinima == this.distanciaMax) {
                Toast.makeText(this.contexto, (String) "Lo siento estas demasiado lejos de la ruta",
                        Toast.LENGTH_LONG).show();
                View v = ((Activity) contexto).findViewById(R.id.main_layout);
                Button play = (Button) v.findViewById(R.id.play_tour);
                play.setText("INICIAR TOUR");
                this.texto.setText("");
                ((MainActivity) contexto).setEstado(0);


            }//si estamos cerca de nuestra ruta
            else {

                if (puntoActual.getId() == 1) {
                    Toast.makeText(this.contexto, (String) "Felicidades!! Estamos en el principio de nuestro viaje juntos, podemos empezar cuando quieras",
                            Toast.LENGTH_LONG).show();
                } else {

                    AlertDialog.Builder builder = new AlertDialog.Builder(contexto);
                    builder.setMessage("No estamos al principio de la ruta, quieres ir al Inicio o ir al Siguiente punto desde donde estas? ")
                            .setCancelable(false)
                            .setPositiveButton("Inicio", new DialogInterface.OnClickListener() {
                                public void onClick(DialogInterface dialog, int id) {
                                    enRuta = true;
                                    sentido = -1;
                                }
                            })
                            .setNegativeButton("Siguiente", new DialogInterface.OnClickListener() {
                                public void onClick(DialogInterface dialog, int id) {
                                    enRuta = true;
                                    sentido = 1;
                                }
                            });

                    Dialog dialog = builder.create();
                    dialog.show();
                    View v = ((Activity) contexto).findViewById(R.id.main_layout);
                    Button ini = (Button) v.findViewById(R.id.botonInicial);
                    ini.setVisibility(v.VISIBLE);
                }

            }
        }
    }


    /**
     * Busca el siguiente punto de la ruta
     * @param locationActual posicion actual
     */
    public void getNextPoint(Location locationActual) {
        int id = admin.getNextPoint(puntoActual.getId(), this.sentido);
        //si devuelve menos 1 es que estamos al final de la ruta
        if (id == -1) {
			View v = ((Activity) contexto).findViewById(R.id.main_layout);
            Button siguiente = (Button) v.findViewById(R.id.botonInicial);
            Button play=(Button) v.findViewById(R.id.play_tour);
            Button info = (Button) v.findViewById(R.id.botonInfo);
            siguiente.setVisibility(View.GONE);
            info.setVisibility(View.GONE);
            play.setText("INCIAR EL TOUR");
            ((MainActivity) contexto).setEstado(0);
            ((MainActivity) contexto).unregisterSensor();

			
            this.texto.setText("Felicidades has llegado al final de la ruta");
        } else {
            for (PuntoGps point : puntosRuta) {
                if (point.getId() == id) {
                    puntoActual = point;
                }
            }
        }
    }

    /**
     * Comprueba que se puede usar el GPS y de ser asi, ejecuta una timerTask, para hacer peticiones de posicion al GPS
     */

    public void iniciarNuevaRuta() {


        if (gps == null) {
            iniGps();
        }

        if (checkActiveGps()) {
            this.enRuta = true;
            startTimerLocation();
        }


    }


    /**
     * Detiene el gps
     */
    public void stopGPS() {

        if (this.gps != null) {
            this.gps.stopUsingGPS();
        }
    }

    /**
     * Detiene el timer que ejecuta la peticion de posicion del GPS.
     */

    public void pararNuevaRuta() {

        stopTimerTask();
        stopGPS();

    }

    /**
     * Activa el boton de informacion en caso de que haya informacion a mostrar en el punto
     */
    public void activarInfo() {
        pararNuevaRuta();
        leerMediaPuntoImportante();
        if (videoExist() || photoExist() || textExist() || Audioexist()) {
            View v = ((Activity) contexto).findViewById(R.id.main_layout);
            Button info = (Button) v.findViewById(R.id.botonInfo);
            info.setVisibility(v.VISIBLE);
			

        }
        if (this.puntoActual.getId() == 1) {
            this.texto.setText("Hemos llegado al principio de nuestro viaje juntos");
        } else {
            this.texto.setText("Hemos llegado a nuestra siguiente parada");
        }
        View v = ((Activity) contexto).findViewById(R.id.main_layout);
        Button inicial = (Button) v.findViewById(R.id.botonInicial);
        inicial.setEnabled(true);
    }

    /**
     * Devuelve en grados a donde estamos mirando con el dispositivo
     *
     * @return float con los grados
     */
    public float getGrados() {
        return this.currentDegree;
    }

    /**
     * Copia si no existian los ficheros necesarios (media) a la memoria interna.
     *
     * @param con Contexto para poder leer los assets de la aplicacion.
     */
    public void copyAssets(Context con) {

        CopyFilesToAssets runner = new CopyFilesToAssets(con, this);
        runner.execute();
    }

    /**
     * Inicia la BD, lee los puntos de la ruta y copia los ficheros a la memoria interna.
     *
     * @param con Contexto para poder leer los assets de la aplicacion.
     */
    public void initApp(Context con) {
        copyAssets(con);
        crearBd();
        leerPuntos();
        cargafinalizada();

    }

    /**
     * funcion que es llamada para controlar las tareas asincronas, que estas han finalizado
     */
    public void cargafinalizada() {
        contadorCarga += 1;
        if (contadorCarga == 2) {
            finishSplash();
        }
    }


    /**
     * Devuelve la descripcion del puntoActual
     * @return
     */
    public String getNombrePuntoCercano() {
        return puntoActual.getDescripcion();
    }

    /**
     * Lee la informacion de el punto importante
     */
    public void leerMediaPuntoImportante() {

        this.videoPuntoActual = admin.getVideo(puntoActual.getId());
        this.photosPuntoActual = admin.getPhotos(puntoActual.getId());
        this.textosPhotosPuntoActual=admin.getPhotosText(puntoActual.getId());
        this.textoPuntoActual = admin.getText(puntoActual.getId());
        this.audioPuntoActual=admin.getAudio(puntoActual.getId());

    }

    /**
     * Comprueba si hay video a mostrar
     * @return
     */
    public boolean videoExist() {
        if (!videoPuntoActual.equals(""))
            return true;
        return false;
    }

    /**
     * Comprueba si hay fotos a mostrar
     * @return
     */
    public boolean photoExist() {
        if (photosPuntoActual != null) {
            return true;

        }
        return false;
    }

    /**
     * Comprueba si hay texto a mostrar
     * @return
     */
    public boolean textExist() {
        if (!textoPuntoActual.equals("")) {
            return true;
        }
        return false;
    }

    /**
     * Comprueba si hay audio a mostrar
     * @return
     */
    public boolean Audioexist() {
        if (!audioPuntoActual.equals("")) {
            return true;
        }
        return false;
    }

    /**
     * Lee fichero de texto de la memoria interna
     * @param tv Donde se muestra el texto
     * @param title Donde se muestra el titulo del tetxo
     */
    public void leerTextoPuntoImportante(TextView tv, TextView title) {
        tv.setText("");
        title.setText(textoPuntoActual.toUpperCase());
        File file = new File(
                android.os.Environment.getExternalStorageDirectory()
                        + File.separator + "pollentia_files/txt" + File.separator + textoPuntoActual + ".txt");

        StringBuilder text = new StringBuilder();

        try {
            BufferedReader br = new BufferedReader(new FileReader(file));
            String line;

            while ((line = br.readLine()) != null) {
                text.append(line);
                text.append('\n');
            }
            br.close();
        } catch (IOException e) {
            //You'll need to add proper error handling here
        }
        tv.setText(text);
    }


    /**
     * prepara las imagenes a mostrar y el viewPager de la galeria
     * @param v el viewpager donde se mostraran las imagenes
     * @param activity activity que llama a la funcion
     * @param con contexto de la activity.
     */
    public void getphotosPuntoImportante(ViewPager v, FullScreenViewActivity activity, final Context con) {
        ImageUtils utils = new ImageUtils(activity.getApplicationContext());


        FullScreenImageAdapter adapter = new FullScreenImageAdapter(activity,
                utils.getFilePaths(photosPuntoActual), textosPhotosPuntoActual,this);



        v.setAdapter(adapter);
        View vista = ((Activity) con).findViewById(R.id.main_layout);
        CirclePageIndicator indicator = (CirclePageIndicator)vista.findViewById(R.id.indicator);
        indicator.setViewPager(v);
        ((CirclePageIndicator) indicator).setSnap(true);


        final float density = con.getResources().getDisplayMetrics().density;
        indicator.setBackgroundColor(0x00000000);
        indicator.setRadius(10 * density);
        indicator.setPageColor(0xFF888888);
        indicator.setFillColor(0x880000FF);
        indicator.setStrokeColor(0xFFFFFFFF);
        indicator.setStrokeWidth(2 * density);

        // displaying selected image first
        v.setCurrentItem(0);
    }



    /**
     * devuelve el video actual del punto en el que estamos.
     * @return
     */
    public Object getVideoPuntoImportante() {
        return videoPuntoActual;
    }

    /**
     * Devuelve la ruta.
     * @return
     */
    public ArrayList<PuntoGps> getRuta() {
        return puntosRuta;
    }


    /**
     * Crea un mapa de google a partir de la lista de puntos.
     * @param map mapa de google.
     */
    public void crearMap(GoogleMap map) {
        map.setMapType(GoogleMap.MAP_TYPE_HYBRID);
        map.setMyLocationEnabled(true);
        PolylineOptions options = new PolylineOptions().width(5).color(Color.BLUE).geodesic(true);
        for (int z = 0; z < puntosRuta.size(); z++) {

            LatLng point = new LatLng(puntosRuta.get(z).getLatitud(), puntosRuta.get(z).getLongitud());
            if (z == 0) {
                map.addMarker(new MarkerOptions().position(point).title("Pollentia")).showInfoWindow();
                CameraUpdate cameraUpdate = CameraUpdateFactory.newLatLngZoom(point, 18);
                map.animateCamera(cameraUpdate);
            }
            if (puntosRuta.get(z).getDescripcion() != "" && puntosRuta.get(z).getDescripcion() != "inicial") {
                MarkerOptions marker = (new MarkerOptions().position(point).title(puntosRuta.get(z).getDescripcion()));
                marker.icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_AZURE));
                map.addMarker(marker);
            }
            options.add(point);
        }
        map.addPolyline(options);
    }

    /**
     * Muestra un video.
     */
    public void openVideoFile() {
        Intent intent = new Intent();
        intent.setAction(android.content.Intent.ACTION_VIEW);

        File file = new File(android.os.Environment.getExternalStorageDirectory() + "/pollentia_files/video/" + videoPuntoActual);
        intent.setDataAndType(Uri.fromFile(file), "video/*");
        contexto.startActivity(intent);
    }

    /**
     * reproduce un fichero de audio.
     */
    public void openAudio() {
        this.audioPlaying=true;
        AudioManager audioManager = (AudioManager)contexto.getSystemService(Context.AUDIO_SERVICE);
        int currentVolume = audioManager.getStreamVolume(AudioManager.STREAM_RING);
        if (currentVolume<10) {
            audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, 10, 0);
        }
        String mp3=android.os.Environment.getExternalStorageDirectory() + "/pollentia_files/audio/" + audioPuntoActual;
        mp=new MediaPlayer();
        try {
            mp.setDataSource(mp3);
            mp.prepare();
            mp.start();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * reproduce un fichero de audio.
     */
    public void closeAudio() {
        if(this.audioPlaying) {
            this.audioPlaying = false;
            mp.stop();
        }
    }


    /**
     * comprueba si si esta reproduciendo el audio
     * @return
     */
    public boolean isAudioPlaying() {
        return audioPlaying;
    }

    /**
     * Comprueba si el dispositivo tiene internet.
     * @param con contexto de la activity.
     * @return boleano indicando si hay o no internet.
     */

    public boolean isConnectingToInternet(Context con) {
        ConnectivityManager connectivity = (ConnectivityManager) con.getSystemService(Context.CONNECTIVITY_SERVICE);
        if (connectivity != null) {
            NetworkInfo[] info = connectivity.getAllNetworkInfo();
            if (info != null)
                for (int i = 0; i < info.length; i++)
                    if (info[i].getState() == NetworkInfo.State.CONNECTED) {
                        return true;
                    }

        }
        return false;
    }


}
