package com.uva.rafael.tfg_goniometer.model;

import android.content.Context;
import android.content.res.Resources;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.support.annotation.Nullable;

import com.uva.rafael.tfg_goniometer.R;
import com.uva.rafael.tfg_goniometer.data.Contract;
import com.uva.rafael.tfg_goniometer.data.GoniometerDBHelper;
import com.uva.rafael.tfg_goniometer.data.entities.ArticulacionInvolucrada;
import com.uva.rafael.tfg_goniometer.data.entities.Medicion;
import com.uva.rafael.tfg_goniometer.data.entities.Movimiento;
import com.uva.rafael.tfg_goniometer.data.entities.Paciente;
import com.uva.rafael.tfg_goniometer.data.enums.Articulacion;
import com.uva.rafael.tfg_goniometer.data.enums.Lado;
import com.uva.rafael.tfg_goniometer.data.enums.Modo;
import com.uva.rafael.tfg_goniometer.data.enums.Sexo;
import com.uva.rafael.tfg_goniometer.data.enums.TipoMovimiento;
import com.uva.rafael.tfg_goniometer.interfaces.ModelFunctions;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Locale;

/**
 * Esta clase es el principal y único Modelo de la aplicación. Implementa el patrón de diseño
 * <tt>Singleton</tt>, por lo que sólo existe una única instancia de esta clase en toda la
 * aplicación.
 * <p>
 * <p>Se encarga, principalmente, de servir como intermediario entre los Presentadores y la clase
 * <tt>SQLiteHelper</tt> que se ocupa del manejo de la Base de Datos, haciendo consultas, bien para
 * obtener información de la misma, bien para almacenar información (como <tt>Pacientes</tt> o
 * <tt>Mediciones</tt>) introducida por el usuario, o bien para eliminar información de la misma.</p>
 * <p>
 * <p>Ademas de eso, se encarga de gestionar si se debe mostrar o no el botón de añadir una nueva
 * medición a un paciente en particular.</p>
 * <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
 * @version 1.0
 * @see SQLiteDatabase
 * @see Cursor
 * @see GoniometerDBHelper
 * @see Paciente
 * @see Medicion
 */

public class MainModel implements ModelFunctions {

    // Constantes para pasar parámetros a través de un objeto Bundle en la creación de Fragments
    // Lectura obtenida del goniómetro
    public final static String LECTURA_GONIOMETRO = "LECTURA_GONIOMETRO";
    // Nombre del paciente
    public final static String NOMBRE_PACIENTE = "NOMBRE_PACIENTE";
    // ID del paciente
    public final static String ID_PACIENTE = "ID_PACIENTE";
    // Fecha y hora en la que se realizó una medición
    public final static String DATETIME = "DATETIME";

    // Única instancia del Modelo (patrón Singleton)
    private final static MainModel ourInstance = new MainModel();

    // Referencia a la clase SQLiteHelper para acceder a la Base de Datos
    private GoniometerDBHelper goniometerDBHelper;

    // Variable para gestionar si se debe mostrar o no el botón de añadir una nueva medición
    private boolean mostrarOpcionesAlmacenarMedicion = false;
    // Variable para gestionar si se debe mostrar o no el botón para borrar el perfil de un paciente
    private boolean mostrarOpcionesBorrarPaciente = true;

    // Variable empleada para recuperar Strings del fichero res/values/strings.xml
    private Resources resources;

    /**
     * Único y principal constructor de la clase. Simplemente devuelve la referencia a su única
     * instancia posible.
     */
    public static MainModel getInstance() {
        return ourInstance;
    }

    /**
     * Método que proporciona el Context a la clase SQLiteHelper para acceder a la Base de Datos. Se
     * encarga de crear una nueva instancia de esta clase a partir del mismo.
     * <p>
     * <p>Ademas, utiliza el <tt>Context</tt> recibido como parámetro para obtener los
     * <tt>Resources</tt> y, así, recuperar los <tt>Strings</tt> del fichero
     * <tt>res/values/strings.xml</tt>.</p>
     *
     * @param context Contexto de la aplicación (MainActivity)
     */
    public void setInitialSettings(Context context) {
        resources = context.getResources();
        goniometerDBHelper = new GoniometerDBHelper(context);
    }

    /**
     * Método que realiza una consulta a la Base de Datos para almacenar en la misma la información
     * del paciente que recibe como parámetro
     *
     * @param nuevoPaciente <tt>ArrayList<String></tt> a convertir en <tt>Paciente</tt> y almacenar
     *                      en la Base de Datos
     * @return long > 0 si la consulta se realizó con éxito, -1 en caso contrario
     */
    @Override
    public long storePatient(ArrayList<String> nuevoPaciente) {

        // Se crea una nueva instancia de tipo "Paciente" con la información ya filtrada
        Paciente paciente = new Paciente(
                nuevoPaciente.get(0), // Nombre
                Integer.parseInt(nuevoPaciente.get(1)), // Edad
                Sexo.findByValue(nuevoPaciente.get(2)), // Sexo
                Integer.parseInt(nuevoPaciente.get(3)), // ID
                nuevoPaciente.get(4), // Diagnóstico
                new ArrayList<>(Arrays.asList(nuevoPaciente.get(5).split(","))), // Tags
                Long.parseLong(nuevoPaciente.get(6)), // Teléfono
                nuevoPaciente.get(7), // Dirección
                nuevoPaciente.get(8), // Síntomas
                nuevoPaciente.get(9), // Tratamiento previo
                nuevoPaciente.get(10), // Tratamiento actual
                nuevoPaciente.get(11)); // Comentarios adicionales

        // Insertar el paciente en la BD
        return goniometerDBHelper.mock(Contract.PatientEntry.TABLE_NAME,
                paciente.toContentValues());
    }

    /**
     * Método que, dado el <tt>ArrayList<String></tt> con toda la información introducida por el
     * usuario sobre la medición, se encarga de obtener una instancia de tipo <tt>Medicion</tt> y
     * realizar una consulta a la Base de Datos para almacenarla.
     *
     * @param nuevaMedicion <tt>ArrayList<String></tt> a convertir en <tt>Medicion</tt> y almacenar
     *                      en la Base de Datos
     * @return long > 0 si la consulta se realizó con éxito, -1 en caso contrario
     */
    @Override
    public long storeMeasurement(ArrayList<String> nuevaMedicion) {

        // Componentes de una instancia "ArticulacionInvolucrada"
        Lado ladoMedicion;
        Articulacion articulacionMedicion;

        // Componentes de una instancia "Movimiento"
        TipoMovimiento tipoMovimientoMedicion;
        Modo modoMedicion;

        // Se obtiene una instancia de tipo Lado para su inserción en la BD
        ladoMedicion = Lado.findByValue(nuevaMedicion.get(2));

        // Comprobar si el usuario ha seleccionado la opción "Otra" en "Articulacion"
        if (nuevaMedicion.get(3).matches(resources.getString(R.string.other)))
            // Se obtiene una instancia de tipo Articulacion para su posterior inserción en la BD
            articulacionMedicion = Articulacion.OTRA.getOtraArticulacion(nuevaMedicion.get(8));
        else
            /*
             * El usuario ha seleccionado una articulación predefinida
             *
             * Se obtiene una instancia de tipo Articulacion para su posterior inserción en la BD
             */
            articulacionMedicion = Articulacion.findByValue(nuevaMedicion.get(3));

        // Comprobar si el usuario ha seleccionado la opción "Otro" en "Movimiento"
        if (nuevaMedicion.get(4).matches(resources.getString(R.string.other)))
            // Se obtiene una instancia de tipo TipoMovimiento para su posterior inserción en la BD
            tipoMovimientoMedicion = TipoMovimiento.OTRO.getOtroTipoMovimiento(nuevaMedicion.get(9));
        else
            // Se obtiene una instancia de tipo TipoMovimiento para su posterior inserción en la BD
            tipoMovimientoMedicion = TipoMovimiento.findByValue(nuevaMedicion.get(4));

        // Comprobar si el usuario ha seleccionado la opción "Otro" en "Modo"
        if (nuevaMedicion.get(5).matches(resources.getString(R.string.other)))
            // Se obtiene una instancia de tipo Modo para su posterior inserción en la BD
            modoMedicion = Modo.OTRO.getOtroModo(nuevaMedicion.get(10));
        else
            // Se obtiene una instancia de tipo Modo para su posterior inserción en la BD
            modoMedicion = Modo.findByValue(nuevaMedicion.get(5));

        // Se crea una nueva instancia de tipo "Medicion" con la información obtenida
        Medicion medicion = new Medicion(
                nuevaMedicion.get(0), // Fecha y hora de la medición
                Double.parseDouble(nuevaMedicion.get(1)), // Lectura del goniómetro
                new ArticulacionInvolucrada(ladoMedicion, articulacionMedicion), // Articulación
                new Movimiento(tipoMovimientoMedicion, modoMedicion), // Movimiento
                nuevaMedicion.get(6), // Nombre del paciente
                Integer.parseInt(nuevaMedicion.get(7))); // ID del paciente

        // Insertar la medición en la BD
        return goniometerDBHelper.mock(Contract.MeasurementEntry.TABLE_NAME,
                medicion.toContentValues());
    }

    /**
     * Método que se encarga de realizar una consulta a la Base de Datos y devolver una lista con
     * todos los nombres e ids de los pacientes almacenados en la misma.
     *
     * @return pacientes <tt>ArrayList<ArrayList<String>></tt> con los nombres e ids de todos los
     * pacientes almacenados en la Base de Datos
     */
    @Nullable
    @Override
    @SuppressWarnings("unchecked")
    public ArrayList<ArrayList<String>> getPacientes() {
        // ArrayList a devolver con los nombres e ids de los pacientes
        ArrayList<ArrayList<String>> pacientes = new ArrayList<>();
        // ArrayList donde se almacenara la información del paciente de manera individual
        ArrayList<String> paciente = new ArrayList<>();

        // Obtención de la BD en formato de sólo-lectura
        SQLiteDatabase database = goniometerDBHelper.getReadableDatabase();

        // Realización de la consulta y obtención de los resultados en un objeto Cursor
        Cursor cursor = database.query(Contract.PatientEntry.TABLE_NAME, new String[]
                        {Contract.PatientEntry.NOMBRE, Contract.PatientEntry.ID}, null, null, null,
                null, null);

        // Comprobar si se ha obtenido algún resultado en la consulta anterior
        if (cursor.moveToFirst())
            do {
                // Nombre del paciente
                paciente.add(0, cursor.getString(0));
                // ID del paciente
                paciente.add(1, cursor.getString(1));

                /*
                 * Una vez que se ha obtenido toda la información de un paciente, se clona el
                 * ArrayList que almacena el mismo en el ArrayList que devuelve todos los
                 * datos de los pacientes
                 */
                pacientes.add((ArrayList<String>) paciente.clone());
            } while (cursor.moveToNext());

        // Liberar los recursos del Cursor
        cursor.close();

        /*
         * Devolver el ArrayList con los elementos que se hayan obtenido, o devolver null en  caso
         * de no haber obtenido ninguno
         */
        return (pacientes.size() != 0) ? pacientes : null;
    }

    /**
     * Método que se encarga de realizar una consulta a la Base de Datos y devolver una lista con
     * la información a mostrar de un <tt>Paciente</tt> (en concreto, edad, sexo, diagnóstico,
     * comentarios adicionales y tags) a partir del nombre y el ID del mismo.
     *
     * @param nombre Nombre del paciente del que recuperar la información
     * @param id ID del paciente del que recuperar la información
     * @return informacionPaciente <tt>ArrayList<String></tt> con toda la información del paciente
     * almacenada en la Base de Datos
     */
    @Override
    public ArrayList<String> getPacienteBy(String nombre, int id) {
        // ArrayList a devolver con la información del paciente
        ArrayList<String> informacionPaciente = new ArrayList<>();

        // Obtención de la BD en formato de sólo-lectura
        SQLiteDatabase database = goniometerDBHelper.getReadableDatabase();

        // Creación de un array de String con los campos a devolver por la consulta a la BD
        String[] campos = new String[]{
                Contract.PatientEntry.EDAD,
                Contract.PatientEntry.SEXO,
                Contract.PatientEntry.DIAGNOSTICO,
                Contract.PatientEntry.COMENTARIOS,
                Contract.PatientEntry.TAGS};

        // Realización de la consulta y obtención de los resultados en un objeto Cursor
        Cursor cursor = database.query(Contract.PatientEntry.TABLE_NAME, campos,
                Contract.PatientEntry.NOMBRE + " = '" + nombre + "' AND " + Contract.PatientEntry.ID
                        + " = " + id, null, null, null, null);

        // Comprobar si se ha obtenido algún resultado en la consulta anterior
        if (cursor.moveToFirst())
            do {
                // Edad del paciente (campo obligatorio)
                informacionPaciente.add(cursor.getString(0));
                /*
                 * Sexo del paciente (campo obligatorio)
                 *
                 * Primera letra en mayúsculas, resto de la palabra en minúsculas
                 */
                informacionPaciente.add(
                        cursor.getString(1).substring(0, 1) +
                                (cursor.getString(1)).substring(1).toLowerCase());
                // Diagnóstico del paciente (campo obligatorio)
                informacionPaciente.add(cursor.getString(2));
                /*
                 * Comentarios adicionales sobre el paciente.
                 *
                 * Como es un campo opcional a introducir durante la creación de  un nuevo paciente,
                 * se comprueba si se ha almacenado "NULL" (correspondiente a  que el usuario no
                 * introdujera nada en ese campo), de ser así, se devuelve una cadena vacía
                 */
                informacionPaciente.add(
                        !(cursor.getString(3).equals("NULL")) ? cursor.getString(3) : "");
                // Tags del paciente (campo obligatorio)
                informacionPaciente.add(cursor.getString(4));
            } while (cursor.moveToNext());

        // Liberar los recursos del Cursor
        cursor.close();

        /*
         * Devolver el ArrayList con los elementos que se hayan obtenido, o devolver null en  caso
         * de no haber obtenido ninguno
         */
        return (informacionPaciente.size() != 0) ? informacionPaciente : null;
    }

    /**
     * Método que se encarga de realizar una consulta a la Base de Datos y devolver una lista con
     * todas las mediciones que se han realizado a un <tt>Paciente</tt> en concreto, a partir del
     * nombre e ID del mismo.
     *
     * @param nombre Nombre del paciente del que recuperar la información
     * @param id ID del paciente del que recuperar la información
     * @return medicionesPaciente <tt>ArrayList<ArrayList<String>></tt> con todas las mediciones
     * que se han realizado al paciente
     */
    @Override
    @SuppressWarnings("unchecked")
    public ArrayList<ArrayList<String>> getMedicionesPacienteBy(String nombre, int id) {
        // ArrayList a devolver con la mediciones del paciente
        ArrayList<ArrayList<String>> medicionesPaciente = new ArrayList<>();
        // ArrayList donde se almacenaran las mediciones del paciente de manera individual
        ArrayList<String> medicion = new ArrayList<>();

        // Obtención de la BD en formato de sólo-lectura
        SQLiteDatabase database = goniometerDBHelper.getReadableDatabase();

        // Creación de un array de String con los campos a devolver por la consulta a la BD
        String[] campos = new String[]{
                Contract.MeasurementEntry.FECHA_HORA,
                Contract.MeasurementEntry.LECTURA,
                Contract.MeasurementEntry.LADO,
                Contract.MeasurementEntry.ARTICULACION,
                Contract.MeasurementEntry.OTRA_ARTICULACION,
                Contract.MeasurementEntry.MOVIMIENTO,
                Contract.MeasurementEntry.OTRO_MOVIMIENTO,
                Contract.MeasurementEntry.MODO,
                Contract.MeasurementEntry.OTRO_MODO};

        // Realización de la consulta y obtención de los resultados en un objeto Cursor
        Cursor cursor = database.query(Contract.MeasurementEntry.TABLE_NAME, campos,
                Contract.MeasurementEntry.NOMBRE_PACIENTE + " = '" + nombre + "' AND " +
                        Contract.MeasurementEntry.ID_PACIENTE + " = " + id, null, null, null, null);

        // Comprobar si se ha obtenido algún resultado en la consulta anterior
        if (cursor.moveToFirst())
            do {
                // Fecha-Hora de la medición (campo obligatorio)
                medicion.add(0, cursor.getString(0));
                // Lectura del goniómetro obtenida durante la medición (campo obligatorio)
                medicion.add(1, cursor.getString(1));
                // Lado en el que se encuentra la articulación involucrada (campo obligatorio)
                medicion.add(2, cursor.getString(2));
                // Articulación involucrada en la medición (campo obligatorio)
                medicion.add(3, cursor.getString(3));
                /*
                 * Otra articulación.
                 *
                 * Campo que, por defecto, es "NULL", salvo en el caso de que el usuario
                 * seleccionara en "Articulacion" el valor "Otra".
                 *
                 * En caso de ser "NULL" se devuelve una cadena vacía
                 */
                medicion.add(4, !(cursor.getString(4).equals("NULL")) ? cursor.getString(4) : "");
                // Movimiento que realizó la articulación (campo obligatorio)
                medicion.add(5, cursor.getString(5));
                /*
                 * Otro movimiento.
                 *
                 * Campo que, por defecto, es "NULL", salvo en el caso de que el usuario
                 * seleccionara en "Movimiento" el valor "Otro".
                 *
                 * En caso de ser "NULL" se devuelve una cadena vacía
                 */
                medicion.add(6, !(cursor.getString(6).equals("NULL")) ? cursor.getString(6) : "");
                // Modo en el que la articulación realizó el movimiento (campo obligatorio)
                medicion.add(7, cursor.getString(7));
                /*
                 * Otro modo de movimiento.
                 *
                 * Campo que, por defecto, es "NULL", salvo en el caso de que el usuario
                 * seleccionara en "Tipo de Movimiento" el valor "Otro".
                 *
                 * En caso de ser "NULL" se devuelve una cadena vacía
                 */
                medicion.add(8, !(cursor.getString(8).equals("NULL")) ? cursor.getString(8) : "");

                /*
                 * Una vez que se ha obtenido toda la información de una medición, se clona el
                 * ArrayList que almacena la medición en el ArrayList que devuelve todas las
                 * mediciones del paciente
                 */
                medicionesPaciente.add((ArrayList<String>) medicion.clone());
            } while (cursor.moveToNext());

        // Liberar los recursos del Cursor
        cursor.close();

        /*
         * Devolver el ArrayList con los elementos que se hayan obtenido, o devolver null en  caso
         * de no haber obtenido ninguno
         */
        return (medicionesPaciente.size() != 0) ? medicionesPaciente : null;
    }

    /**
     * Método que se encarga de realizar una consulta a la Base de Datos para, primero, borrar todas
     * las mediciones que se han realizado a un paciente, y, después, eliminar al paciente en
     * cuestión de la misma, a partir del nombre e ID del paciente.
     *
     * @param nombre Nombre del paciente del que eliminar toda la información
     * @param id ID del paciente del que eliminar toda la información
     * @return int == 1 si el borrado se realizó con éxito, 0 en caso contrario
     */
    @Override
    public int onDeletePatient(String nombre, int id) {
        // Obtención de la BD en formato de escritura
        SQLiteDatabase database = goniometerDBHelper.getWritableDatabase();

        // Consulta de borrado de todas las mediciones del paciente
        database.delete(Contract.MeasurementEntry.TABLE_NAME,
                Contract.MeasurementEntry.NOMBRE_PACIENTE + " = '" + nombre + "' AND " +
                        Contract.MeasurementEntry.ID_PACIENTE + " = " + id, null);

        // Consulta de borrado del paciente en sí
        return database.delete(Contract.PatientEntry.TABLE_NAME,
                Contract.PatientEntry.NOMBRE + " = '" + nombre + "' AND " + Contract.PatientEntry.ID
                        + " = " + id, null);
    }

    /**
     * Método que se encarga de realizar una consulta a la Base de Datos y devolver un listado de
     * los nombres e IDs de los pacientes que se encuentran en la misma, filtrados según el criterio
     * seleccionado por el usuario en el <tt>Spinner</tt> de <tt>PacientesFragment</tt> y la
     * información introducida en el <tt>EditText</tt> del mismo <tt>Fragment</tt>.
     *
     * @param texto  Información introducida por el usuario en el <tt>EditText</tt>
     * @param filtro Filtro seleccionado por el usuario en el <tt>Spinner</tt>
     * @return listadoPacientesFiltrado <tt>ArrayList<ArrayList<String>></tt> con los nombres de
     *                                  todos los pacientes que coincidan con los criterios de
     *                                  filtración introducidos por el usuario
     */
    @Override
    @SuppressWarnings("unchecked")
    public ArrayList<ArrayList<String>> getPacientesFiltrado(String texto, String filtro) {
        // ArrayList a devolver con los nombres e IDs de los pacientes
        ArrayList<ArrayList<String>> listadoPacientesFiltrado = new ArrayList<>();
        // ArrayList donde se almacenara la información del paciente de manera individual
        ArrayList<String> paciente = new ArrayList<>();

        // Obtención de la BD en formato de sólo-lectura
        SQLiteDatabase database = goniometerDBHelper.getReadableDatabase();

        // Inicialización del Cursor antes de realizar la consulta
        Cursor cursor;

        /*
         * La consulta a realizar depende del filtro seleccionado por el usuario en el Spinner.
         *
         * Lo que se busca es que la información introducida por el usuario (parámetro texto) sea
         * una subcadena, bien del nombre, del id, o de las etiquetas del paciente. En caso de que
         * se cumpla esa condición, el nombre de ese paciente se extrae de la BD.
         */
        if (filtro.equals(resources.getString(R.string.nombre_array)))
            // Filtro: Nombre
            cursor = database.query(Contract.PatientEntry.TABLE_NAME,
                    new String[]{Contract.PatientEntry.NOMBRE, Contract.PatientEntry.ID},
                    Contract.PatientEntry.NOMBRE + " LIKE '%" + texto + "%'",
                    null, null, null, null);
        else if (filtro.equals(resources.getString(R.string.id_array)))
                /*
                 * Filtro: ID paciente
                 *
                 * Para comparar con el ID del paciente, que es almacenado como un número entero,
                 * primero se hace un casting a cadena de caracteres, para después compararlo mediante
                 * el operador "LIKE"
                 */
            cursor = database.query(Contract.PatientEntry.TABLE_NAME,
                    new String[]{Contract.PatientEntry.NOMBRE, Contract.PatientEntry.ID},
                    "CAST(" + Contract.PatientEntry.ID + " AS TEXT) LIKE '%" + texto + "%'",
                    null, null, null, null);
        else
            // Filtro: Etiquetas
            cursor = database.query(Contract.PatientEntry.TABLE_NAME,
                    new String[]{Contract.PatientEntry.NOMBRE, Contract.PatientEntry.ID},
                    Contract.PatientEntry.TAGS + " LIKE '%" + texto + "%'",
                    null, null, null, null);

        // Comprobar si se ha obtenido algún resultado en la consulta anterior
        if (cursor.moveToFirst())
            do {
                // Nombre del paciente
                paciente.add(0, cursor.getString(0));
                // ID del paciente
                paciente.add(1, cursor.getString(1));

                /*
                 * Una vez que se ha obtenido toda la información de un paciente, se clona el
                 * ArrayList que almacena el mismo en el ArrayList que devuelve todos los
                 * datos de los pacientes
                 */
                listadoPacientesFiltrado.add((ArrayList<String>) paciente.clone());
            } while (cursor.moveToNext());

        // Liberar los recursos del Cursor
        cursor.close();

        /*
         * Devolver el ArrayList con los elementos que se hayan obtenido
         */
        return listadoPacientesFiltrado;
    }

    /**
     * Método que se encarga de realizar una consulta a la Base de Datos y devolver una lista con
     * las últimas 10 mediciones (como maximo) que se han almacenado en la misma, ordenadas de mas
     * a menos recientes.
     *
     * @return mediciones <tt>ArrayList<ArrayList<String>></tt> con las últimas 10 mediciones (como
     * maximo) que se han almacenado en la Base de Datos
     */
    @Override
    public ArrayList<ArrayList<String>> getUltimasMediciones() {
        // ArrayList a devolver con las últimas 10 mediciones
        ArrayList<ArrayList<String>> mediciones = new ArrayList<>();
        // ArrayList donde se almacenaran las mediciones del paciente de manera individual
        ArrayList<String> medicion = new ArrayList<>();

        // Obtención de la BD en formato de sólo-lectura
        SQLiteDatabase database = goniometerDBHelper.getReadableDatabase();

        // Realización de la consulta y obtención de los resultados en un objeto Cursor
        Cursor cursor = database.query(Contract.MeasurementEntry.TABLE_NAME,
                null, null, null, null, null, Contract.MeasurementEntry.FECHA_HORA + " DESC", "10");

        // Comprobar si se ha obtenido algún resultado en la consulta anterior
        if (cursor.moveToFirst())
            do {
                // Fecha-Hora de la medición (campo obligatorio)
                medicion.add(0, cursor.getString(0));
                // Lectura del goniómetro obtenida durante la medición (campo obligatorio)
                medicion.add(1, cursor.getString(1));
                // Lado en el que se encuentra la articulación involucrada (campo obligatorio)
                medicion.add(2, cursor.getString(2));
                // Articulación involucrada en la medición (campo obligatorio)
                medicion.add(3, cursor.getString(3));
                /*
                 * Otra articulación.
                 *
                 * Campo que, por defecto, es "NULL", salvo en el caso de que el usuario
                 * seleccionara en "Articulacion" el valor "Otra".
                 */
                medicion.add(4, cursor.getString(4));
                // Movimiento que realizó la articulación (campo obligatorio)
                medicion.add(5, cursor.getString(5));
                /*
                 * Otro movimiento.
                 *
                 * Campo que, por defecto, es "NULL", salvo en el caso de que el usuario
                 * seleccionara en "Movimiento" el valor "Otro".
                 */
                medicion.add(6, cursor.getString(6));
                // Modo en el que la articulación realizó el movimiento (campo obligatorio)
                medicion.add(7, cursor.getString(7));
                /*
                 * Otro modo de movimiento.
                 *
                 * Campo que, por defecto, es "NULL", salvo en el caso de que el usuario
                 * seleccionara en "Tipo de Movimiento" el valor "Otro".
                 */
                medicion.add(8, cursor.getString(8));
                // Nombre del paciente al que se realizó la medición (campo obligatorio)
                medicion.add(9, cursor.getString(9));
                // ID del paciente al que se realizó la medición (campo obligatorio)
                medicion.add(10, cursor.getString(10));

                /*
                 * Una vez se han obtenido todos los datos referentes a una medición, son enviados
                 * al método "getDatosFormateados" para que aplique correcciones de
                 * mayúsculas/minúsculas y saltos de línea con el fin de que se puedan apreciar
                 * todos los datos de una medición en la pantalla del dispositivo (por cuestiones de
                 * espacio en la pantalla).
                 *
                 * Después de eso, los datos son añadidos al ArrayList que almacena todas las
                 * mediciones que se van a devolver
                 */
                mediciones.add(getDatosFormateados(medicion));
            } while (cursor.moveToNext());

        // Liberar los recursos del Cursor
        cursor.close();

        /*
         * Devolver el ArrayList con los elementos que se hayan obtenido, o devolver null en  caso
         * de no haber obtenido ninguno
         */
        return (mediciones.size() != 0) ? mediciones : null;
    }

    /**
     * Método que se encarga de, a partir de los datos correspondientes a una medición, dar un
     * formato adecuado a los mismos para que se puedan mostrar correctamente en la pantalla del
     * dispositivo.
     * <p>
     * <p>Principalmente realiza tareas de añadir saltos de línea entre cada palabra (con el fin de
     * evitar que alguna parte de la medición no se pueda visualizar en el dispositivo), así como de
     * dejar sólo la primera letra de cada palabra con mayúscula, y pasando el resto de la misma a
     * minúscula, para que así el texto ocupe menos espacio (los datos, por defecto, se obtienen de
     * la Base de Datos con todos su caracteres en mayúscula).</p>
     * <p>
     * <p>Por último, este método tiene en cuenta el idioma del dispositivo para mostrar la
     * información de un modo u otro, atendiendo a criterios sintacticos.</p>
     *
     * @param medicion <tt>ArrayList<String></tt> sobre la que aplicar las correciones de formato
     * @return <tt>ArrayList<String></tt> con todos los campos de la medición con el correspondiente
     * formato ya aplicado para su correcta visualización en la pantalla del dispositivo
     */
    @Override
    public ArrayList<String> getDatosFormateados(ArrayList<String> medicion) {
        // Variables donde se almacenaran temporalmente los datos de la medición ya formateados
        final String fechaHora, articulacion, movimiento, lectura_goniometro, nombrePaciente,
                idPaciente;

        /*
         * Variable donde almacenar el idioma actual del dispositivo, para mostrar la información
         * relativa a la articulación de un modo u otro
         */
        String idioma = Locale.getDefault().getLanguage();


        // Fecha - Hora de la medición
        if (idioma.contentEquals("es")) // Idioma: Español
            fechaHora = medicion.get(0).replace(" ", "\n");
        else { // Cualquier otro idioma
            String[] fecha_hora = medicion.get(0).split(" ");
            fechaHora = fecha_hora[0] + " " + fecha_hora[1] + " " + fecha_hora[2] + "\n" +
                    fecha_hora[3] + " " + fecha_hora[4];
        }

        // Lectura del goniómetro
        if (medicion.get(1).contains("."))
            lectura_goniometro = medicion.get(1) + "º";
        else
            lectura_goniometro = medicion.get(1) + ".0º";

        // Articulación de la medición
        if (medicion.get(2).equalsIgnoreCase(resources.getString(R.string.columna)))
            // Lado == "Columna"
            if (idioma.contentEquals("es")) { // Idioma: Español
                articulacion =
                        // Primera letra en mayúsculas, el resto de la palabra en minúsculas
                        medicion.get(2).substring(0, 1)
                                + medicion.get(2).substring(1).toLowerCase()
                                + "\n"
                                // Primera letra en mayúsculas, el resto de la palabra en minúsculas
                                + medicion.get(3).substring(0, 1)
                                + medicion.get(3).substring(1).toLowerCase();
            } else { // Cualquier otro idioma
                articulacion =
                        // Primera letra en mayúsculas, el resto de la palabra en minúsculas
                        medicion.get(3).substring(0, 1)
                                + medicion.get(3).substring(1).toLowerCase()
                                + "\n"
                                // Primera letra en mayúsculas, el resto de la palabra en minúsculas
                                + medicion.get(2).substring(0, 1)
                                + medicion.get(2).substring(1).toLowerCase();
            }
        else // Lado = Izquierdo | Derecho
            if (medicion.get(3).matches(resources.getString(R.string.other)))
                // Articulacion == "Otra"
                if (idioma.contentEquals("es")) { // Idioma: Español
                    articulacion =
                            // Primera letra en mayúsculas, el resto de la palabra en minúsculas
                            // Articulacion introducida por el usuario
                            (medicion.get(4).substring(0, 1).toUpperCase()
                                    + medicion.get(4).substring(1).toLowerCase()
                                    + "\n-\nLado\n"
                                    // Primera letra en mayúsculas, el resto de la palabra en minúsculas
                                    // Lado
                                    + medicion.get(2).substring(0, 1)
                                    + medicion.get(2).substring(1).toLowerCase())
                                    // Sólo una palabra por línea por cuestión de espacio en la pantalla
                                    .replace(" ", "\n");
                } else { // Cualquier otro idioma
                    articulacion =
                            // Primera letra en mayúsculas, el resto de la palabra en minúsculas
                            // Articulacion introducida por el usuario
                            (medicion.get(4).substring(0, 1).toUpperCase()
                                    + medicion.get(4).substring(1).toLowerCase()
                                    + "\n-\n"
                                    // Primera letra en mayúsculas, el resto de la palabra en minúsculas
                                    // Lado
                                    + medicion.get(2).substring(0, 1)
                                    + medicion.get(2).substring(1).toLowerCase()
                                    + "\nSide")
                                    // Sólo una palabra por línea por cuestión de espacio en la pantalla
                                    .replace(" ", "\n");
                }
            else // Articulación != "Otra"
                if (idioma.contentEquals("es")) { // Idioma: Español
                    articulacion =
                            // Primera letra en mayúsculas, el resto de la palabra en minúsculas
                            // Articulacion
                            medicion.get(3).substring(0, 1)
                                    + medicion.get(3).substring(1).toLowerCase()
                                    + "\n-\nLado\n"
                                    // Primera letra en mayúsculas, el resto de la palabra en minúsculas
                                    // Lado
                                    + medicion.get(2).substring(0, 1)
                                    + medicion.get(2).substring(1).toLowerCase();
                } else { // Cualquier otro idioma
                    articulacion =
                            // Primera letra en mayúsculas, el resto de la palabra en minúsculas
                            // Articulacion
                            (medicion.get(3).substring(0, 1)
                                    + medicion.get(3).substring(1).toLowerCase()
                                    + "\n-\n"
                                    // Primera letra en mayúsculas, el resto de la palabra en minúsculas
                                    // Lado
                                    + medicion.get(2).substring(0, 1)
                                    + medicion.get(2).substring(1).toLowerCase()
                                    + "\nSide")
                                    // Sólo una palabra por línea por cuestión de espacio en la pantalla
                                    .replace(" ", "\n");
                }

        // Movimiento de la articulación
        if (medicion.get(5).matches(resources.getString(R.string.other)))
            // Movimiento == "Otro"
            if (medicion.get(7).matches(resources.getString(R.string.other)))
                // Tipo de movimiento == "Otro"
                movimiento =
                        // Primera letra en mayúsculas, el resto de la palabra en minúsculas
                        // Movimiento introducido por el usuario
                        (medicion.get(6).substring(0, 1).toUpperCase()
                                + medicion.get(6).substring(1).toLowerCase()
                                + "\n-\n"
                                // Primera letra en mayúsculas, el resto de la palabra en minúsculas
                                // Tipo de movimiento introducido por el usuario
                                + medicion.get(8).substring(0, 1).toUpperCase()
                                + medicion.get(8).substring(1).toLowerCase())
                                // Sólo una palabra por línea por cuestión de espacio en la pantalla
                                .replace(" ", "\n");
            else // Tipo de movimiento != "Otro"
                movimiento =
                        // Primera letra en mayúsculas, el resto de la palabra en minúsculas
                        // Movimiento introducido por el usuario
                        (medicion.get(6).substring(0, 1).toUpperCase()
                                + medicion.get(6).substring(1).toLowerCase())
                                + "\n-\n"
                                // Primera letra en mayúsculas, el resto de la palabra en minúsculas
                                // Tipo de movimiento
                                + medicion.get(7).substring(0, 1)
                                + medicion.get(7).substring(1).toLowerCase()
                                // Sólo una palabra por línea por cuestión de espacio en la pantalla
                                .replace(" ", "\n")
                                .replace("_", "\n");
        else // Movimiento != "Otro"
            if (medicion.get(7).matches(resources.getString(R.string.other)))
                // Tipo de movimiento == "Otro"
                movimiento =
                        // Primera letra en mayúsculas, el resto de la palabra en minúsculas
                        // Movimiento
                        (medicion.get(5).substring(0, 1)
                                + medicion.get(5).substring(1).toLowerCase()
                                + "\n-\n"
                                // Primera letra en mayúsculas, el resto de la palabra en minúsculas
                                // Tipo de movimiento introducido por el usuario
                                + medicion.get(8).substring(0, 1).toUpperCase()
                                + medicion.get(8).substring(1).toLowerCase())
                                // Sólo una palabra por línea por cuestión de espacio en la pantalla
                                .replace(" ", "\n")
                                .replace("_", "\n");
            else // Tipo de movimiento != "Otro"
                movimiento =
                        // Primera letra en mayúsculas, el resto de la palabra en minúsculas
                        // Movimiento
                        (medicion.get(5).substring(0, 1)
                                + medicion.get(5).substring(1).toLowerCase()
                                + "\n-\n"
                                // Primera letra en mayúsculas, el resto de la palabra en minúsculas
                                // Tipo de movimiento
                                + medicion.get(7).substring(0, 1)
                                + medicion.get(7).substring(1).toLowerCase())
                                // Sólo una palabra por línea por cuestión de espacio en la pantalla
                                .replace(" ", "\n")
                                .replace("_", "\n");


        // Nombre del paciente al que se realizó la medición
        String nombre; // Variable temporal donde almacenar el resultado después del try-catch

        try {
            nombre = medicion.get(9);
        } catch (IndexOutOfBoundsException e) {
            nombre = "";
        }

        nombrePaciente = nombre; // Asignación del valor final

        // ID del paciente al que se realizó la medición
        String id; // Variable temporal donde almacenar el resultado después del try-catch

        try {
            id = medicion.get(10);
        } catch (IndexOutOfBoundsException e) {
            id = "";
        }

        idPaciente = id; // Asignación del valor final

        return new ArrayList<String>() {{
            add(fechaHora);
            add(lectura_goniometro);
            add(articulacion);
            add(movimiento);
            add(nombrePaciente);
            add(idPaciente);
        }};
    }

    /**
     * Método que se encarga de realizar una consulta a la Base de Datos para borrar una medición
     * de la misma, a partir de la fecha-hora en la que se realizó.
     *
     * @param fechaHora Fecha y hora en la que se realizó la medición
     * @return int == 1 si el borrado se realizó con éxito, 0 en caso contrario
     */
    @Override
    public int onDeleteMeasurement(String fechaHora) {
        // Obtención de la BD en formato de escritura
        SQLiteDatabase database = goniometerDBHelper.getWritableDatabase();

        // Consulta de borrado de la medición
        return database.delete(Contract.MeasurementEntry.TABLE_NAME,
                Contract.MeasurementEntry.FECHA_HORA + " = '" + fechaHora + "'", null);
    }

    /**
     * Método "getter" de la variable <tt>mostrarOpcionesAlmacenarMedicion</tt> para ver si es
     * necesario mostrar el botón de añadir una nueva medición al perfil de un paciente.
     *
     * @return mostrarOpcionesAlmacenarMedicion
     */
    public boolean getMostrarOpcionesAlmacenarMedicion() {
        return mostrarOpcionesAlmacenarMedicion;
    }

    /**
     * Método "setter" de la variable <tt>mostrarOpcionesAlmacenarMedicion</tt> para ver si es
     * necesario mostrar el botón de añadir una nueva medición al perfil de un paciente.
     *
     * @param opciones Nuevo valor de la variable <tt>mostrarOpcionesAlmacenarMedicion</tt>
     */
    public void setMostrarOpcionesAlmacenarMedicion(boolean opciones) {
        this.mostrarOpcionesAlmacenarMedicion = opciones;
    }

    /**
     * Método "getter" de la variable <tt>mostrarOpcionesBorrarPaciente</tt> para ver si es
     * necesario mostrar el botón de borrar el perfil de un paciente.
     *
     * @return mostrarOpcionesBorrarPaciente
     */
    public boolean getMostrarOpcionesBorrarPaciente() {
        return mostrarOpcionesBorrarPaciente;
    }

    /**
     * Método "setter" de la variable <tt>mostrarOpcionesBorrarPaciente</tt> para ver si es
     * necesario mostrar el botón de borrar el perfil de un paciente.
     *
     * @param opciones Nuevo valor de la variable <tt>mostrarOpcionesBorrarPaciente</tt>
     */
    public void setMostrarOpcionesBorrarPaciente(boolean opciones) {
        this.mostrarOpcionesBorrarPaciente = opciones;
    }
}
