package com.example.unodtantos.appqueology2.controlador;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.View;

import com.example.unodtantos.appqueology2.R;
import com.example.unodtantos.appqueology2.modelo.Arista;
import com.example.unodtantos.appqueology2.modelo.Vertice;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Stack;


/**
 * Created by unodtantos on 06/04/2015.
 */
public class GrafoView extends View {

    /*--------------------------------------------------------------------------------------------*/

    // <editor-fold defaultstate="collapsed" desc="Atributos de la clase">

    private Controlador controlador;
    private String nombre_ue;
    private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    private RectF  ovalE;
    private RectF  ovalEE;
    private Paint white = new Paint();
    private Paint white2 = new Paint();
    private Paint blue = new Paint();
    private Paint azul = new Paint();
    private Paint negro = new Paint();
    private Paint gris = new Paint();
    private ScaleGestureDetector mScaleDetector;
    private float mScaleFactor = 1.f;
    private Canvas canvas_general;
    private float xxx;
    private float yyy;
    private String ue_seleccionada;
    private ArrayList<RectF> puntos_blancos;
    private ArrayList<RectF> puntos_negros;
    private ArrayList<RectF> puntos_negros1;
    private ArrayList<String> nombres;
    private ArrayList<RectF> puntos_blancos1;
    private LinkedList lista_vertices;

    // </editor-fold>

    /*--------------------------------------------------------------------------------------------*/

    // <editor-fold defaultstate="collapsed" desc="Constructor de la clase">

    /**
     * Constructor de la clase.
     * @param context
     * @param nombre_proyecto
     * @param nom_ue
     */
    public GrafoView(Context context, String nombre_proyecto, String nom_ue) {
        super(context);

        inicializacion(context, nombre_proyecto, nom_ue);
    }

    // </editor-fold>

    /*--------------------------------------------------------------------------------------------*/

    // <editor-fold defaultstate="collapsed" desc="Inicializacion variables">

    /**
     * Método que se encarga de inicializar las variables necesarias para el correcto funcionamiento
     * de la clase.
     * @param context
     * @param nombre_proyecto
     * @param nombre_ue
     */
    private void inicializacion(Context context, String nombre_proyecto, String nombre_ue){

        this.nombre_ue = nombre_ue;
        this.controlador = (Controlador) context;
        this.controlador.cargarEstructura(nombre_proyecto);
        this.lista_vertices = controlador.obtenerVertices();
        this.mPaint.setColor(Color.BLACK);
        this.mPaint.setAntiAlias(true);
        this.white.setColor(Color.WHITE);
        this.white2.setColor(Color.WHITE);
        this.mPaint.setAntiAlias(true);
        this.mPaint.setTextSize(50);
        this.white.setAntiAlias(true);
        this.white2.setAntiAlias(true);
        this.white2.setTextSize(50);
        this.blue.setColor(Color.BLUE);
        int z = context.getResources().getColor(R.color.azul);
        this.azul.setColor(z);
        int g = context.getResources().getColor(R.color.button_normal);
        this.gris.setColor(g);
        int n = context.getResources().getColor(R.color.negro);
        this.negro.setColor(n);
        this.mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());
    }

    // </editor-fold>

    /*--------------------------------------------------------------------------------------------*/

    // <editor-fold defaultstate="collapsed" desc="Capturando eventos pantalla">

    /**
     * Método que se encarga de capturar el evento onTouch.
     * @param ev
     * @return
     */
    @Override
    public boolean onTouchEvent(MotionEvent ev) {

        mScaleDetector.onTouchEvent(ev);

        int action = ev.getAction() ;
        float x = ev.getX() ;
        float y = ev.getY() ;

        int width = canvas_general.getWidth() ;
        int height = canvas_general.getHeight() ;

        switch(action) {
            case MotionEvent.ACTION_DOWN:

                for(int i=0; i<puntos_blancos.size();i++){
                    if(puntos_blancos.get(i).contains(x/mScaleFactor, y/mScaleFactor)){

                        xxx=x;
                        yyy=y;
                        nombre_ue = "";
                        invalidate();
                    }
                }
        }
        return true;
    }

    // </editor-fold>

    /*--------------------------------------------------------------------------------------------*/

    // <editor-fold defaultstate="collapsed" desc="Ajustar tamaño canvas">

    /**
     * Método que se encarga de ajustar el tamaño del Canvas.
     * @param widthMeasureSpec
     * @param heightMeasureSpec
     */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

        Controlador controlador = (Controlador) getContext();
        ArrayList medidas = controlador.getMedidasPantalla();

        int ancho_max = calcularAnchoMaximo();
        int alto_max = calcularAltoMaximo();

        int width = (500 * ancho_max)^ancho_max;
        int height = 300 * alto_max;

        if((int) medidas.get(0) > width){
            width = (int) medidas.get(0);
        }

        if((int) medidas.get(1) > height){
            height = (int) medidas.get(1);
        }

        setMeasuredDimension(width, height);
    }

    /**
     * Método que se encarga de calcular el ancho máximo que debe tener en función del número de
     * UEs y relaciones almacendadas.
     * @return
     */
    private int calcularAnchoMaximo(){

        ArrayList<ArrayList> momentos = controlador.obtenerMomentos();
        int ancho=0;
        int ancho_max=0;

        for(int i=0;i<momentos.size();i++){
            ArrayList ar = (ArrayList) momentos.get(i).get(1);
            ancho = ar.size();
            if(ancho > ancho_max){
                ancho_max = ancho;
            }
        }
        return ancho_max;
    }

    /**
     * Método que se encarga de calcular el alto máximo que debe tener en función del númeor de UEs
     * y relaciones almacenadas.
     * @return
     */
    private int calcularAltoMaximo(){

        ArrayList<ArrayList> momentos = controlador.obtenerMomentos();
        int alto_max = momentos.size();

        return alto_max;
    }

    // </editor-fold>

    /*--------------------------------------------------------------------------------------------*/

    // <editor-fold defaultstate="collapsed" desc="OnDraw">

    /**
     * Método que se encarga de dibujar los vértices y las aristas.
     * @param canvas
     */
    @Override
    public void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        canvas.save();
        canvas.scale(mScaleFactor, mScaleFactor);

        canvas_general = canvas;

        puntos_negros = new ArrayList<>();
        puntos_negros1 = new ArrayList<>();
        nombres = new ArrayList<>();
        puntos_blancos = new ArrayList<>();
        puntos_blancos1 = new ArrayList<>();

        crearVertices();
        crearDibujarAristas(canvas);
        dibujarVertices(canvas);

        canvas.restore();
    }

    // </editor-fold>

    /*--------------------------------------------------------------------------------------------*/

    // <editor-fold defaultstate="collapsed" desc="Creando y dibujando vertices y aristas">

    private void marcarVerticesComoNoVisitados(){

        LinkedList lista_vertices = controlador.obtenerVertices();
        for (int i=0;i<lista_vertices.size(); i++){
            Vertice w = (Vertice) lista_vertices.get(i);
            w.setState(0);
        }
    }

    /**
     * Método que se encarga de crear los diferentes figuras que representan a los vértices.
     */
    private void crearVertices(){

        Vertice v_inicio = controlador.obtenerVerticeInicio();
        nombres.add("Inicio");
        ovalE = new RectF((float) 300 - 78, (float) 47, (float) 300 + 78, (float) 153);
        ovalEE = new RectF((float) 300 - 75, (float) 50, (float) 300 + 75, (float) 150);
        puntos_negros1.add(ovalE);
        puntos_blancos1.add(ovalEE);

        Stack stack = new Stack();
        stack.push(v_inicio);
        v_inicio.setState(1);

        int factor_ancho = 0;
        int factor_alto = 335;
        boolean control = false;

        marcarVerticesComoNoVisitados();

        RectF oval;
        RectF oval1;

        while(!stack.isEmpty()) {
            Vertice v = (Vertice) stack.peek();
            Vertice vv = controlador.obtenerAdyacentesHijosNoVisitados(v);
            if(vv!=null){
                vv.setState(2);
                nombres.add(vv.getElemento().toString());
                oval = new RectF((float) 300 - 128 + factor_ancho, (float) 47 +  factor_alto, (float) 300 + 128 +  factor_ancho, (float) 153 + factor_alto);
                oval1 = new RectF((float) 300 - 125 + factor_ancho, (float) 50 +  factor_alto, (float) 300 + 125 + factor_ancho, (float) 150 + factor_alto);
                puntos_negros.add(oval);
                puntos_blancos.add(oval1);
                puntos_negros1.add(oval);
                puntos_blancos1.add(oval1);

                factor_alto = factor_alto + 300;
                control = true;

                stack.push(vv);

            }else{
                factor_alto = factor_alto - 300;
                if (control){
                    factor_ancho = factor_ancho + 300;
                    control = false;
                }
                stack.pop();
            }
        }
    }

    /**
     * Método que se encarga de crear y dibujar las aristas.
     * @param canvas
     */
    private void crearDibujarAristas(Canvas canvas){

        LinkedList aristas = controlador.obtenerAristas();

        Paint p_b = new Paint();
        p_b.setStrokeWidth(4);
        p_b.setColor(Color.BLACK);

        int contador = 0;

        for(int i=0;i<aristas.size();i++){
            Arista a = (Arista) aristas.get(i);
            Vertice v1 = a.getV1();
            Vertice v2 = a.getV2();
            for(int j=0;j<puntos_negros1.size();j++){
                if(v1.getElemento().toString().equals(nombres.get(j).toString())){
                    for(int l=0;l<puntos_negros1.size();l++){
                        if(v2.getElemento().toString().equals(nombres.get(l).toString())){
                            canvas.drawLine(puntos_negros1.get(j).centerX(), puntos_negros1.get(j).centerY(), puntos_negros1.get(l).centerX(), puntos_negros1.get(l).centerY(), p_b);
                        }
                    }
                    contador++;
                    if(contador==3){
                        contador=0;
                    }
                }
            }
        }
    }

    /**
     * Es el método que se encarga de dibujar las diferentes figuras que representan a los vértices.
     * Se hace por separado del crear los nodos para que las lineas que representan a las aristas
     * queden por debajo de los ovalos que representan a los vértices.
     * @param canvas
     */
    public void dibujarVertices(Canvas canvas){

        Vertice v_inicio = controlador.obtenerVerticeInicio();

        canvas.drawRect(ovalE, negro);  // Se dibuja el inicial
        String input = "Inicio";
        canvas.drawText(input, (float) (ovalE.centerX() - 10 * input.length()), ovalE.centerY() + 15, white2);

        Stack stack1 = new Stack();

        marcarVerticesComoNoVisitados();

        stack1.push(v_inicio);
        v_inicio.setState(1);

        int cont = 0;
        while(!stack1.isEmpty()) {
            Vertice v = (Vertice) stack1.peek();
            Vertice vv = controlador.obtenerAdyacentesHijosNoVisitados(v);
            if(vv!=null){
                vv.setState(2);
                canvas.drawOval(puntos_negros.get(cont), mPaint);
                if(puntos_blancos.get(cont).contains(xxx/mScaleFactor,yyy/mScaleFactor) || vv.getElemento().toString().equals(nombre_ue)){

                    canvas.drawOval(puntos_blancos.get(cont), gris);
                    canvas.drawText(vv.getElemento().toString(), (float) puntos_negros.get(cont).centerX() - 10 * (vv.getElemento().toString().length()), puntos_negros.get(cont).centerY() + 15, white2);
                    ue_seleccionada = vv.getElemento().toString();
                    nombre_ue = ue_seleccionada;
                    controlador.setUe_seleccionada_grafo(ue_seleccionada);
                }else {
                    canvas.drawOval(puntos_blancos.get(cont), white);
                    canvas.drawText(vv.getElemento().toString(), puntos_negros.get(cont).centerX()-10*(vv.getElemento().toString().length()), puntos_negros.get(cont).centerY()+15, mPaint);
                }
                cont++;
                stack1.push(vv);
            }else{
                stack1.pop();
            }
        }
    }

    // </editor-fold>

    /*--------------------------------------------------------------------------------------------*/

    // <editor-fold defaultstate="collapsed" desc="Escalando la pantalla">

    /**
     * Es el método que se encarga de escalar la vista a modo de zoom.
     */
    private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
        @Override
        public boolean onScale(ScaleGestureDetector detector) {

            mScaleFactor *= detector.getScaleFactor();
            mScaleFactor = Math.max(0.1f, Math.min(mScaleFactor, 5.0f));
            invalidate();

            return true;
        }
    }

    public void invalidar(){
        invalidate();
    }

    // </editor-fold>

    /*--------------------------------------------------------------------------------------------*/

    // <editor-fold defaultstate="collapsed" desc="Getters and setters">

    public String getNombreUe() {
        return nombre_ue;
    }

    public void setNombreUe(String nombre_ue) {
        this.nombre_ue = nombre_ue;
    }

    public Canvas getCanvas_general() {
        return canvas_general;
    }

    public void setCanvas_general(Canvas canvas_general) {
        this.canvas_general = canvas_general;
    }

    public float getXxx() {
        return xxx;
    }

    public void setXxx(float xxx) {
        this.xxx = xxx;
    }

    public float getYyy() {
        return yyy;
    }

    public void setYyy(float yyy) {
        this.yyy = yyy;
    }

    // </editor-fold>

    /*--------------------------------------------------------------------------------------------*/
}