package com.uva.rafael.tfg_goniometer.view.fragments;

import android.app.Fragment;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;

import com.uva.rafael.tfg_goniometer.R;
import com.uva.rafael.tfg_goniometer.dependencyInjection.App;
import com.uva.rafael.tfg_goniometer.interfaces.ViewFunctions;
import com.uva.rafael.tfg_goniometer.presenter.NuevaMedicionPresenter;

import java.util.ArrayList;

import javax.inject.Inject;

/**
 * Esta clase es el <tt>Fragment</tt> que se encarga de, por un lado, mostrar al usuario la
 * información del paciente al que se va a añadir una nueva medición, y, por otro lado, mostrar al
 * usuario las distintas opciones de las que dispone a la hora de añadir la medición. Por último,
 * recoge toda la información que ha introducido al usuario con respecto a la nueva medición y la
 * envía a <tt>NuevaMedicionPresenter</tt> para que, finalmente, sea almacenada en la Base de Datos.
 *
 * <p>Esta clase forma parte de la aplicación TFG-Goniometer, desarrollada para el Trabajo de
 * Fin de Grado - Grado en Ingeniería Informática (Universidad de Valladolid)</p>
 *
 * @author Rafael Matamoros Luque
 * @see Spinner
 * @see NuevaMedicionPresenter
 * @version 1.0
 */
public class NuevaMedicionFragment extends Fragment implements ViewFunctions.NuevaMedicionFunctions {

    /*
     * Presentador asociado con este Fragment (Vista). Su instancia se obtiene mediante Inversión
     * de Dependencias
     */
    @SuppressWarnings({"unused", "CanBeFinal"})
    @Inject
    NuevaMedicionPresenter presenter;

    /*
     * Referencias a los Spinners donde el usuario introduce la articulación sobre la que se ha
     * realizado la medición (indicado el lado del cuerpo en el que se encuentra y la articulación
     * en concreto) y el movimiento que ha realizado ésta (indicando si ha sido realizado única y
     * exclusivamente por el paciente o ha necesitado ayuda para realizar el movimiento)
     */
    private Spinner spinner_joint_side, spinner_joint, spinner_movement, spinner_movement_type;
    private LinearLayout jointLayout, jointOtherLayout, movementOtherLayout, movementTypeOtherLayout;

    // TextViews donde se muestra la información que se tiene del paciente en la Base de Datos
    private TextView nombre, edad, sexo, id, diagnostico, comentarios_adicionales, tags, fecha_hora,
            lectura_goniometro;

    /*
     * Parámetros que se obtienen en un Bundle a la hora de crear el Fragment.
     *
     * Por un lado, se obtiene nombrePaciente e idPaciente para poder realizar una consulta a la BD
     * y mostrar la información relativa a ese paciente.
     * Por otro lado, se obtiene la información relevante sobre la medición que se ha realizado,
     * como es la fecha y hora de la misma y la lectura final del goniómetro.
     */
    private String nombrePaciente, fechaHora;
    private int idPaciente;
    private double lecturaGoniometro;

    // Bundle donde almacenar los argumentos recibidos en la creación del Fragment
    private Bundle args;

    /**
     * Called when the activity is starting.
     *
     * <p>En este punto se comprueba si se ha pasado información al <tt>Fragment</tt> a través de un
     * Bundle, y, de ser así, es almacenada en las variables globales del <tt>Fragment</tt>.</p>
     *
     * @param savedInstanceState <tt>Bundle</tt>: If the activity is being re-initialized after
     *                           previously being shut down then this <tt>Bundle</tt> contains the
     *                           data it most recently supplied in
     *                           <tt>onSaveInstanceState(Bundle)</tt>. Otherwise it is null.
     */
    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Obtención de la instancia de NuevaMedicionPresenter
        ((App) getActivity().getApplication()).getComponent(this).injectNuevaMedicionFragment(this);

        args = getArguments();

        // Se comprueba si se ha pasado información en el Bundle
        if (args != null) {
            // Solicitar la información al Presentador
            presenter.onCreate();
        }
    }

    /**
     * Método llamado para instanciar el <tt>Fragment</tt> con su layout asociada
     * (R.layout.nueva_medicion_fragment, en este caso).
     *
     * Se encarga de mostrar al usuario la información que se tiene almacenada en la Base de Datos
     * sobre ese paciente, así como enviar al <tt>NuevaMedicionPresenter</tt> la nueva información
     * sobre la medición introducida por el usuario cuando éste indica que quiere guardar la medición
     * en la Base de Datos.
     *
     * @param inflater The LayoutInflater object that can be used to inflate any view in the
     *                 fragment.
     * @param container If non-null, this is the parent view that the fragment's UI should be
     *                  attached to. The fragment should not add the view itself, but this can be
     *                  used to generate the LayoutParams of the view.
     * @param savedInstanceState If non-null, this fragment is being re-constructed from a previous
     *                           saved state as given here. Return the View for the fragment's UI,
     *                           or null.
     */
    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
                             Bundle savedInstanceState) {

        // Inflar el layout del Fragment
        View vista = inflater.inflate(R.layout.nueva_medicion_fragment, container, false);

        // Bindeo de los TextView donde se muestra la información que se tiene del paciente en la BD
        nombre = (TextView) vista.findViewById(R.id.nombre_editText);
        edad = (TextView) vista.findViewById(R.id.edad_textView);
        sexo = (TextView) vista.findViewById(R.id.sexo_textView);
        id = (TextView) vista.findViewById(R.id.id_textView);
        diagnostico = (TextView) vista.findViewById(R.id.diagnostico_editText);
        comentarios_adicionales = (TextView) vista.findViewById(R.id.comentarios_adicionales_editText);
        tags = (TextView) vista.findViewById(R.id.tags_editText);
        fecha_hora = (TextView) vista.findViewById(R.id.fecha_textView);
        lectura_goniometro = (TextView) vista.findViewById(R.id.lectura_goniometro_textView);

        /*
         * Se envía la información en el Bundle al Presenter con el fin de obtener la información del
         * paciente de la BD y enviarla de vuelta al Fragment para que se muestre en los TextView
         */
        presenter.getInformacionPaciente(nombrePaciente, idPaciente, this.fechaHora,
                String.valueOf(this.lecturaGoniometro));

        /*
         * Bindeo de los Spinner donde se muestran las distintas opciones a la hora de almacenar una
         * medición
         */
        spinner_joint_side = (Spinner) vista.findViewById(R.id.spinner_joint_side);
        spinner_joint = (Spinner) vista.findViewById(R.id.spinner_joint);
        spinner_movement = (Spinner) vista.findViewById(R.id.spinner_movement);
        spinner_movement_type = (Spinner) vista.findViewById(R.id.spinner_movement_type);

        /*
         * Se envían las referencias a los Spinners a NuevaMedicionPresenter para asignarles los
         * arrays con los distintos items que tendrán cada uno de ellos.
         *
         * Inicialmente sólo se "cargan" por completo los Spinners "spinner_joint_side" y
         * "spinner_movement_type". El resto de Spinners se van cargando según las opciones que
         * vaya seleccionando el usuario, para evitar así, que se seleccione un movimiento en
         * particular en una articulación que no puede realizarlo.
         */
        presenter.loadSpinners(spinner_joint_side, spinner_joint, spinner_movement,
                spinner_movement_type);

        /*
         * Bindeo de los RelativeLayout que contienen al Spinner y a la imagen de la flecha que lo
         * acompaña, para que el usuario pueda pulsar sobre la flecha y se despliegue el "dropdown"
         * del Spinner también.
         */
        RelativeLayout spinner_joint_side_dropdown = (RelativeLayout) vista.findViewById
                (R.id.spinner_joint_side_dropdown);
        RelativeLayout spinner_joint_dropdown = (RelativeLayout) vista.findViewById
                (R.id.spinner_joint_dropdown);
        RelativeLayout spinner_movement_dropdown = (RelativeLayout) vista.findViewById
                (R.id.spinner_movement_dropdown);
        RelativeLayout spinner_movement_type_dropdown = (RelativeLayout) vista.findViewById
                (R.id.spinner_movement_type_dropdown);

        /*
         * Cuando el usuario pulsa sobre cualquier parte del RelativeLayout se interpreta como si
         * hubiera pulsado únicamente sobre el Spinner
         */
        spinner_joint_side_dropdown.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                spinner_joint_side.performClick();
            }
        });

        spinner_joint_dropdown.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                spinner_joint.performClick();
            }
        });

        spinner_movement_dropdown.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                spinner_movement.performClick();
            }
        });

        spinner_movement_type_dropdown.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                spinner_movement_type.performClick();
            }
        });

        /*
         * Bindeo de los LinearLayout que se muestran (junto con los EditText) cuando el usuario
         * selecciona la opción "Otro/a" en alguno de los Spinners
         */
        jointLayout = (LinearLayout) vista.findViewById(R.id.jointLayout);
        jointOtherLayout = (LinearLayout) vista.findViewById(R.id.jointOtherLayout);
        movementOtherLayout = (LinearLayout) vista.findViewById(R.id.movementOtherLayout);
        movementTypeOtherLayout = (LinearLayout) vista.findViewById(R.id.movementTypeOtherLayout);

        /*
         * Creación de "ItemSelectedListener" para cada uno de los Spinners
         */
        presenter.onItemSelectedListener(spinner_joint_side, spinner_joint);
        presenter.onItemSelectedListener(spinner_joint, spinner_movement);
        presenter.onItemSelectedListener(spinner_movement, null);
        presenter.onItemSelectedListener(spinner_movement_type, null);

        /*
         * Bindeo de los EditText que se muestran cuando el usuario selecciona la opción "Otro/a" en
         * alguno de los Spinners
         */
        final EditText jointOther = (EditText) vista.findViewById
                (R.id.otra_articulacion_editText);
        final EditText movementOther = (EditText) vista.findViewById
                (R.id.otro_movimiento_editText);
        final EditText movementTypeOther = (EditText) vista.findViewById
                (R.id.otro_tipo_movimiento_editText);

        // Bindeo del botón "HECHO"
        Button hecho = (Button) vista.findViewById(R.id.hecho);
        hecho.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                /*
                 * Cuando el usuario pulsa el botón, toda la información introducida es enviada a
                 * NuevaMedicionPresenter para que compruebe si es correcta y, de ser así, almacenarla
                 * en la BD
                 */
                presenter.onDoneClicked(nombrePaciente,
                        idPaciente,
                        fecha_hora.getText().toString(),
                        spinner_joint_side.getSelectedItem().toString(),
                        spinner_joint.getSelectedItem().toString(),
                        jointOther.getText().toString(),
                        spinner_movement.getSelectedItem().toString(),
                        movementOther.getText().toString(),
                        spinner_movement_type.getSelectedItem().toString(),
                        movementTypeOther.getText().toString(),
                        lectura_goniometro.getText().toString());
            }
        });
        return vista;
    }

    /**
     * Método que se encarga de asignar a las variables locales, la información contenida en el
     * <tt>Bundle</tt>
     *
     * @param constantesBundle <tt>ArrayList<String></tt> con las constantes para obtener la
     *                         información del <tt>Bundle</tt>
     */
    @Override
    public void setConstantesBundle(ArrayList<String> constantesBundle) {
        nombrePaciente = args.getString(constantesBundle.get(0));
        idPaciente = args.getInt(constantesBundle.get(1));
        lecturaGoniometro = args.getDouble(constantesBundle.get(2));
        fechaHora = args.getString(constantesBundle.get(3));
    }

    /**
     * Método que se encarga de escribir en el <tt>TextView</tt> indicado el texto que recibe como
     * parámetro.
     *
     * @param textView <tt>TextView</tt> en el que se debe escribir
     * @param text Texto a escribir
     */
    @Override
    public void setText(int textView, String text) {
        // Selección múltiple del TextView sobre el que escribir el texto
        switch (textView) {
            case 0:
                nombre.setText(text);
                break;
            case 1:
                edad.setText(text);
                break;
            case 2:
                sexo.setText(text);
                break;
            case 3:
                id.setText(text);
                break;
            case 4:
                diagnostico.setText(text);
                break;
            case 5:
                comentarios_adicionales.setText(text);
                break;
            case 6:
                tags.setText(text);
                break;
            case 7:
                fecha_hora.setText(text);
                break;
            case 8:
                lectura_goniometro.setText(text);
                break;
        }
    }

    /**
     * Método que se encarga de modificar la visibilidad del <tt>LinearLayout</tt> en función del
     * segundo parámetro que se recibe
     *
     * @param layout <tt>LinearLayout</tt> sobre el que se aplica este método
     * @param visible Visibilidad a asignar
     */
    @Override
    public void setLayoutVisibility(int layout, int visible) {
        // Selección múltiple del LinearLayout cuya visibilidad se va a modificar
        switch (layout) {
            case 0:
                jointLayout.setVisibility(visible);
                break;
            case 1:
                jointOtherLayout.setVisibility(visible);
                break;
            case 2:
                movementOtherLayout.setVisibility(visible);
                break;
            case 3:
                movementTypeOtherLayout.setVisibility(visible);
                break;
        }
    }

    /**
     * Método que muestra un mensaje de éxito cuando se ha logrado almacenar correctamente una
     * medición en la Base de Datos.
     */
    @Override
    public void displaySuccess() {
        Toast toast = Toast.makeText(getActivity(), R.string.measurement_success,
                Toast.LENGTH_LONG);

        TextView textView = (TextView) toast.getView().findViewById(android.R.id.message);

        if (textView != null)
            textView.setGravity(Gravity.CENTER); // Centrar el mensaje en el Toast

        toast.show();
    }

    /**
     * Método que muestra un mensaje de error cuando se ha intentado introducir una nueva medición
     * en la Base de Datos.
     */
    @Override
    public void displayErrorDatabase() {
        Toast toast = Toast.makeText(getActivity(), R.string.measurement_error_database,
                Toast.LENGTH_LONG);

        TextView textView = (TextView) toast.getView().findViewById(android.R.id.message);

        if (textView != null)
            textView.setGravity(Gravity.CENTER); // Centrar el mensaje en el Toast

        toast.show();
    }

    /**
     * Método que muestra un mensaje de error cuando el usuario ha introducido algún dato erróneo, o
     * no ha rellenado alguno de los campos obligatorios al almacenar una nueva medición.
     */
    @Override
    public void displayErrorUser() {
        Toast toast = Toast.makeText(getActivity(), R.string.user_error,
                Toast.LENGTH_LONG);

        TextView textView = (TextView) toast.getView().findViewById(android.R.id.message);

        if (textView != null)
            textView.setGravity(Gravity.CENTER); // Centrar el mensaje en el Toast

        toast.show();
    }

    /**
     * Perform any final cleanup before an activity is destroyed.
     */
    @Override
    public void onDestroy() {
        presenter.onDestroy();
        super.onDestroy();
    }
}
