import glob
import sqlite3
import os
import matplotlib.pyplot as plt
import numpy as np
from collections import defaultdict
import json
import os
import random
import matplotlib.colors as mcolors
import matplotlib.pyplot as plt

# Directory and file pattern
def flags_plot(pathwork,
        datestart,
        dateend,
        family,
        plot_title,
        vcoord,
        id_stn, 
        varno,
        region):
        
    ##directory_path = f'{pathwork}/output_file{region}.db'  
    #files = glob.glob(os.path.join(directory_path, file_pattern))
    #print (files)
    # Extract date from filename
    #extract_date_from_filename = lambda filename: os.path.basename(filename)[:10]
    #files.sort(key=extract_date_from_filename)
    

    db_path = f'{pathwork}/output_file{region}.db'

    # Connect to the SQLite database
    connection = sqlite3.connect(db_path)
    cursor = connection.cursor()

    # Get distinct dates from the flag_observations table
    cursor.execute("SELECT DISTINCT date FROM flag_observations")
    dates = [row[0] for row in cursor.fetchall()]
    dates.sort()  # Ensure dates are in order
    # Check all active bits
    def bits_active(number):
        if number == 0:
            return [0]  # Si el número es 0, devolver [0] explícitamente
        return [bit for bit in range(24) if (number & (1 << bit)) != 0]
    
    results_by_file = defaultdict(lambda: defaultdict(int))
    total_observations_by_file = {}
    
    # Bit descriptions
    bit_descriptions = {
        0: "Modified or generated by ADE", 1: "exceeds climate extreme", 2: "Erroneous", 3: "possibly erroneous",
        4: "doubtful", 5: "interpolated", 6: "Bias corrected", 7: "AMSU,SSMIS: rejection by satellite QC \n for various reasons ", 8: "Unselected (blacklisted)",
        9: "Rejected for background check", 10: "generated by analysis", 11: "MW,GR: unselected channel \n MW: thinned data; uncorrected data \n IA: uncorrected data plus data flagged by \n satellite QC for rejection for other reasons",
        12: "Assimilated", 13: "O-P rogue check failure level 1", 14: "O-P rogue check failure level 2",
        15: "O-P rogue check failure level 3", 16: "Rejection for O-P magnitude", 17: "rejected by QC-Var 3DVAR",
        18: "Rejected over land due to higher topography", 19: "Rejected due to land/sea mask",
        20: "aircraft TrackQC rejection", 21: "rejected due to transmittance (weighting function) above model top",
        22: "QC Obs rejection", 23: "Cloud affected radiance"
    }
    
    # Process each SQLite file
    for date in dates:
    #    cursor.execute(f"SELECT count(*) FROM data natural join header  where id_stn='{id_stn}' and varno={varno} and vcoord={vcoord}")
        cursor.execute(f"SELECT count(*) FROM data natural join header  where varno={varno} and date={date}")



        total_observations_by_file[file] = cursor.fetchone()[0]
     #  cursor.execute(f"SELECT DISTINCT flag FROM data natural join header  where id_stn='{id_stn}' and varno={varno} and vcoord={vcoord}")
       
        cursor.execute(f"SELECT DISTINCT flag FROM data natural join header  where varno={varno} and date={date}")
        flag_values = cursor.fetchall()
        for value in flag_values:
            flag = value[0]
            bits = bits_active(flag) 
           # cursor.execute(f"SELECT count(*) FROM data natural join header  WHERE flag = {flag} and  id_stn='{id_stn}' and varno={varno} and vcoord={vcoord}")
            
            cursor.execute(f"SELECT count(*) FROM data natural join header  WHERE flag = {flag} and  varno={varno} and date={date}")
            num_observations = cursor.fetchone()[0]
            results_by_file[date][flag] += num_observations
    
        connection.close()
    # Normalize data and calculate cumulative percentages
    normalized_data = defaultdict(lambda: defaultdict(float))
    numberobs_data = defaultdict(lambda: defaultdict(float))
    
    for date, counts in results_by_file.items():
        total_observations = total_observations_by_file[date]
        accumulated = 0
        for flag, num_observations in sorted(counts.items(), key=lambda item: item[1], reverse=True):
            percentage = (num_observations / total_observations) * 100
            normalized_data[date][flag] = (accumulated, accumulated + percentage)
            numberobs_data[date][flag] = num_observations
            accumulated += percentage
    # Calculate average percentage for each flag across all files
    average_percentage_by_flag = {}
    average_obs_by_flag = {}
    # Iterar sobre los archivos
    #for file in files:
    #    percentage_values = []
    #    obs_values = []
    #    
    #    # Obtener los flags presentes en el archivo actual
    #    flags_in_file = normalized_data[file].keys()
    #    
    #    # Iterar sobre los flags presentes en el archivo actual
    #    for flag in flags_in_file:
    #        # Asegurarse de que el flag está presente en normalized_data[file]
    #        if flag in normalized_data[file]:
    #            start, end = normalized_data[file][flag]
    #            percentage_values.append(end - start)
    #            obs_values.append(numberobs_data[file][flag])
    #    
    #    # Calcular el promedio de los valores obtenidos para cada flag en este archivo
    #    if flags_in_file:
    #        average_percentage_by_flag[file] = np.mean(percentage_values)
    #        average_obs_by_flag[file] = np.mean(obs_values)
    all_flags = set()
    for file in dates:
        all_flags.update(normalized_data[file].keys())
   # for flag in all_flags:
   #     percentage_values = []
   #     obs_values = []
   #     for file in files:
   #         if flag in normalized_data[file]:
#            
   #            start, end = normalized_data[file][flag]
    #           print (start, end)
    #           percentage_values.append(end - start)
    #           obs_values.append(numberobs_data[file][flag])
    #    average_percentage_by_flag[flag] = np.mean(percentage_values)
    #    average_obs_by_flag[flag] = np.mean(obs_values)
    total_observations_all_files = sum(total_observations_by_file.values())
    for flag in set(flag for counts in results_by_file.values() for flag in counts):
        total_flag_observations = sum(results_by_file[file][flag] for file in dates)
        average_percentage_by_flag[flag] = (total_flag_observations / total_observations_all_files) * 100
        average_obs_by_flag[flag] = total_flag_observations
   # exit()
    # Plot stacked bar chart and pie charts
    fig = plt.figure(figsize=(20, 20))
    gs = fig.add_gridspec(14, 5, height_ratios=[3000, 1000, 1000, 1000, 1000, 1000, 1000,1000,1000,1000,1000,1000,1000,1000], hspace=1)
    ax1 = fig.add_subplot(gs[0, :])
    fig.subplots_adjust(top=0.9, bottom=0.1, left=0.1, right=0.9, hspace=1, wspace=0.9)
    
    file_names = list(total_observations_by_file.keys())
    x = np.arange(len(file_names))
    bar_width = 1 / len(file_names)

    all_flags = set()
 
    for file in dates:
        connection = sqlite3.connect(file)
        cursor = connection.cursor()
        cursor.execute(f"SELECT DISTINCT flag FROM data natural join header")
        flag_values = cursor.fetchall()

        for value in flag_values:
            flag = value[0]
            print ("flagflagflagflag",flag)
            all_flags.add(flag)
      #  exit()
        connection.close()

    # Crear un mapeo de colores universal
   # unique_colors = plt.cm.get_cmap('tab20', len(all_flags))
   # color_mapping = {flag: unique_colors(i) for i, flag in enumerate(sorted(all_flags))}
    mapping_file = 'color_mapping.json'

    def load_color_mapping():
        """Carga el mapeo de colores desde el archivo JSON."""
        if os.path.exists(mapping_file):
            with open(mapping_file, 'r') as file:
                try:
                    mapping = json.load(file)
                    print("Loaded color mapping:", mapping)  # Verifica el contenido cargado
                    return mapping
                except json.JSONDecodeError:
                    print("Error decoding JSON from the file. Returning empty mapping.")
                    return {}
        return {}
    
    def save_color_mapping(mapping):
        """Guarda el mapeo de colores en el archivo JSON."""
        with open(mapping_file, 'w') as file:
            json.dump(mapping, file, indent=4)
            print("Saved color mapping:", mapping)  # Verifica el contenido guardado
    
    def get_color_for_flag(flag, color_mapping):
        """Obtiene el color para un flag. Si no existe, asigna uno nuevo y actualiza el archivo."""
        if flag not in color_mapping:
            print(f"Flag {flag} not found in color mapping. Assigning a new color.")  # Mensaje de advertencia
            new_color = generate_random_color()
            color_mapping[flag] = new_color
            save_color_mapping(color_mapping)  # Guarda solo el nuevo mapeo
        return color_mapping[flag]
    
    def generate_random_color():
        """Genera un color hexadecimal aleatorio."""
        return "#{:06x}".format(random.randint(0, 0xFFFFFF))
    
    # Cargar el mapeo de colores
    color_mapping = load_color_mapping()

 #  mapping_file = 'color_mapping.json'
 #  
 #   def load_color_mapping():
 #       """Carga el mapeo de colores desde el archivo JSON."""
 #       if os.path.exists(mapping_file):
 #           with open(mapping_file, 'r') as file:
 #               try:
 #                   mapping = json.load(file)
 #                   print("Loaded color mapping:", mapping)  # Verifica el contenido cargado
 #                   return mapping
 #               except json.JSONDecodeError as e:
 #                   print(f"Error decoding JSON: {e}")
 #                   return {}
 #       return {}
 #   
 #   def get_color_for_flag(flag, color_mapping):
 #       """Obtiene el color para un flag. Si no existe, se detiene la ejecución."""
 #       print(f"Searching for flag: {flag} (type: {type(flag)})")
 #       if flag not in color_mapping:
 #           raise KeyError(f"Flag {flag} not found in color mapping. Please add it to the file.")
 #       return color_mapping[flag]
 #
 #   
 #   
 #   # Cargar el mapeo de colores
 #   color_mapping = load_color_mapping()
    # Aquí deberías añadir la lógica para obtener todos los flags
    all_flags = {8390912, 0, 2240, 8390720}
    # Cargar el mapeo de colores
    color_mapping = load_color_mapping()
    all_flags = {8390912, 0, 4, 2240, 8390720}  # Reemplaza esto con la lógica específica para tus flags

    # Obtener el color para cada flag
    for flag in all_flags:
        color = get_color_for_flag(str(flag), color_mapping) 
        print(f"Flag: {flag}, Color: {color}")  # Verifica los colores asignados
     #   unique_colors = plt.cm.get_cmap('tab20', len(average_percentage_by_flag))
    x_positions = []
    for file_idx, file in enumerate(files):
        sorted_flags = sorted(average_percentage_by_flag.items(), key=lambda item: item[1], reverse=True)
        sorted_flags_keys = [flag for flag, _ in sorted_flags]
        normalized_data[file] = {flag: normalized_data[file][flag] for flag in sorted_flags_keys}
        for flag_idx, flag in enumerate(sorted_flags_keys):
           # print (normalized_data[file])
            try:
               start, end = normalized_data[file][flag]
            except:
               start = 99.999
               end   = 100
          #  print ('=====', start, end)
            bits = bits_active(flag)
            avg_percentage = average_percentage_by_flag[flag]
            avg_obs = average_obs_by_flag[flag]
            def format_avg_obs(avg_obs, num_files):
                avg_per_file = avg_obs / num_files
                if avg_per_file >= 1:
                    return f"{int(round(avg_per_file))}"
                else:
                    # Redondear a la primera cifra significativa
                    return f"{avg_per_file:.1g}"
            def format_avg_percentage(avg_percentage):
                if avg_percentage >= 0.001:
                    return f"{avg_percentage:.3f}"  # 3 decimales
                else:
                    return f"{avg_percentage:.1g}"  # Primera cifra significativa
                        # Usar la función en el código

            formatted_avg_obs = format_avg_obs(avg_obs, len(files))
            formatted_avg_percentage = format_avg_percentage(avg_percentage)
                       # label = f'Flag {flag} (Active bits: {bits}, Avg % (N. Obs. Total): {avg_percentage:.3f}, Avg N. Obs per day: {round(avg_obs,1)})' if file_idx == 0 else ""
            label = (f'Flag {flag} (Active bits: {bits}, '
                 fr'Avg % $^1$: {formatted_avg_percentage}, '
                 fr'Avg N. Obs. $^2$ : {formatted_avg_obs})') if file_idx == 0 else ""
            x_position = file_idx + bar_width * np.arange(len(files)) - 0.4
       #     print ('--->',color_mapping[int(flag)],flag)
            x_positions.extend(x_position)  
            ax1.bar(x_position, end - start, bottom=start, width=bar_width, label=label, color=get_color_for_flag(str(flag), color_mapping))
    
    x_min = min(x_positions)
    x_max = max(x_positions) + bar_width
    ax1.set_xlim(x_min, x_max)
    ax1.set_ylim(0, 100)
    ax1.set_yticks(np.arange(0, 101, 20)) 
    gs.update(wspace=0.3)
    ax1.set_xlabel('Date', fontsize=15 )
    ax1.set_ylabel('Percentage of N. Obs.', fontsize=15 )
    ax1.set_title(f'Normalized Distribution of Flags and Active Bits per File \n 20220601-20220620 {family}', fontsize=15 )
    tick_interval = max(1, len(file_names) // 6)
    ax1.set_xticks(x[::tick_interval])
    ax1.set_xticklabels([os.path.basename(file)[0:10] for file in file_names][::tick_interval])
   # ax1.legend(loc='center left', bbox_to_anchor=(1.1, -1.5))
    ax1.legend(loc='upper center', bbox_to_anchor=(0.15, -0.4), ncol=1, frameon=False, fontsize=8)
    ax1.tick_params(axis='x', labelsize=18)
    ax1.tick_params(axis='y', labelsize=18)
    bit_observations = defaultdict(int)
    total_observations_all_files = 0
    explanation_text = (
    r"$^1$ Average percentage is calculated based on the total number of observations.\n"
    r"$^2$ Average number of observations per 6-hour intervals."
)
    explanation_text = (
    r"$^1$ Average percentage is calculated based on the total number of observations."
    r"$^2$ Average number of observations per 6-hour intervals."
)

# Añadir el texto al gráfico
# transform=ax.transAxes coloca el texto en coordenadas relativas al área de los ejes
   # ax.text(0.5, 0.2, explanation_text, ha='center', va='top', fontsize=10, transform=ax.transAxes)
    fig.text(0.5, 0.5, explanation_text, ha='center', va='top', fontsize=7, linespacing=1.5)

  #  fig.text(0.5, 0.1, explanation_text, ha='center', fontsize=10)
    for file in files:
        connection = sqlite3.connect(file)
        cursor = connection.cursor() 
        cursor.execute(f"SELECT count(*) FROM data  natural join header where  varno={varno}")
        
      #  cursor.execute(f"SELECT count(*) FROM data  natural join header where  id_stn='{id_stn}' and varno={varno} and vcoord={vcoord}")
        total_observations_all_files += cursor.fetchone()[0]
        cursor.execute(f"SELECT DISTINCT flag FROM data natural join header   where varno={varno}")
        
      #  cursor.execute(f"SELECT DISTINCT flag FROM data natural join header   where  id_stn='{id_stn}' and varno={varno} and vcoord='{vcoord}'")
        flag_values = cursor.fetchall()
    
        for value in flag_values:
            flag = value[0]
            bits = bits_active(flag)
           # cursor.execute(f"SELECT count(*) FROM data natural join header  WHERE flag = {flag} and  id_stn='{id_stn}' and varno={varno} and vcoord={vcoord}")
            cursor.execute(f"SELECT count(*) FROM data natural join header  WHERE flag = {flag} and  varno={varno}")
            
            num_observations = cursor.fetchone()[0]
            for bit in bits:
                bit_observations[bit] += num_observations
    
        connection.close()
    
    existing_bits = [bit for bit in range(24) if bit_observations[bit] > 0]
    for bit in existing_bits:
       print ('--->',bit, bit_observations[bit])
    print (total_observations_all_files)
    bit_percentages = {bit: (bit_observations[bit] / total_observations_all_files) * 100 for bit in existing_bits}
    sorted_bits = sorted(bit_percentages.items(), key=lambda x: x[1], reverse=True)
    
    #
   # handles, labels = ax1.get_legend_handles_labels()
 #   ncol = 2
#    nrows = (len(handles) + ncol - 1) // ncol  
  #  print ("Calcular el número de filas", nrows )
    # Create pie charts
    num_cols = 5
    total_plots = len(sorted_bits) 
    num_rows = (total_plots + (num_cols - 3)) // (num_cols - 2) 
    for i, (bit, percentage) in enumerate(sorted_bits):
        row = i // (num_cols - 2) + 2
        col = (i % (num_cols - 2)) + 2  # Comienza en la segunda columna (índice 1)
      #  row = row = 3 + 2 * (i // 2) 
      #  col = (i % 2) + 1
        if row >= num_rows + 2:  # Asegúrate de que el índice de fila esté dentro del rango válido
            row = num_rows + 1  # Ajusta a la última fila si es necesario

        if col >= num_cols:  # Asegúrate de que el índice de columna esté dentro del rango válido
            col = num_cols - 1  # Ajusta a la última columna si es necesario

        ax2 = fig.add_subplot(gs[row, col])
    
        sizes = [percentage, 100 - percentage]
        labels = [f' Bit {bit} \n {round(percentage,2)}% ', f' Rest (Bits) \n {round(100 - percentage,2)}% ']
        colors = ['tab:blue', 'white']
   
        textprops = {'color': 'black', 'fontweight': 'bold'}
        #ax2.pie(sizes, labels=labels, colors=colors, autopct='%1.2f%%', startangle=140, wedgeprops={'edgecolor': 'black'})
        ax2.pie(
        sizes,
        labels=labels,
        colors=colors,
        autopct=None,
        startangle=190,
        wedgeprops={'edgecolor': 'black'},
        textprops=textprops,
        labeldistance=1.2  # Mueve las etiquetas más allá del borde del círculo
    )
        ax2.set_title(f'Bit {bit}: \n {bit_descriptions.get(bit, "Unknown")}', pad=10, fontsize=10)#,   fontweight='bold')
     #   ax2.set_title(f'Bit {bit}: \n {bit_descriptions.get(bit, "Unknown")}', fontweight='bold', pad=20, loc='center', bbox={'facecolor':'white', 'alpha':0.5, 'pad':5})
   
        ax2.axis('equal')
    plt.rcParams.update({'font.size': 30})
    fig.tight_layout()
    plt.savefig(f'{pathwork}/combined_channel_all_id_stn_all_{varno}_plot.png', bbox_inches='tight')
