package com.uva.rafael.tfg_goniometer.presenter;

import android.app.Fragment;
import android.content.DialogInterface;
import android.os.Bundle;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AlertDialog;
import android.view.Gravity;
import android.view.View;
import android.widget.TableRow;
import android.widget.TextView;

import com.uva.rafael.tfg_goniometer.R;
import com.uva.rafael.tfg_goniometer.interfaces.PresenterFunctions;
import com.uva.rafael.tfg_goniometer.model.MainModel;
import com.uva.rafael.tfg_goniometer.view.fragments.NuevaMedicionFragment;
import com.uva.rafael.tfg_goniometer.view.fragments.PerfilPacienteFragment;

import java.text.DateFormat;
import java.util.ArrayList;

/**
 * Este es el Presentador asociado al <tt>Fragment PerfilPacienteFragment</tt> de la aplicación. Se
 * encarga de llevar a cabo toda la lógica asociada a las acciones del usuario realizadas en la IU.
 *
 * <p>En concreto, se encarga, inicialmente, de indicar a la Vista (<tt>PerfilPacienteFragment</tt>)
 * si es necesario mostrar el botón para añadir una nueva medición al paciente actual o no.</p>
 *
 * <p>Ademas, se encarga de obtener toda la información relativa a un paciente (tanto los datos
 * personales como el listado de mediciones que se han realizado sobre él, y enviar esa información
 * a la Vista (<tt>PerfilPacienteFragment</tt>) para que se la muestre al usuario.</p>
 *
 * <p>Por último, se encarga de gestionar cuando el usuario pulsa sobre el botón para añadir una
 * nueva medición o para borrar el perfil del paciente en concreto.</p>
 *
 * <p>Esta clase forma parte de la aplicación TFG-Goniometer, desarrollada para el Trabajo de
 * Fin de Grado - Grado en Ingeniería Informatica (Universidad de Valladolid)</p>
 *
 * @author Rafael Matamoros Luque
 * @see PerfilPacienteFragment
 * @see MainModel
 * @see NuevaMedicionFragment
 * @see DateFormat
 * @see TableRow
 * @see AlertDialog
 * @version 1.0
 */

public class PerfilPacientePresenter implements PresenterFunctions,
        PresenterFunctions.PerfilPacienteFunctions {

    private final MainModel model;
    // Referencias al fragmento (Vista) con el que esta asociado y al Modelo de la aplicación
    private PerfilPacienteFragment fragment;

    /**
     * Constructor principal de la clase
     *
     * @param fragment Fragmento (Vista) con la que mantiene una relación 1-a-1.
     * @param model    Modelo (único) de la aplicación.
     */
    public PerfilPacientePresenter(Fragment fragment, MainModel model) {
        this.fragment = (PerfilPacienteFragment) fragment;
        this.model = model;
    }

    /**
     * Método que se encarga de enviar a la Vista (<tt>PerfilPacienteFragment</tt>) los nombres
     * de las constantes para obtener los parámetros de tipo <tt>Bundle</tt> recibidos en la
     * creación del <tt>Fragment</tt>.
     */
    @Override
    public void onCreate() {
        // ArrayList a devolver con las constantes necesarias
        ArrayList<String> values = new ArrayList<>();

        values.add(MainModel.NOMBRE_PACIENTE);
        values.add(MainModel.ID_PACIENTE);
        values.add(MainModel.LECTURA_GONIOMETRO);
        values.add(MainModel.DATETIME);

        // Enviar las constantes a la Vista
        fragment.setConstantesBundle(values);
    }

    /**
     * Evento <tt>ClickListener</tt> que se llama cuando el usuario ha pulsado sobre el botón
     * para añadir una nueva medición en el <tt>PerfilPacienteFragment</tt>.
     * <p>
     * Crea un nuevo <tt>Fragment</tt> de tipo <tt>NuevaMedicionFragment</tt>.
     *
     * @param nombrePaciente Nombre del paciente al que se le quiere añadir la medición
     * @param idPaciente ID del paciente al que se le quiere añadir la medición
     * @param lecturaGoniometro Lectura del goniómetro obtenida durante la medición
     * @param fechaHora Fecha y hora en la que se realizó la medición
     */
    @Override
    public void onAlmacenarMedicionClicked(String nombrePaciente, int idPaciente,
                                           double lecturaGoniometro, String fechaHora) {
        // Añadir nueva medición al paciente
        NuevaMedicionFragment nuevaMedicionFragment = new NuevaMedicionFragment();


        Bundle params = new Bundle();

        // Se pasa en el Bundle el nombre e ID del paciente, la lectura del goniómetro y la fecha
        params.putString(MainModel.NOMBRE_PACIENTE, nombrePaciente);
        params.putInt(MainModel.ID_PACIENTE, idPaciente);
        params.putDouble(MainModel.LECTURA_GONIOMETRO, lecturaGoniometro);
        params.putString(MainModel.DATETIME, fechaHora);

        nuevaMedicionFragment.setArguments(params);

        fragment.getFragmentManager()
                .beginTransaction()
                .replace(R.id.content_frame, nuevaMedicionFragment)
                .addToBackStack("NuevaMedicionFragment")
                .commit();
    }

    /**
     * Método que se encarga de obtener la información relativa a un paciente a partir del nombre y
     * el id del mismo, mediante una consulta a la Base de Datos.
     *
     * Posteriormente, envía esta información a la Vista (<tt>PerfilPacienteFragment</tt>) para que
     * se la muestre al usuario.
     *
     * @param nombre Nombre del paciente
     * @param id ID del paciente
     */
    @Override
    public void mostrarInformacionPaciente(String nombre, int id) {
        // Obtención de la información del paciente de la BD
        ArrayList<String> informacionPaciente = model.getPacienteBy(nombre, id);

        if (informacionPaciente != null) {
            // Si se dispone de la información, enviarla a la Vista para que la muestre
            fragment.setText(0, nombre);
            fragment.setText(1, informacionPaciente.get(0));
            fragment.setText(2, informacionPaciente.get(1));
            fragment.setText(3, String.valueOf(id));
            fragment.setText(4, informacionPaciente.get(2));
            fragment.setText(5, informacionPaciente.get(3));
            fragment.setText(6, informacionPaciente.get(4));
        }
    }

    /**
     * Método que se encarga de obtener todas las mediciones que se han realizado sobre el paciente
     * en cuestión, introducirlas en objetos de tipo <tt>TableRow</tt> dandoles un formato
     * adecuado, para que se puedan mostrar correctamente en la pantalla del dispositivo, y enviarlas
     * de vuelta a la Vista (<tt>PerfilPacienteFragment</tt>) para que las añada al objeto
     * <tt>TableLayout</tt> que se utiliza para mostrar las mediciones del paciente.
     *
     * @param nombre Nombre del paciente del que recuperar las mediciones
     * @param id ID del paciente del que recuperar las mediciones
     */
    @Override
    public void mostrarMedicionesPaciente(String nombre, int id) {
        // Obtención del listado de mediciones del paciente a través de una consulta a la BD
        ArrayList<ArrayList<String>> medicionesPaciente = model.getMedicionesPacienteBy(nombre, id);

        if (medicionesPaciente != null) {
            // Se disponen mediciones de ese paciente

            // layout_width = "wrap_content" y layout_height = "wrap_content"
            TableRow.LayoutParams layoutParams = new TableRow.LayoutParams(
                    TableRow.LayoutParams.WRAP_CONTENT, TableRow.LayoutParams.WRAP_CONTENT);

            layoutParams.gravity = Gravity.CENTER_VERTICAL; //layout_gravity = "center_vertical"

            // Obtención de la escala para poder pasar de px a dp
            float scale = fragment.getResources().getDisplayMetrics().density;
            // 5dp:
            int padding_5dp = (int) (5 * scale + 0.5f);

            int dip_1 = (int) (1 * scale + 0.5f);

            // Definición de View para separar las columnas dentro de un TableRow
            TableRow.LayoutParams lpSeparadorVertical = new TableRow.LayoutParams(dip_1,
                    TableRow.LayoutParams.MATCH_PARENT);
            // Definición de View para separar los distintos TableRow dentro del TableLayout
            TableRow.LayoutParams lpSeparadorHorizontal = new TableRow.LayoutParams(
                    TableRow.LayoutParams.MATCH_PARENT, dip_1);

            // Recorrido de cada una de las mediciones del paciente
            for (ArrayList<String> medicion : medicionesPaciente) {

                /*
                 * Se utiliza el método "getDatosFormateados" de MainModel para obtener todos los
                 * datos relativos a una medición en el formato adecuado para mostrarlos por
                 * pantalla
                 */
                medicion = model.getDatosFormateados(medicion);

                // TableRow de la medición actual
                TableRow tableRow = new TableRow(fragment.getActivity());
                tableRow.setBackgroundColor(ContextCompat.getColor(fragment.getActivity(),
                        android.R.color.transparent));


                // Fecha - Hora de la medición
                TextView fechaHora_textView = new TextView(fragment.getActivity());
                // layout_width, layout_height y layout_gravity
                fechaHora_textView.setLayoutParams(layoutParams);
                fechaHora_textView.setGravity(Gravity.CENTER); // gravity
                // padding = "5dp"
                fechaHora_textView.setPadding(padding_5dp, padding_5dp, padding_5dp, padding_5dp);
                fechaHora_textView.setText(medicion.get(0));
                fechaHora_textView.setTextColor(ContextCompat.getColor(fragment.getActivity(),
                        android.R.color.black)); // textColor = "@android:color/black"


                // Articulación de la medición
                TextView articulacion_textView = new TextView(fragment.getActivity());
                // layout_width, layout_height y layout_gravity
                articulacion_textView.setLayoutParams(layoutParams);
                articulacion_textView.setGravity(Gravity.CENTER); // gravity
                // padding = "5dp"
                articulacion_textView.setPadding(padding_5dp, padding_5dp, padding_5dp, padding_5dp);
                articulacion_textView.setText(medicion.get(2));
                articulacion_textView.setTextColor(ContextCompat.getColor(fragment.getActivity(),
                        android.R.color.black)); // textColor = "@android:color/black"


                // Movimiento de la articulación
                TextView movimiento_textView = new TextView(fragment.getActivity());
                // layout_width, layout_height y layout_gravity
                movimiento_textView.setLayoutParams(layoutParams);
                movimiento_textView.setGravity(Gravity.CENTER); // gravity
                // padding = "5dp"
                movimiento_textView.setPadding(padding_5dp, padding_5dp, padding_5dp, padding_5dp);
                movimiento_textView.setText(medicion.get(3));
                movimiento_textView.setTextColor(ContextCompat.getColor(fragment.getActivity(),
                        android.R.color.black)); // textColor = "@android:color/black"

                // Lectura del goniómetro
                TextView lectura_goniometro_textView = new TextView(fragment.getActivity());
                // layout_width, layout_height y layout_gravity
                lectura_goniometro_textView.setLayoutParams(layoutParams);
                lectura_goniometro_textView.setGravity(Gravity.CENTER); // gravity
                // padding = "5dp"
                lectura_goniometro_textView.setPadding(padding_5dp, padding_5dp, padding_5dp,
                        padding_5dp);
                lectura_goniometro_textView.setText(medicion.get(1));
                lectura_goniometro_textView.setTextColor(ContextCompat.getColor(fragment.getActivity(),
                        android.R.color.black)); // textColor = "@android:color/black"


                // Views que separan las columnas del TableLayout
                View separadorVertical1 = new View(fragment.getActivity());
                // layout_width, layout_height y layout_gravity
                separadorVertical1.setLayoutParams(lpSeparadorVertical);
                separadorVertical1.setBackgroundColor(ContextCompat.getColor(fragment.getActivity(),
                        android.R.color.darker_gray));

                View separadorVertical2 = new View(fragment.getActivity());
                // layout_width, layout_height y layout_gravity
                separadorVertical2.setLayoutParams(lpSeparadorVertical);
                separadorVertical2.setBackgroundColor(ContextCompat.getColor(fragment.getActivity(),
                        android.R.color.darker_gray));

                View separadorVertical3 = new View(fragment.getActivity());
                // layout_width, layout_height y layout_gravity
                separadorVertical3.setLayoutParams(lpSeparadorVertical);
                separadorVertical3.setBackgroundColor(ContextCompat.getColor(fragment.getActivity(),
                        android.R.color.darker_gray));


                // View que separa las distintas filas del TableLayout
                View separadorHorizontal = new View(fragment.getActivity());
                // layout_width, layout_height y layout_gravity
                separadorHorizontal.setLayoutParams(lpSeparadorHorizontal);
                separadorHorizontal.setBackgroundColor(ContextCompat.getColor(fragment.getActivity(),
                        android.R.color.darker_gray));


                // Añadir todos los componentes al TableRow antes de añadirlo al TableLayout
                tableRow.addView(fechaHora_textView);
                tableRow.addView(separadorVertical1);
                tableRow.addView(articulacion_textView);
                tableRow.addView(separadorVertical2);
                tableRow.addView(movimiento_textView);
                tableRow.addView(separadorVertical3);
                tableRow.addView(lectura_goniometro_textView);

                // Añadir el TableRow y el View separador al TableLayout de la Vista
                fragment.addView(tableRow);
                fragment.addView(separadorHorizontal);
            }
        }
    }

    /**
     * Evento <tt>ClickListener</tt> que se llama cuando el usuario ha pulsado sobre el botón para
     * borrar el perfil del paciente en el <tt>PerfilPacienteFragment</tt>.
     *
     * <p>Se encarga de crear un dialogo que envía a la Vista para que se lo muestre al usuario, con
     * el fin de confirmar que quiere borrar el paciente junto con todas las mediciones que se le
     * han realizado.</p>
     *
     * <p>Si el usuario confirma que desea eliminar el perfil del paciente, el método se encarga de
     * borrar el <tt>Fragment</tt> actual y mostrar el listado de pacientes actualizado con el
     * paciente ya eliminado.</p>
     *
     * @param nombre Nombre del paciente a eliminar
     * @param id ID del paciente a eliminar
     */
    @Override
    public void onDeletePatientClicked(final String nombre, final int id) {
        // Dialogo que se mostrara al usuario
        AlertDialog dialog;

        // 1. Instantiate an AlertDialog.Builder with its constructor
        AlertDialog.Builder builder = new AlertDialog.Builder(fragment.getActivity());

        // 2. Chain together various setter methods to set the dialog characteristics
        builder.setTitle(nombre + " - ID " + id);
        builder.setMessage(R.string.confirmacion_borrado_paciente);

        // 3. Add the buttons
        builder.setPositiveButton(R.string.eliminar, new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialogInterface, int i) {
                // User clicked ELIMINAR button

                // Se intenta eliminar el paciente de la BD a través del Modelo
                if (model.onDeletePatient(nombre, id) == 1) {
                    /*
                     * El paciente se ha podido eliminar con éxito. Notificar a la vista que
                     * muestre un mensaje indicando esta situación al usuario
                     */
                    fragment.displaySuccess();

                    /*
                     * Recuperar el Fragment que se encontraba en primera posición en el BackStack
                     * con el listado de pacientes actualizado (después de que el paciente en
                     * cuestión haya sido eliminado)
                     */
                    fragment.getFragmentManager().popBackStack();

                    // Borrar el Fragment actual
                    fragment.getFragmentManager()
                            .beginTransaction()
                            .remove(fragment)
                            .commit();
                } else
                    /*
                     * Se ha producido un error al eliminar el paciente de la BD. Notificar a la
                     * Vista para que informe de esta situación al usuario
                     */
                    fragment.displayError();
            }
        });

        builder.setNegativeButton(R.string.cancelar, new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialogInterface, int i) {
                // User clicked CANCELAR button

                // No realizar ninguna acción. Simplemente cerrar el dialogo
            }
        });

        // 4. Get the AlertDialog from create()
        dialog = builder.create();

        // Enviar el dialogo a la Vista para que se lo muestre al usuario
        fragment.displayResult(dialog);
    }

    /**
     * Método que se encarga de notificar a la Vista (<tt>PerfilPacienteFragment</tt>) si es
     * necesario mostrar el botón para añadir una nueva medición al paciente actual o no.
     */
    @Override
    public void onCreateView() {
        fragment.setMostrarOpcionesNuevaMedicion(model.getMostrarOpcionesAlmacenarMedicion());
        fragment.setMostrarOpcionesBorrarPaciente(model.getMostrarOpcionesBorrarPaciente());
    }

    /**
     * Perform any final cleanup before an activity is destroyed.
     *
     * Se encarga de liberar la referencia al <tt>Fragment</tt> con el que esta asociado.
     */
    @Override
    public void onDestroy() {
        fragment = null;
    }
}
