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
from datetime import datetime, timedelta
def load_color_mapping( mapping_file):
        """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)
                    return mapping
                except json.JSONDecodeError:
                    print("Error decoding JSON from the file. Returning empty mapping.")
                    return {}
        return {}
    
def save_color_mapping(mapping,mapping_file):
        """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,mapping_file):
        """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,mapping_file)  # 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))
    

def parse_date(date_str):
    """Convierte un string en formato YYYYMMDDHH a un objeto datetime."""
    return datetime.strptime(date_str, '%Y%m%d%H')

def format_date(date):
    """Convierte un objeto datetime a un string en formato YYYYMMDDHH."""
    return date.strftime('%Y%m%d%H')

def generate_date_range(datestart_str, dateend_str):
    """Genera una lista de fechas desde datestart menos 6 horas hasta dateend en formato YYYYMMDDHH."""
    # Convierte las cadenas en objetos datetime
    datestart = parse_date(datestart_str)
    dateend = parse_date(dateend_str)

    # Resta 6 horas a datestart
   # datestart -= timedelta(hours=6)

    # Genera una lista de fechas desde datestart hasta dateend
    date_list = []
    current_date = datestart
    while current_date <= dateend:
        date_list.append(format_date(current_date))
        current_date += timedelta(hours=6)  # Cambia el intervalo según sea necesario

    return date_list# Directory and file pattern
def generate_random_color():
        """Genera un color hexadecimal aleatorio."""
        return "#{:06x}".format(random.randint(0, 0xFFFFFF))
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

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]
import sqlite3
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from collections import defaultdict
import json
import os

def parse_date(date_str):
    try:
        # Assuming date format is 'YYYYMMDDHH' or 'YYYY-MM-DD HH:MM:SS'
        return pd.to_datetime(date_str, format='%Y%m%d%H', errors='coerce')
    except ValueError:
        return pd.NaT
def flags_plot(pathwork,
               datestart,
               dateend,
               family,
               plot_title,
               vcoord,
               id_stn, 
               varno,
               region):
    db_path = f'{pathwork}/output_file{region}.db'
    
    # Connect to the SQLite database
    connection = sqlite3.connect(db_path)
    cursor = connection.cursor()
    
    dates = generate_date_range(datestart, dateend)
    dates.sort()  # Ensure dates are in order
    
    # Initialize data structures for storing results
    results_by_file = defaultdict(lambda: defaultdict(int))
    total_observations_by_file = {}
    
    # Bit descriptions for flags
    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: "Rejected by satellite QC",
        8: "Unselected (blacklisted)", 9: "Rejected for background check", 10: "Generated by analysis",
        11: "Unselected channel", 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 above model top", 22: "QC Obs rejection", 23: "Cloud affected radiance"
    }
    
    # Fetch total observations and flag values in one go
    for date in dates:
        query_id_stn=''
        query_vcoord=''
        if id_stn!='join':
            query_id_stn=f" and id_stn='{id_stn}'"
        if vcoord!='join':
            query_vcoord=f" and vcoord='{vcoord}'"
 



        cursor.execute(f"""
            SELECT flag, sum(num_data_entries)
            FROM flag_observations
            WHERE varno={varno} AND date={date} {query_id_stn} {query_vcoord}
            GROUP BY flag
        """)
        flag_data = cursor.fetchall()
        
        total_observations = sum(num for _, num in flag_data)
        total_observations_by_file[date] = total_observations
        
        for flag, num_observations in flag_data:
            results_by_file[date][flag] = num_observations

    # Normalize data and calculate cumulative percentages
    normalized_data = defaultdict(lambda: defaultdict(lambda: (0, 0)))
    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 and observations for each flag
    average_percentage_by_flag = {}
    average_obs_by_flag = {}
    total_observations_all_files = sum(total_observations_by_file.values())
    
    all_flags = set(flag for counts in results_by_file.values() for flag in counts)
    
    for flag in all_flags:
        total_flag_observations = sum(results_by_file[date].get(flag, 0) for date in dates)
        average_percentage_by_flag[flag] = (total_flag_observations / total_observations_all_files) * 100
        average_obs_by_flag[flag] = total_flag_observations
    
    # Prepare for plotting
    fig = plt.figure(figsize=(20, 27))
    gs = fig.add_gridspec(14, 5, height_ratios=[3000] + [1000] * 13, hspace=1)
    ax1 = fig.add_subplot(gs[0, :])
    
    file_names = list(total_observations_by_file.keys())
    x = np.arange(len(file_names))
    bar_width = 1 / len(file_names)

    # Load color mapping
    with open('color_mapping.json') as f:
        color_mapping = json.load(f)
    
    x_positions = []
    
    for file_idx, date in enumerate(dates):
        sorted_flags = sorted(average_percentage_by_flag.items(), key=lambda item: item[1], reverse=True)
        sorted_flags_keys = [flag for flag, _ in sorted_flags] 
        print ("DAVID", str(flag))

        for flag_idx, flag in enumerate(sorted_flags_keys): 
            print ("DAVID", str(flag))

            try:
                start, end = normalized_data[date][flag]
            except KeyError:
                start = 99.999
                end = 100
            label = (f'Flag {flag} (Active bits: {bits_active(flag)}, '
                     rf'Average percentage $^1$ : {format_avg_percentage(average_percentage_by_flag[flag])}%, '
                     rf'Average number of observations $^2$: {format_avg_obs(average_obs_by_flag[flag], len(dates))})') \
                    if file_idx == 0 else ""
            x_position = file_idx + bar_width * np.arange(len(dates)) - 0.5
            x_positions.extend(x_position)
            print ("DAVID", str(flag))
            ax1.bar(x_position, end - start, bottom=start, width=bar_width,
                    label=label, color=get_color_for_flag(str(flag), color_mapping,'color_mapping.json'))
    
    ax1.set_xlim(min(x_positions), max(x_positions) + bar_width)
    ax1.set_ylim(0, 100)
    ax1.set_yticks(np.arange(0, 101, 20))
    ax1.set_xlabel('Date', fontsize=15)
    ax1.set_ylabel('Percentage of N. Obs.', fontsize=15)
    ax1.set_title(f'Normalized Distribution Postalt of Flags and Active Bits per File \n {datestart}-{dateend} # Family:{family} # Channel:{vcoord} # Sat:{id_stn}', fontsize=15)
    ax1.set_xticks(x[::max(1, len(file_names) // 6)])
    ax1.set_xticklabels([os.path.basename(file)[0:10] for file in file_names][::max(1, len(file_names) // 6)])
    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)
    
    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."
    )
    fig.text(0.5, 0.5, explanation_text, ha='center', va='top', fontsize=7, linespacing=1.5)

    # Calculate bit observations
    bit_observations = defaultdict(int)
    for date in dates:
        cursor.execute(f"""
            SELECT flag, sum(num_data_entries)
            FROM flag_observations
            WHERE varno={varno} AND date={date}  {query_id_stn} {query_vcoord}

            GROUP BY flag
        """)
        flag_data = cursor.fetchall()
        for flag, num_observations in flag_data:
            bits = bits_active(flag)
            for bit in bits:
                bit_observations[bit] += num_observations

    existing_bits = [bit for bit in range(24) if bit_observations[bit] > 0]
    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)
    
    # Plot pie charts for each bit
    num_cols = 5
    num_rows = (len(sorted_bits) + (num_cols - 3)) // (num_cols - 2) + 1
    
    for i, (bit, percentage) in enumerate(sorted_bits):
        row = i // (num_cols - 2) + 2
        col = (i % (num_cols - 2)) + 2
        ax2 = fig.add_subplot(gs[row, col])
        
        sizes = [percentage, 100 - percentage]
        significant_threshold = 1 
        if percentage < significant_threshold:
            labels = [f'Bit {bit}\n{percentage:.1g}%', f'Rest (Bits)\n{round(100 - percentage, 2)}%']
        else:
            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=None, startangle=190,
                wedgeprops={'edgecolor': 'black'}, textprops=textprops, labeldistance=1.2)
        ax2.set_title(f'Bit {bit}: \n {bit_descriptions.get(bit, "Unknown")}', pad=10, fontsize=10)
        ax2.axis('equal')

    plt.rcParams.update({'font.size': 30})
    plt.savefig(f'{pathwork}/combined_postalt_channel_{vcoord}_id_stn_{id_stn}_{varno}_plot.png', bbox_inches='tight')
    connection.close()
