package com.uva.rafael.tfg_goniometer.data;

import android.content.ContentValues;
import android.content.Context;
import android.database.sqlite.SQLiteConstraintException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;

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;

/**
 * Esta clase es una clase auxiliar que se encarga de la creación de la Base de Datos y la gestión
 * de la versión de la misma.
 *
 * <p>En concreto, se encarga de crear (y actualizar cuando hay un cambio de versión) toda la
 * estructura de la Base de Datos, rellenar las tablas correspondientes a las clases de tipo
 * <tt>Enum</tt>, y proporcionar un método para hacer inserciones en la misma desde el Modelo de la
 * aplicación (<tt>MainModel</tt>).</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 SQLiteOpenHelper
 * @see SQLiteDatabase
 * @see Contract
 * @version 1.0
 */

public class GoniometerDBHelper extends SQLiteOpenHelper {

    // Nombre y versión de la Base de Datos
    private static final String DATABASE_NAME = "TFG-Goniometer.db";
    private static final int DATABASE_VERSION = 1;

    /**
     * Constructor principal de la clase
     *
     * @param context Contexto de acción para el helper
     */
    public GoniometerDBHelper(Context context) {
        super(context, DATABASE_NAME, null, DATABASE_VERSION);
    }

    /**
     * Called when the database is created for the first time. This is where the creation of tables
     * and the initial population of the tables happen.
     *
     * @param sqLiteDatabase The database
     */
    @Override
    public void onCreate(SQLiteDatabase sqLiteDatabase) {

        /*
         * Creación de las tablas de la BD
         *
         * Primero se crean las tablas correspondientes a las clases Enum, ya que, no tienen
         * ninguna clave foranea
         */

        // Enum ARTICULACION
        sqLiteDatabase.execSQL(
                "CREATE TABLE " + Contract.JointEnum.TABLE_NAME + " ("
                + Contract.JointEnum.ARTICULACION + " TEXT PRIMARY KEY NOT NULL,"
                + Contract.JointEnum.OTRA_ARTICULACION + " TEXT)");

        // Enum LADO
        sqLiteDatabase.execSQL(
                "CREATE TABLE " + Contract.SideEnum.TABLE_NAME + " ("
                + Contract.SideEnum.LADO + " TEXT PRIMARY KEY NOT NULL)");

        // Enum MODO
        sqLiteDatabase.execSQL(
                "CREATE TABLE " + Contract.ModeEnum.TABLE_NAME + " ("
                + Contract.ModeEnum.MODO + " TEXT PRIMARY KEY NOT NULL,"
                + Contract.ModeEnum.OTRO_MODO + " TEXT)");

        // Enum SEXO
        sqLiteDatabase.execSQL(
                "CREATE TABLE " + Contract.SexEnum.TABLE_NAME + " ("
                + Contract.SexEnum.SEXO + " TEXT PRIMARY KEY NOT NULL)");

        // Enum TIPOMOVIMIENTO
        sqLiteDatabase.execSQL(
                "CREATE TABLE " + Contract.MovementTypeEnum.TABLE_NAME + " ("
                + Contract.MovementTypeEnum.TIPO_MOVIMIENTO + " TEXT PRIMARY KEY NOT NULL,"
                + Contract.MovementTypeEnum.OTRO_TIPO_MOVIMIENTO + " TEXT)");

        // Entity PACIENTE
        sqLiteDatabase.execSQL(
                "CREATE TABLE " + Contract.PatientEntry.TABLE_NAME + " ("
                        + Contract.PatientEntry.NOMBRE + " TEXT NOT NULL,"
                + Contract.PatientEntry.EDAD + " INTEGER NOT NULL,"
                + Contract.PatientEntry.SEXO + " TEXT NOT NULL,"
                        + Contract.PatientEntry.ID + " INTEGER NOT NULL,"
                + Contract.PatientEntry.DIAGNOSTICO + " TEXT NOT NULL,"
                + Contract.PatientEntry.TAGS + " TEXT NOT NULL,"
                + Contract.PatientEntry.TELEFONO + " LONG,"
                + Contract.PatientEntry.DIRECCION + " TEXT,"
                + Contract.PatientEntry.SINTOMAS + " TEXT,"
                + Contract.PatientEntry.TRATAMIENTO_PREVIO + " TEXT,"
                + Contract.PatientEntry.TRATAMIENTO_ACTUAL + " TEXT,"
                + Contract.PatientEntry.COMENTARIOS + " TEXT,"
                        + " PRIMARY KEY (" + Contract.PatientEntry.NOMBRE + ", "
                        + Contract.PatientEntry.ID + "),"
                + " FOREIGN KEY (" + Contract.PatientEntry.SEXO + ") REFERENCES "
                + Contract.SexEnum.TABLE_NAME + "(" + Contract.SexEnum.SEXO + "))");

        // Entity MEDICION
        sqLiteDatabase.execSQL(
                "CREATE TABLE " + Contract.MeasurementEntry.TABLE_NAME + " ("
                + Contract.MeasurementEntry.FECHA_HORA + " TEXT PRIMARY KEY NOT NULL,"
                        + Contract.MeasurementEntry.LECTURA + " DOUBLE NOT NULL,"
                + Contract.MeasurementEntry.LADO + " TEXT NOT NULL,"
                + Contract.MeasurementEntry.ARTICULACION + " TEXT NOT NULL,"
                + Contract.MeasurementEntry.OTRA_ARTICULACION + " TEXT,"
                + Contract.MeasurementEntry.MOVIMIENTO + " TEXT NOT NULL,"
                + Contract.MeasurementEntry.OTRO_MOVIMIENTO + " TEXT,"
                + Contract.MeasurementEntry.MODO + " TEXT NOT NULL,"
                + Contract.MeasurementEntry.OTRO_MODO + " TEXT,"
                        + Contract.MeasurementEntry.NOMBRE_PACIENTE + " TEXT NOT NULL,"
                        + Contract.MeasurementEntry.ID_PACIENTE + " INTEGER NOT NULL,"
                + " FOREIGN KEY (" + Contract.MeasurementEntry.LADO + ") REFERENCES "
                        + Contract.SideEnum.TABLE_NAME + "(" + Contract.SideEnum.LADO + "),"
                + " FOREIGN KEY (" + Contract.MeasurementEntry.ARTICULACION + ") REFERENCES "
                        + Contract.JointEnum.TABLE_NAME + "(" + Contract.JointEnum.ARTICULACION + "),"
                + " FOREIGN KEY (" + Contract.MeasurementEntry.OTRA_ARTICULACION + ") REFERENCES "
                        + Contract.JointEnum.TABLE_NAME + "(" + Contract.JointEnum.OTRA_ARTICULACION + "),"
                + " FOREIGN KEY (" + Contract.MeasurementEntry.MOVIMIENTO + ") REFERENCES "
                        + Contract.MovementTypeEnum.TABLE_NAME + "(" + Contract.MovementTypeEnum.TIPO_MOVIMIENTO + "),"
                + " FOREIGN KEY (" + Contract.MeasurementEntry.OTRO_MOVIMIENTO + ") REFERENCES "
                        + Contract.MovementTypeEnum.TABLE_NAME + "(" + Contract.MovementTypeEnum.OTRO_TIPO_MOVIMIENTO + "),"
                + " FOREIGN KEY (" + Contract.MeasurementEntry.MODO + ") REFERENCES "
                        + Contract.ModeEnum.TABLE_NAME + "(" + Contract.ModeEnum.MODO + "),"
                + " FOREIGN KEY (" + Contract.MeasurementEntry.OTRO_MODO + ") REFERENCES "
                        + Contract.ModeEnum.TABLE_NAME + "(" + Contract.ModeEnum.OTRO_MODO + "),"
                        + " FOREIGN KEY (" + Contract.MeasurementEntry.NOMBRE_PACIENTE + ") REFERENCES "
                        + Contract.PatientEntry.TABLE_NAME + "(" + Contract.PatientEntry.NOMBRE + "),"
                        + " FOREIGN KEY (" + Contract.MeasurementEntry.ID_PACIENTE + ") REFERENCES "
                        + Contract.PatientEntry.TABLE_NAME + "(" + Contract.PatientEntry.ID + "))");


        // Valores de la tabla Enum ARTICULACION
        ContentValues values = new ContentValues();

        // Pares clave-valor
        values.put(Contract.JointEnum.ARTICULACION, Articulacion.HOMBRO.toString());
        values.put(Contract.JointEnum.ARTICULACION, Articulacion.CODO.toString());
        values.put(Contract.JointEnum.ARTICULACION, Articulacion.MUÑECA.toString());
        values.put(Contract.JointEnum.ARTICULACION, Articulacion.CADERA.toString());
        values.put(Contract.JointEnum.ARTICULACION, Articulacion.RODILLA.toString());
        values.put(Contract.JointEnum.ARTICULACION, Articulacion.TOBILLO.toString());
        values.put(Contract.JointEnum.ARTICULACION, Articulacion.OTRA.toString());
        values.put(Contract.JointEnum.ARTICULACION, Articulacion.CERVICAL.toString());
        values.put(Contract.JointEnum.ARTICULACION, Articulacion.TORACICA.toString());
        values.put(Contract.JointEnum.ARTICULACION, Articulacion.LUMBAR.toString());

        // Insertar en la BD
        sqLiteDatabase.insertOrThrow(Contract.JointEnum.TABLE_NAME, null, values);


        // Valores de la tabla Enum LADO
        values = new ContentValues();

        // Pares clave-valor
        values.put(Contract.SideEnum.LADO, Lado.IZQUIERDO.toString());
        values.put(Contract.SideEnum.LADO, Lado.DERECHO.toString());
        values.put(Contract.SideEnum.LADO, Lado.COLUMNA.toString());

        // Insertar en la BD
        sqLiteDatabase.insertOrThrow(Contract.SideEnum.TABLE_NAME, null, values);


        // Valores de la tabla Enum MODO
        values = new ContentValues();

        // Pares clave-valor
        values.put(Contract.ModeEnum.MODO, Modo.ACTIVO.toString());
        values.put(Contract.ModeEnum.MODO, Modo.ACTIVO_ASISTIDO.toString());
        values.put(Contract.ModeEnum.MODO, Modo.PASIVO.toString());
        values.put(Contract.ModeEnum.MODO, Modo.OTRO.toString());

        // Insertar en la BD
        sqLiteDatabase.insertOrThrow(Contract.ModeEnum.TABLE_NAME, null, values);


        // Valores de la tabla Enum SEXO
        values = new ContentValues();

        // Pares clave-valor
        values.put(Contract.SexEnum.SEXO, Sexo.HOMBRE.toString());
        values.put(Contract.SexEnum.SEXO, Sexo.MUJER.toString());

        // Insertar en la BD
        sqLiteDatabase.insertOrThrow(Contract.SexEnum.TABLE_NAME, null, values);


        // Valores de la tabla enum TIPOMOVIMIENTO
        values = new ContentValues();

        // Pares clave-valor
        values.put(Contract.MovementTypeEnum.TIPO_MOVIMIENTO, TipoMovimiento.FLEXION.toString());
        values.put(Contract.MovementTypeEnum.TIPO_MOVIMIENTO, TipoMovimiento.EXTENSION.toString());
        values.put(Contract.MovementTypeEnum.TIPO_MOVIMIENTO, TipoMovimiento.ADUCCION.toString());
        values.put(Contract.MovementTypeEnum.TIPO_MOVIMIENTO, TipoMovimiento.ABDUCCION.toString());
        values.put(Contract.MovementTypeEnum.TIPO_MOVIMIENTO, TipoMovimiento.VARO.toString());
        values.put(Contract.MovementTypeEnum.TIPO_MOVIMIENTO, TipoMovimiento.VALGO.toString());
        values.put(Contract.MovementTypeEnum.TIPO_MOVIMIENTO, TipoMovimiento.ROTACION_INTERNA.toString());
        values.put(Contract.MovementTypeEnum.TIPO_MOVIMIENTO, TipoMovimiento.ROTACION_EXTERNA.toString());
        values.put(Contract.MovementTypeEnum.TIPO_MOVIMIENTO, TipoMovimiento.PRONACION.toString());
        values.put(Contract.MovementTypeEnum.TIPO_MOVIMIENTO, TipoMovimiento.SUPINACION.toString());
        values.put(Contract.MovementTypeEnum.TIPO_MOVIMIENTO, TipoMovimiento.FLEXION_LATERAL.toString());
        values.put(Contract.MovementTypeEnum.TIPO_MOVIMIENTO, TipoMovimiento.ROTACION.toString());
        values.put(Contract.MovementTypeEnum.TIPO_MOVIMIENTO, TipoMovimiento.OTRO.toString());

        // Insertar en la BD
        sqLiteDatabase.insertOrThrow(Contract.MovementTypeEnum.TABLE_NAME, null, values);
    }

    /**
     * Called when the database needs to be upgraded. The implementation use this method to drop
     * tables, add tables, or do anything else it needs to upgrade to the new schema version.
     *
     * @param sqLiteDatabase The database
     * @param oldVersion     The old database version
     * @param newVersion     The new database version
     */
    @Override
    public void onUpgrade(SQLiteDatabase sqLiteDatabase, int oldVersion, int newVersion) {
        // Borrar toda la BD
        sqLiteDatabase.execSQL("DROP TABLE IF EXISTS " + Contract.MeasurementEntry.TABLE_NAME);
        sqLiteDatabase.execSQL("DROP TABLE IF EXISTS " + Contract.PatientEntry.TABLE_NAME);

        sqLiteDatabase.execSQL("DROP TABLE IF EXISTS " + Contract.MovementTypeEnum.TABLE_NAME);
        sqLiteDatabase.execSQL("DROP TABLE IF EXISTS " + Contract.SexEnum.TABLE_NAME);
        sqLiteDatabase.execSQL("DROP TABLE IF EXISTS " + Contract.ModeEnum.TABLE_NAME);
        sqLiteDatabase.execSQL("DROP TABLE IF EXISTS " + Contract.SideEnum.TABLE_NAME);
        sqLiteDatabase.execSQL("DROP TABLE IF EXISTS " + Contract.JointEnum.TABLE_NAME);

        // Volver a crear toda la BD
        onCreate(sqLiteDatabase);
    }

    /**
     * Método que se emplea para insertar un objeto de tipo <tt>ContentValues</tt> en una tabla en
     * concreto de la Base de Datos (cuyo nombre se recibe como parámetro, también).
     *
     * @param tableName Nombre de la tabla donde realizar la inserción
     * @param values Fila a insertar dentro de la tabla
     * @return long ID de la fila que se acaba de insertar, -1 en caso de error
     */
    public long mock(String tableName, ContentValues values) {
        // Obtención de la BD en formato de escritura
        SQLiteDatabase sqLiteDatabase = getWritableDatabase();

        // Inserción en la BD
        try {
            return sqLiteDatabase.insertOrThrow(
                    tableName,
                    null,
                    values);
        } catch (SQLiteConstraintException e) {
            // Se ha intentado introducir un paciente con el mismo nombre e ID de uno ya existente
            return -2;
        }
    }
}
