package com.example.theviper;

import android.annotation.TargetApi;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Point;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.ShapeDrawable;
import android.graphics.drawable.shapes.PathShape;
import android.media.AudioManager;
import android.media.SoundPool;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.preference.PreferenceManager;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Display;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;

import java.io.FileInputStream;
import java.util.Vector;

/**
 * Created by Javier on 14/11/2015.
 */
public class VistaJuego extends View {

    private static final String TAG = "VistaJuego:";

    // Creamos hilo
    public ThreadJuego getThread() {
        return thread;
    }
    // Actividad padre
    private Activity padre;

    // //// THREAD Y TIEMPO //////
    // Thread encargado de procesar el juego
    private ThreadJuego thread = new ThreadJuego();
    // Cada cuanto queremos procesar cambios (ms)
    private static int PERIODO_PROCESO = 50;
    // Cuando se realizó el último proceso
    private long ultimoProceso = 0;

    // //// SNAKE //////
    private Grafico snake;// Gráfico de la serpiente
    private Drawable drawableSnake;
    private Vector<Grafico> snake_cola; // Cola de la serpiente
    private Vector<int[]> snake_cola_pos;// Vector de posiciones de cada parte de la cola
    private int long_cola;// determina la longitud de la cola
    private Drawable drawableCola;
    private int crecimiento;


    //Manejo de velocidad y dirección//
    private int velocidad_snake; // 5 velocidad de la serpiente
    // 0 para derecha, 1 abajo, 2 para izquierda, 3 para arriba
    private int direccion[] = { 0, 1, 2, 3 };
    // Puntero hacia direccion[]. La serpiente inicialimente va para arriba (direccion[0]=0=>arriba)
    public int currentDirection = 0;
    public boolean change_direction = false; // determina si la serpiente ha de cambiar de direccion
    private int snakePosX;
    private int snakePosY;

    //Manejo de la cola

    int[] pos_cabeza_anterior = new int[4]; // guardamos la posicion de la cabeza anterior
    private boolean addcola = false;	//true para alargar la cola
    //La serpiente es inicializada con toda su cola (de longitud el valor inicial de "longitud_cola_inicial")
    //Inicialmente la cola de la serpiente, aunque esté inicializada, está oculta. A medida que se ganan
    //puntos se desoculta la cola
    private int long_cola_inicial=500;
    private int ocultos;
    public int cont_retardo_cola=0; //cuanto es el retardo estre los graficos de la cola y la serpiente

    //COMIDA
    private Vector<Grafico> mil; //Grafico de Milhouse que se come la serpiente para ganar puntiación
    private Grafico milg;
    private Drawable drawableMil;
    private int contadorMil = 1000;
    private int milMax = 5;
    private int puntuacion = 0;


    // OBSTACULOS //
    // Tratamiento de los obstaculosteniendo en cuenta la resolucion de la pantalla, de la camara
    //y el offset de correccion del mapa
    private Vector<Grafico> Obstaculos;
    private Drawable drawableObstaculo;
    private int resolucion_camaraX = 1280;  //640
    private int resolucion_camaraY = 720;//960;  //480
    private int resolucion_pantallaX;
    private int resolucion_pantallaY;

    private int resolucionCasilla = 5;  // <----- // AJUSTAME!!!!!
    private int casillasX;
    private int casillasY;
    //private int factorAlto = 3;
    private int offsetX = 0;
    private int offsetY = 0;

    private int mapa;
    private Drawable drawableBorde;
    private Grafico graficoBorde;
    //Los obstáculos están presentes en el mapa almacenada en el fichero /sdcard/mapa.txt
    private static String FICHERO = Environment.getExternalStorageDirectory()+ "/TheViper/mapa.txt";
    private int[][] mapa_camara;

    //MULTIMEDIA
    private boolean musica;
    SoundPool soundPool;
    int idObstaculo, idComer;

    public VistaJuego(Context context, AttributeSet attrs) {
        super(context, attrs);

        setup(context);
        if(mapa==2)dibujarBorde();
        else if(mapa==3 || mapa==4){
        leerFichero();
        dibujarMapa();}
    }

    //////////////////////////////
    // INICIALIZACION DEL JUEGO //
    /////////////////////////////

    @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR2)
    private void setup(Context context){

        // Defino objetos a dibujar
        drawableObstaculo = context.getResources().getDrawable(
                R.drawable.obstaculo_5x5);//obstaculo_5x5
        drawableSnake = context.getResources().getDrawable(
                R.drawable.cabezabig);
        drawableMil = context.getResources().getDrawable(
                R.drawable.mil);
        drawableCola = context.getResources().getDrawable(R.drawable.mitadbig);
        snake = new Grafico(this, drawableSnake);

        mil = new Vector<Grafico>();
        milg = new Grafico(this, drawableMil);
        milg.setIncY(Math.random() * 4 - 2);
        milg.setIncX(Math.random() * 4 - 2);
        mil.add(milg);

        snake_cola = new Vector<Grafico>();
        Obstaculos = new Vector<Grafico>();
        puntuacion=0;

        // Obtengo resolucion de la pantalla y calculo variables que dependen de ella
        WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
        Display display = windowManager.getDefaultDisplay();
        Point size = new Point();
        display.getSize(size);

        //Probar valores de resolución de casillas:
        resolucionCasilla = snake.getAncho()/2;
        Log.d(TAG,"ResolucionCasilla= " + resolucionCasilla);

        resolucion_pantallaX = size.x;
        resolucion_pantallaY = size.y;
        resolucion_camaraX = resolucion_pantallaX;//
        resolucion_camaraY = resolucion_pantallaY;//

        offsetX = 0;
       // offsetY = resolucion_pantallaY/3;

        //resolucionCasilla = (int) resolucion_pantallaX/resolucion_pantallaX;
        casillasX = resolucion_camaraX / resolucionCasilla;
        casillasY = (resolucion_camaraY / resolucionCasilla); // * factorAlto;

        //Obtengo preferencias de usuario
        SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(context);
        velocidad_snake = Integer.parseInt( pref.getString("velocidad", "10") );
        ocultos = long_cola_inicial-Integer.parseInt( pref.getString("colainit", "1") );
        crecimiento = Integer.parseInt(pref.getString("crecimiento", "6"));
        mapa = Integer.parseInt(pref.getString("mapa", "1"));
        musica=pref.getBoolean("musica",true);
        if(musica)
        {
            soundPool = new SoundPool( 5, AudioManager.STREAM_MUSIC , 0);
            idObstaculo = soundPool.load(context, R.raw.sonido_obstaculo, 0);
            idComer = soundPool.load(context, R.raw.sonido_mil, 0);
        }
    }

    private void dibujarBorde(){
        //los bordes podrian cambiar en función del tamaño del dispositivo...
        Path path = new Path();

        path.moveTo((float) 0.3, (float) 0.3);
        path.lineTo((float) 0.3, (float) 71.7);
       // path.lineTo((float) 0.0, (float) resolucion_pantallaX-1);
        path.lineTo((float) 95.7, (float) 71.7);
        path.lineTo((float) 95.7, (float) 0.3);//resolucion_pantallaX-1
        path.lineTo((float) 0.3, (float) 0.3);
        ShapeDrawable dBorde = new ShapeDrawable(
                new
                        PathShape(path, 1, 1));
        dBorde.getPaint().setColor(Color.RED);
        dBorde.getPaint().setStyle(Paint.Style.STROKE);
        dBorde.setIntrinsicWidth(20);
        dBorde.setIntrinsicHeight(15);
        drawableBorde = dBorde;
        graficoBorde = new Grafico(this, drawableBorde);
    }

    private void leerFichero(){
        mapa_camara = new int[casillasX][casillasY];
        try {
            FileInputStream f = new FileInputStream(FICHERO);
            for (int y = 0; y < casillasY; y++ ){ //recorro las filas (Y)
                for (int x = 0; x < casillasX; x++ ){ //recorro las columnas (X)
                    char caracter = (char) f.read();
                    mapa_camara[x][y] =  (int) caracter;
                }
            }
            f.close();
        } catch (Exception e) {
        }
    }

    private void dibujarMapa(){
        for (int y = 0; y < casillasY; y++ ){ //recorro las filas (Y)
            for (int x = 0; x < casillasX; x++ ){ //recorro las columnas (X)
                if (mapa_camara[x][y] == 49) //Un 1 (0x49) me indica que la casilla hay un obstáculo
                {
                    Grafico obstaculo = new Grafico(this, drawableObstaculo);
                    obstaculo.setPosX(x*resolucionCasilla + offsetX);
                    obstaculo.setPosY(y*resolucionCasilla + offsetY);
                    Obstaculos.add(obstaculo);
                }
            }
        }
    }

    /////////////////////////////
    // MOVIMINTOS Y POSICIONES //
    /////////////////////////////

    synchronized protected void actualizaFisica() {
        long ahora = System.currentTimeMillis();// No hagas nada si el período
        // de proceso no se ha cumplido.
        if (ultimoProceso + PERIODO_PROCESO > ahora) {
            return;
        }
        // Para una ejecución en tiempo real calculamos retardo
        double retardo = (ahora - ultimoProceso) / PERIODO_PROCESO;
        ultimoProceso = ahora; // Para la próxima vez

        if (change_direction) {
            snake.setAngulo((int) (direccion[currentDirection]) * 90);
            change_direction = false;
        }

        double nIncX = velocidad_snake
                * Math.cos(Math.toRadians(snake.getAngulo()) * retardo);
        double nIncY = velocidad_snake
                * Math.sin(Math.toRadians(snake.getAngulo()) * retardo);

        snake.setIncX(nIncX);
        snake.setIncY(nIncY);

        //Vamos añadiendo comida
        milg = new Grafico(this, drawableMil);
        //milg.setIncY(Math.random() * 4 - 2);
        //milg.setIncX(Math.random() * 4 - 2);
        milg.setIncX(nIncX);
        milg.setIncY(nIncY);
        milg.setPosX(Math.random() * (resolucion_pantallaX - milg.getAncho()));
        milg.setPosY(Math.random() * (resolucion_pantallaY - milg.getAlto()));
        if(contadorMil>=10000) {
            mil.add(milg);
            contadorMil=0;
        }

        if(mil.size() < milMax)
        contadorMil++;

        actualizarPosicion(retardo);

        comprobarObstaculo();

        comprobarCola();

        comprobarComida(retardo);

        actualizarColas();

    }

    private void actualizarPosicion(double retardo){
        // Actualizamos posiciones X e Y
        snakePosX =(int) snake.getPosX();
        snakePosY =(int) snake.getPosY();

        switch(currentDirection)
        {
            case 0:	pos_cabeza_anterior[0] = snakePosX;//-10;
                pos_cabeza_anterior[1] = snakePosY;
                break;
            case 1:	pos_cabeza_anterior[0] = snakePosX;
                pos_cabeza_anterior[1] = snakePosY;//-10;
                break;
            case 2:	pos_cabeza_anterior[0] = snakePosX;//+10;
                pos_cabeza_anterior[1] = snakePosY;
                break;
            case 3:	pos_cabeza_anterior[0] = snakePosX;
                pos_cabeza_anterior[1] = snakePosY;//+10;
                break;

        }

        pos_cabeza_anterior[2] = (int) snake.getAngulo();
        pos_cabeza_anterior[3] = (int) snake.getRotacion();

        snake.incrementaPos(retardo); // incrementamos la posicion de la cabeza
        // snake.comprobarPosicion(retardo);

    }

    private void actualizarColas(){
        snake_cola.get(0).setPosX(pos_cabeza_anterior[0]);
        snake_cola.get(0).setPosY(pos_cabeza_anterior[1]);
        snake_cola.get(0).setAngulo(pos_cabeza_anterior[2]);
        snake_cola.get(0).setRotacion(pos_cabeza_anterior[3]);


        cont_retardo_cola++;
        if(cont_retardo_cola>=20/velocidad_snake)//
        {
            cont_retardo_cola=0;
            for (int i = long_cola - 1; i > 0; i--) { // ACTUALIZAMOS LA POSICIÓN DE LAS COLAS
                if (addcola == true) {
                    ocultos=ocultos-crecimiento;//2
                    addcola = false;
                } else {

                    snake_cola.get(i).setPosX((int) snake_cola.get(i - 1).getPosX());
                    snake_cola.get(i).setPosY((int) snake_cola.get(i - 1).getPosY());
                    snake_cola.get(i).setAngulo(
                            (int) snake_cola.get(i - 1).getAngulo());
                    snake_cola.get(i).setRotacion(
                            (int) snake_cola.get(i - 1).getRotacion());
                }
            }
        }
    }

    private void comprobarCola(){
        //COMPROBAR TOQUE CON COLA, SI LO HAY -> SALIR
        for(int a=2; a<(snake_cola.size()-ocultos);a++)//a=1
        {
            if (snake.getPosX() <= snake_cola.get(a).getPosX() + snake.getAncho() / 2
                    && snake.getPosX() > snake_cola.get(a).getPosX()  - snake.getAncho() / 2) {
                if ((snake.getPosY() >= snake_cola.get(a).getPosY() - snake.getAlto() / 2
                        && snake.getPosY() <= snake_cola.get(a).getPosY() + snake.getAlto() / 2)) {

                    salir();
                }
            }
        }
    }

    private void comprobarComida(double retardo){
        // COMPROBAR TOQUE CON COMIDA
        for(Grafico milg : mil) {
            if (snake.getPosX() <= milg.getPosX() + milg.getAncho() / 2
                    && snake.getPosX() > milg.getPosX() - milg.getAncho() / 2) {
                if ((snake.getPosY() >= milg.getPosY() - milg.getAlto() / 1.8 && snake
                        .getPosY() <= milg.getPosY() + milg.getAlto() / 1.8)) {

                    milg.variarPos(retardo);
                    puntuacion = puntuacion + velocidad_snake + crecimiento;
                    addcola = true;
                    if (musica)
                        soundPool.play(idComer, 1, 1, 1, 0, 1);

                }
            }
        }

        //para evitar que caiga dentro de un obstaculo:
        if(mapa==4)
        for (Grafico obs : Obstaculos){
            if (milg.getPosX() <= obs.getPosX() + obs.getAncho() / 2
                    && milg.getPosX() > obs.getPosX() - obs.getAncho() / 2) {
                if ((milg.getPosY() >= obs.getPosY() - obs.getAlto() / 1.8 && milg
                        .getPosY() <= obs.getPosY() + obs.getAlto() / 1.8)) {

                    milg.variarPos(retardo);

                }
            }
        }

    }

    private void comprobarObstaculo(){
        //COMPROBAR TOQUE CON OBSTÁCULO DEL MAPA
        if (mapa==1){
            comprobarSalida();
        }
        else if (mapa==2){
            if(snake.getPosX()>=(resolucion_pantallaX-10) || snake.getPosX()<= 10
                    || snake.getPosY()>=(resolucion_pantallaY-10) || snake.getPosY()<= 10 )
            {
                salir();
            }
        }

        else if(mapa==3){
            comprobarSalida();
        }
        else{
           /* try{

                //Comprobar a posición con cada uno de los bloques obstaculo sería muy pesado
                //Si conocemos la equivalencia de cada bloque del mapa con la posición de la serpiente
                //podemos simplemente comprobar el contenido de una posición concreta del ficher de texto
                //optimizando de este modo los recursos de la aplicación.

                //PROBLEMA: EL tamaño de las casillas del mapa varía en función del dispositivo utilizado
                //Solución: austar correctamente la resolución de cada Casilla en función del dispositivo (estamoos en ello)

                if (mapa_camara[(snakePosX-1)/resolucionCasilla][(snakePosY-1)/resolucionCasilla]==49){
                    salir();
                }
            } catch (Exception e) { //Se ha accedido a una posicion de mapa[][] que no existe
            }*/

            for(Grafico obs : Obstaculos) {
                if (snake.getPosX() <= obs.getPosX() + obs.getAncho() / 2
                        && snake.getPosX() > obs.getPosX() - obs.getAncho() / 2) {
                    if ((snake.getPosY() >= obs.getPosY() - obs.getAlto() / 2 && snake
                            .getPosY() <= obs.getPosY() + obs.getAlto() / 2)) {

                        salir();
                    }
                }
            }

            comprobarSalida();
        }
    }

    private void comprobarSalida(){
        if(snake.getPosX()>=(resolucion_pantallaX- snake.getAncho()/2)){
            snake.setPosX(0 + snake.getAncho());

            if(musica)
                soundPool.play(idObstaculo, 1, 1, 1, 0, 1);
        }
        else if (snake.getPosX()<= snake.getAncho()/2){
            snake.setPosX(resolucion_pantallaX - 0 - snake.getAncho());

            if(musica)
                soundPool.play(idObstaculo, 1, 1, 1, 0, 1);
        }
        else if(snake.getPosY()>=(resolucion_pantallaY-snake.getAlto()/2)){
            snake.setPosY(0 + snake.getAlto());
            if(musica)
                soundPool.play(idObstaculo, 1, 1, 1, 0, 1);
        }
        else if(snake.getPosY()<= snake.getAlto()/2 ){
            snake.setPosY(resolucion_pantallaY - snake.getAlto() - 0) ;
            if(musica)

                soundPool.play(idObstaculo, 1, 1, 1, 0, 1);
        }
    }


    ////////////////////////////
    // MANEJO DE LA SERPIENTE //
    ///////////////////////////

    @Override
    public boolean onTouchEvent (MotionEvent event) {
        super.onTouchEvent(event);
        float x = event.getX();
        float y = event.getY();
        switch (event.getAction()) {

            case MotionEvent.ACTION_UP:
                if(x<=resolucion_pantallaX/2) giro_sentido_antihorario(); else giro_sentido_horario();
                break;
        }

        return true;
    }

    public void giro_sentido_horario (){
        cont_retardo_cola=0;
        currentDirection = (currentDirection>=3)? 0 : currentDirection+1;
        change_direction=true;
    }

    public void giro_sentido_antihorario (){
        cont_retardo_cola=0;
        currentDirection = (currentDirection<=0)? 3 : currentDirection-1;
        change_direction=true;
    }

    ////////////////////////////
    // THREAD Y CICLO DE VIDA //
    ///////////////////////////

    class ThreadJuego extends Thread {
        private boolean pausa, corriendo;

        public synchronized void pausar() {
            pausa = true;
        }

        public synchronized void reanudar() {
            pausa = false;
            notify();
        }

        public void detener() {
            corriendo = false;
            if (pausa)
                reanudar();
        }

        @Override
        public void run() {
            corriendo = true;
            while (corriendo) {
                actualizaFisica();
                synchronized (this) {
                    while (pausa) {
                        try {
                            wait();
                        } catch (Exception e) {
                        }
                    }
                }
            }
        }
    }

    @Override
    protected void onSizeChanged(int ancho, int alto, int ancho_anter, // Seleccionamos
                                 int alto_anter) {
        super.onSizeChanged(ancho, alto, ancho_anter, alto_anter);
        // Una vez que conocemos nuestro ancho y alto.


        snake.setPosX(0.5 * (ancho - snake.getAncho()));
        snake.setPosY(0.5 * (alto - snake.getAlto()));

        for(Grafico milg : mil) {
            milg.setPosX(Math.random() * (ancho - milg.getAncho()));
            milg.setPosY(Math.random() * (alto - milg.getAlto()));
        }

        long_cola = long_cola_inicial; // longitud de cola inicial.
        snake_cola_pos = new Vector<int[]>();
        int[] pos_inicial = new int[4];
        // coordenada X que le restamos el la mitad del ancho de la cabeza de la serpiente X
        pos_inicial[0] = (int) snake.getPosX();
        pos_inicial[1] = (int) snake.getPosY(); // corrdenada Y
        pos_inicial[2] = 0; // Angulo
        pos_inicial[3] = 0; // Rotacion

        snake_cola_pos.add(pos_inicial);
        snake_cola = new Vector<Grafico>();
        for (int i = 0; i < long_cola; i++) {
            // a cada cola que añadamos le ponemos una posición inicial.
            Grafico cola = new Grafico(this, drawableCola);
            cola.setPosX(pos_inicial[0] - cola.getAncho() * i);
            cola.setPosY(pos_inicial[1]);
            cola.setAngulo(pos_inicial[2]);
            cola.setRotacion(pos_inicial[3]);
            snake_cola.add(cola);

        }
        ultimoProceso = System.currentTimeMillis();
        thread.start();

    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        snake.dibujaGrafico(canvas);
        for(Grafico milg : mil) {
            milg.dibujaGrafico(canvas);
        }

        if(mapa==2)
            graficoBorde.dibujaGrafico(canvas);

        if(mapa==3 || mapa==4)
            for (Grafico obstaculo : Obstaculos) {
                obstaculo.dibujaGrafico(canvas);
            }
        for (int i = snake_cola.size()-ocultos - 1; i >= 0; i--)
            snake_cola.get(i).dibujaGrafico(canvas);
    }

    public void setPadre(Activity padre) {
        this.padre = padre;
    }

    private void salir() {
        Bundle bundle = new Bundle();
        bundle.putInt("puntuacion", puntuacion);
        Intent intent = new Intent();
        intent.putExtras(bundle);
        padre.setResult(Activity.RESULT_OK, intent);
        padre.finish();
    }

}
