package com.uva.rafael.tfg_goniometer.presenter;

import android.app.Fragment;
import android.content.pm.ActivityInfo;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.Toolbar;
import android.widget.ArrayAdapter;
import android.widget.Spinner;

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.MainActivity;
import com.uva.rafael.tfg_goniometer.view.adapters.PacienteListAdapter;
import com.uva.rafael.tfg_goniometer.view.fragments.NuevoPacienteFragment;
import com.uva.rafael.tfg_goniometer.view.fragments.PacientesFragment;
import com.uva.rafael.tfg_goniometer.view.fragments.PerfilPacienteFragment;

import java.util.ArrayList;

/**
 * Este es el Presentador asociado al <tt>Fragment PacientesFragment</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 de realizar la "configuración inicial" del <tt>Fragment</tt>, que,
 * consiste en actualizar el item del <tt>NavigationView</tt> al cuarto ítem del mismo, fijar la
 * orientación de la pantalla a "Portrait", para evitar que se pueda girar y fijar el <tt>Toolbar</tt>
 * como <tt>SupportActionBar</tt>.</p>
 *
 * <p>Ademas de eso, se encarga de configurar el <tt>RecyclerView</tt> inicial que se emplea para
 * mostrar el listado de pacientes (obteniendo el <tt>RecyclerView.Adapter</tt>), de "cargar" el
 * <tt>Spinner</tt> que muestra el criterio de filtrado de pacientes, y de gestionar cuando el
 * usuario ha pulsado sobre el botón para añadir un nuevo paciente, o para ver el perfil de uno
 * de los que se encuentran almacenados en la Base de Datos.</p>
 *
 * <p>Por último, se encarga de obtener el <tt>RecyclerView.Adapter</tt> correspondiente cada
 * vez que se recibe, desde la Vista, el evento <tt>onTextChanged</tt> sobre el cuadro de
 * búsqueda de pacientes.</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 PacientesFragment
 * @see MainModel
 * @see Toolbar
 * @see RecyclerView
 * @see Spinner
 * @see NuevoPacienteFragment
 * @see PacienteListAdapter
 * @see PerfilPacienteFragment
 * @version 1.0
 */

public class PacientesPresenter implements PresenterFunctions,
        PresenterFunctions.PacientesFunctions {

    private final MainModel model;
    // Referencias al fragmento (Vista) con el que esta asociado y al Modelo de la aplicación
    private PacientesFragment 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 PacientesPresenter(Fragment fragment, MainModel model) {
        this.fragment = (PacientesFragment) fragment;
        this.model = model;
    }

    /**
     * Método que realiza las operaciones iniciales cuando se crea el
     * <tt>Fragment PacientesFragment</tt>.
     *
     * <p>Se encarga de marcar el cuarto ítem del <tt>NavigationView</tt> (correspondiente a
     * <tt>PacientesFragment</tt>), fijar la orientación de la pantalla en "Portrait" y de utilizar
     * el <tt>Toolbar</tt> recibido como <tt>SupportActionBar</tt>.</p>
     *
     * <p>En este caso, sólo se mostrara el <tt>Toolbar</tt> en el caso de que el usuario no vaya a
     * almacenar una nueva medición en el sistema. Esto es así, para evitar que el usuario vaya a
     * otras partes de la aplicación, a través del mismo, sin haber completado la tarea de almacenar
     * la medición, lo que puede provocar problemas de comportamiento de la aplicación.</p>
     *
     * <p>Así, ademas, se guía al usuario paso a paso, lo que facilita el uso de la aplicación.</p>
     *
     * @param toolbar <tt>Toolbar</tt> a emplear como <tt>SupportActionBar</tt>
     */
    @Override
    public void setUpInitialSettings(Toolbar toolbar) {
        // Marcar el cuarto ítem del Navigationview
        ((MainActivity) fragment.getActivity()).setNavigationItem(3);

        // Fijar la orientación de la pantalla en "Portrait"
        fragment.getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);

        // Comprobar que el usuario NO va a almacenar una medición
        if (!model.getMostrarOpcionesAlmacenarMedicion()) {
            // Utilizar el toolbar como SupportActionBar
            AppCompatActivity activity = ((AppCompatActivity) fragment.getActivity());

            activity.setSupportActionBar(toolbar);

            if (activity.getSupportActionBar() != null)
                activity.getSupportActionBar().setHomeAsUpIndicator(R.mipmap.ic_menu);

            activity.getSupportActionBar().setDisplayHomeAsUpEnabled(true);
            activity.getSupportActionBar().setTitle("");
        }
    }

    /**
     * Método que se encarga de hacer una configuración inicial del <tt>RecyclerView</tt> que muestra
     * el listado de pacientes almacenados en la Base de Datos.
     *
     * <p>Realiza una operación para mejorar el rendimiento del mismo, así como añadirle el
     * <tt>LayoutManager</tt> y el <tt>RecyclerView.Adapter</tt>.</p>
     *
     * @param recyclerView <tt>RecyclerView</tt> a configurar
     * @param args <tt>Bundle</tt> con los argumentos para crear el <tt>PacienteListAdapter</tt>
     */
    @Override
    public void configRecyclerView(RecyclerView recyclerView, Bundle args) {
        // Operación para mejorar el rendimiento del RecyclerView
        recyclerView.setHasFixedSize(true);

        // Obtención del LayoutManager a partir de la actividad principal
        RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(fragment.getActivity());
        recyclerView.setLayoutManager(layoutManager);

        // Obtención del adaptador (PacienteListAdapter)
        RecyclerView.Adapter adapter = getPacienteListAdapter(null, args);
        recyclerView.setAdapter(adapter);
    }

    /**
     * Evento <tt>ClickListener</tt> que se llama cuando el usuario ha pulsado sobre el botón
     * para añadir un nuevo paciente en el <tt>PacientesFragment</tt>.
     *
     * Crea un nuevo <tt>Fragment</tt> de tipo <tt>NuevoPacienteFragment</tt>.
     */
    @Override
    public void onAñadirPacienteClicked() {
        // FloatingActionButton para crear un nuevo paciente
        NuevoPacienteFragment nuevoPacienteFragment = new NuevoPacienteFragment();

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

    /**
     * Método que se encarga de crear un <tt>ArrayAdapter</tt> para el <tt>Spinner</tt> recibido como
     * parámetro y adjunta un recurso de tipo layout que define cómo se muestra la opción
     * seleccionada en el control del mismo.
     *
     * <p>Ademas, especifica el layout que el <tt>Adapter</tt> usara para mostrar la lista de opciones
     * del <tt>Spinner</tt>.</p>
     *
     * <p>Por último, aplica el adaptador al <tt>Spinner</tt> en cuestión.</p>
     *
     * @param spinner <tt>Spinner</tt> sobre el que se aplica
     */
    @Override
    public void loadSpinner(Spinner spinner) {
        // Create an ArrayAdapter using the string array and a default spinner layout
        ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(fragment.getActivity(),
                R.array.filterPatients_array, R.layout.spinner_item);

        // Specify the layout to use when the list of choices appears
        adapter.setDropDownViewResource(R.layout.spinner_dropdown_item);

        // Apply the adapter to the spinner
        spinner.setAdapter(adapter);
    }

    /*
     * Método que se encarga de devolver una instancia de RecyclerView.Adapter (PacienteListAdapter
     * en este caso)
     */
    @org.jetbrains.annotations.Contract("null, _ -> !null; !null, _ -> !null")
    private PacienteListAdapter getPacienteListAdapter(ArrayList<ArrayList<String>> listadoPacientes,
                                                       Bundle args) {
        if (listadoPacientes == null)
            /*
             * Si no se especifica el listado de pacientes a mostrar, mostrar todos los que se
             * encuentran almacenados en la BD
             */
            return new PacienteListAdapter(model.getPacientes(), args, this);
        else
            return new PacienteListAdapter(listadoPacientes, args, this);
    }

    /**
     * Método que se encarga de re-asignar el <tt>RecyclerView.Adapter</tt> al <tt>RecyclerView</tt>
     * de <tt>PacientesFragment</tt>, encargado de mostrar el listado de pacientes que coinciden con
     * los criterios de búsqueda (o todos en caso de no especificar ninguno).
     *
     * <p>Primero, obtiene, a partir del Modelo, el listado de pacientes filtrado, según el criterio
     * seleccionado por el usuario en el <tt>Spinner</tt>, y la información introducida por el
     * mismo en el <tt>EditText</tt>.</p>
     *
     * <p>Una vez que se tiene el listado de pacientes ya filtrado, se crea el nuevo
     * <tt>RecyclerView.Adapter</tt> y se asigna al <tt>RecyclerView</tt> recibido como parámetro.</p>
     *
     * @param recyclerView <tt>RecyclerView</tt> sobre el que se aplica este método
     * @param texto Información introducida por el usuario en el cuadro de búsqueda del
     *              <tt>EditText</tt> de <tt>PacientesFragment</tt>
     * @param filtro Filtro seleccionado por el usuario en el <tt>Spinner</tt> de
     *               <tt>PacientesFragment</tt>
     * @param args <tt>Bundle</tt> con los argumentos para crear el <tt>PacienteListAdapter</tt>
     */
    @Override
    public void onTextChanged(RecyclerView recyclerView, String texto, String filtro, Bundle args) {
        recyclerView.setAdapter(getPacienteListAdapter(model.getPacientesFiltrado(texto, filtro), args));
    }

    /**
     * Evento <tt>ClickListener</tt> que se llama cuando el usuario ha pulsado sobre un elemento del
     * <tt>RecyclerView</tt> que muestra el listado de pacientes en <tt>PacientesFragment</tt>.
     *
     * Crea un nuevo <tt>Fragment</tt> de tipo <tt>PerfilPacienteFragment</tt>.
     *
     * @param nombrePaciente Nombre del paciente sobre el que el usuario ha pulsado
     * @param idPaciente ID del paciente sobre el que el usuario ha pulsado
     * @param args <tt>Bundle</tt> con los argumentos para crear el <tt>PerfilPacienteFragment</tt>
     */
    @Override
    public void onLayoutClicked(String nombrePaciente, int idPaciente, Bundle args) {
        PerfilPacienteFragment perfilPacienteFragment = new PerfilPacienteFragment();

        Bundle params;

        // Se comprueba si se han pasado parámetros para la creación del Fragment
        if (args != null)
            params = args;
        else
            // De no ser así, se crea una nueva instancia de tipo Bundle
            params = new Bundle();

        // Se pasa el nombre y el id del paciente para la creación del Fragment
        params.putString(MainModel.NOMBRE_PACIENTE, nombrePaciente);
        params.putInt(MainModel.ID_PACIENTE, idPaciente);

        perfilPacienteFragment.setArguments(params);

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

    /**
     * Perform any final cleanup before an activity is destroyed.
     *
     * Se encarga de liberar la referencia al <tt>Fragment</tt> con el que esta asociado. Ademas,
     * ya no es necesario seguir mostrando las opciones para almacenar una nueva medición, por lo
     * que también se encarga de notificar al Modelo sobre esta situación.
     */
    @Override
    public void onDestroy() {
        model.setMostrarOpcionesAlmacenarMedicion(false);
        model.setMostrarOpcionesBorrarPaciente(true);
        fragment = null;
    }
}
