import io
import os
import numpy as np
from datos.Config import ConfigManager
import sqlite3

config = ConfigManager()

def exists_database(output):
    if not config.is_db_file_set():
        output.set_output("No database file has been selected.")

        return False

    if not os.path.exists(config.db_file):
        output.set_output("The database does not exist.")

        return False
    
    return True

def get_imagenes_id(output):
    if not exists_database(output):
        return None

    # Conectar a la base de datos
    try:
        conn = sqlite3.connect(config.db_file)
        output.set_output("Connection to the database successfully established.")
        
    except sqlite3.Error as e:
        output.set_output(f"Error connecting to the database: {e}")
        return None
    
    cursor = conn.cursor()
    resposta = {}
    try:
        # Consulta SQL para extraer image_id y rows de la tabla keypoints
        cursor.execute("SELECT image_id, name FROM images")
        
        # Extraer todas las filas de una vez
        rows = cursor.fetchall()
        
        # Procesar las filas extraídas
        for row in rows:
            id, name = row
            resposta[id] = name

    except sqlite3.Error as e:
        output.set_output(f"Error when executing the query: {e}")
        return None

    finally:
        # Cerrar la conexión a la base de datos
        conn.close()
    
    return resposta


def read_blob_data_keypoints(blob_data):
    # Decodificar los datos binarios como una matriz numpy de tipo float32
    # Aquí se asume que cada par de valores X e Y se almacena en 4 bytes (float32)
    # y que la matriz está en formato row-major (filas concatenadas)
    keypoints_data_read = np.frombuffer(blob_data.read(), dtype=np.float32)
    
    # Reformatear la matriz para que tenga dos columnas (X, Y) y tantas filas como puntos
    return keypoints_data_read.reshape(-1, 6)

def extract_features(output):
    keypoints_data = {}  # Diccionario para almacenar los datos extraídos
    num_images = 0  # Contador del número de imágenes
    
    # Conectar a la base de datos
    try:
        conn = sqlite3.connect(config.db_file)
        output.set_output("Connection to the database successfully established.")
        
    except sqlite3.Error as e:
        output.set_output(f"Error when connecting to the database: {e}")
        return {}, 0
    
    cursor = conn.cursor()
    
    try:
        # Consulta SQL para extraer image_id y rows de la tabla keypoints
        cursor.execute("SELECT images.name, keypoints.data FROM images JOIN keypoints ON images.image_id = keypoints.image_id")
        
        # Extraer todas las filas de una vez
        rows = cursor.fetchall()
        
        # Procesar las filas extraídas
        for row in rows:
            image_name, data = row

            # Leer el blob de datos binarios
            data_bytes = io.BytesIO(data)
            
            keypoints_data[image_name] = read_blob_data_keypoints(data_bytes)

            num_images += 1

    except sqlite3.Error as e:
        output.set_output(f"Error when executing the query: {e}")
        return {}, 0

    finally:
        # Cerrar la conexión a la base de datos
        conn.close()
    
    return keypoints_data, num_images

def pair_id_to_image_ids(pair_id):
    pair_id = int(pair_id)
    image_id2 = pair_id % 2147483647
    image_id1 = (pair_id - image_id2) / 2147483647
    return image_id1, image_id2

def extract_matches_entry(output):	
    matches_data = {}  # Diccionario para almacenar los datos extraídos
    num_images = 0  # Contador del número de imágenes
    
    # Conectar a la base de datos
    try:
        conn = sqlite3.connect(config.db_file)
        output.set_output("Connection to the database successfully established.")

    except sqlite3.Error as e:
        output.set_output(f"Error when connecting to the database: {e}")
        return {}, 0
    
    cursor = conn.cursor()
    
    try:
        # Consulta SQL para extraer image_id y rows de la tabla keypoints
        cursor.execute("SELECT pair_id, rows FROM matches where rows > 0")
        
        # Extraer todas las filas de una vez
        rows = cursor.fetchall()
        
        # Procesar las filas extraídas
        for row in rows:
            pair_id, num_matches = row

            # Almacenar los datos en el diccionario
            matches_data[pair_id] = num_matches
            num_images += 1

    except sqlite3.Error as e:
        output.set_output(f"Error when executing the query: {e}")
        return {}, 0

    finally:
        # Cerrar la conexión a la base de datos
        conn.close()
    
    return matches_data, num_images

def extract_matches(output):	
    matches_data = {}  # Diccionario para almacenar los datos extraídos
    num_images = 0  # Contador del número de imágenes
    
    # Conectar a la base de datos
    try:
        conn = sqlite3.connect(config.db_file)
        output.set_output("Connection to the database successfully established.")

    except sqlite3.Error as e:
        output.set_output(f"Error when connecting to the database: {e}")
        return {}, 0
    
    cursor = conn.cursor()
    
    try:
        # Consulta SQL para extraer image_id y rows de la tabla keypoints
        cursor.execute("SELECT pair_id, rows, data FROM matches ORDER BY rows DESC LIMIT 100")
        
        # Extraer todas las filas de una vez
        rows = cursor.fetchall()
        
        # Procesar las filas extraídas
        for row in rows:
            pair_id, num_matches, data = row

            id1, id2 = pair_id_to_image_ids(pair_id)

            # Leer el blob de datos binarios
            data_bytes = io.BytesIO(data)
            
            matches = np.frombuffer(data_bytes.read(), dtype=np.uint32)
            reshaped_matches = matches.reshape(-1, 2)
            
            # Almacenar los datos en el diccionario
            matches_data[(id1, id2)] = (reshaped_matches, num_matches)
            num_images += 1

    except sqlite3.Error as e:
        output.set_output(f"Error when executing the query: {e}")
        return {}, 0

    finally:
        # Cerrar la conexión a la base de datos
        conn.close()
    
    return matches_data, num_images

def extract_id_and_keypoints(output):
    num_images = 0  # Contador del número de imágenes
    data = {}
    # Conectar a la base de datos
    try:
        conn = sqlite3.connect(config.db_file)
        output.set_output("Connection to the database successfully established.")

    except sqlite3.Error as e:
        output.set_output(f"Error when connecting to the database: {e}")
        return {}, 0
    
    output.set_output("Extracting data...")

    cursor = conn.cursor()
    
    try:
        # Consulta SQL para extraer image_id y rows de la tabla keypoints
        cursor.execute("SELECT image_id, rows FROM keypoints")
        
        # Extraer todas las filas de una vez
        rows = cursor.fetchall()
        
        # Procesar las filas extraídas
        for row in rows:
            id, keypoints = row
            data[id] = keypoints
            num_images += 1

        output.set_output("Data extracted correctly.")

    except sqlite3.Error as e:
        output.set_output(f"Error when executing the query: {e}")
        
    finally:
        # Cerrar la conexión a la base de datos
        conn.close()

    return data, num_images