import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import os
import numpy as np
from sklearn.cluster import DBSCAN

def moving_average(data, window_size):
    """Compute the moving average of data."""
    return data.rolling(window=window_size).mean()

def identify_fixations(point_of_gaze_x, point_of_gaze_y, proximity_threshold=100, min_samples=2):
    # Combine X and Y coordinates into a 2D array
    coordinates = np.column_stack((point_of_gaze_x, point_of_gaze_y))
    
    # Use DBSCAN to cluster points of gaze
    dbscan = DBSCAN(eps=proximity_threshold, min_samples=min_samples)
    cluster_labels = dbscan.fit_predict(coordinates)
    
    # Initialize list to store fixations
    fixations = []
    
    # Iterate through unique cluster labels
    unique_labels = np.unique(cluster_labels)
    for label in unique_labels:
        if label == -1:
            # Ignore noise points (not assigned to any cluster)
            continue
        
        # Extract points belonging to the current cluster
        cluster_points = coordinates[cluster_labels == label]
        
        # Calculate the centroid of the cluster
        centroid_x = np.mean(cluster_points[:, 0])
        centroid_y = np.mean(cluster_points[:, 1])
        
        # Add the centroid to fixations list
        fixations.append([centroid_x, centroid_y])
    
    return np.array(fixations)

def generate_diagrams(user_id):
    # Generate the file path based on user ID
    csv_file = f"user_{user_id}_eye_tracking.csv"

    # Check if the file exists
    if not os.path.exists(csv_file):
        print(f"Error: File {csv_file} not found.")
        return None

    # Read the CSV file into a pandas DataFrame
    df = pd.read_csv(csv_file)

    # Adjust the duration to 30 seconds
    df = df[df["Time(s)"] <= 30]

    # Group the data by time and select the first Point of Gaze point for each second
    df_grouped = df.groupby(df["Time(s)"].astype(int))

    # Extract the Point of Gaze coordinates and corresponding time
    point_of_gaze_x = []
    point_of_gaze_y = []
    seconds = []
    window_ids = []  # New list to store window IDs
    corneal_reflections = []  # New list to store corneal reflections

    for second, data in df_grouped:
        point_of_gaze_x.append(data["Point of Gaze X"].iloc[0])
        point_of_gaze_y.append(data["Point of Gaze Y"].iloc[0])
        seconds.append(second)
        window_ids.append(data["Window ID"].iloc[0])  # Add window ID data
        corneal_reflections.append(data["Corneal Reflection"].iloc[0])  # Add corneal reflection data

    # Create subplots in a 3x3 grid with adjusted height ratios
    fig, axes = plt.subplots(3, 3, figsize=(15, 18), gridspec_kw={'height_ratios': [1, 1, 1.2]})

    # Adjust the top space to make room for the titles of upper graphs
    plt.subplots_adjust(top=0.88)

    # Plot Point of Gaze coordinates
    axes[0, 0].scatter(point_of_gaze_x, point_of_gaze_y, c='blue', label='Point of Gaze')
    for i, txt in enumerate(seconds):
        axes[0, 0].annotate(txt, (point_of_gaze_x[i], point_of_gaze_y[i]), textcoords="offset points", xytext=(0,10), ha='center')
    axes[0, 0].set_xlabel("X-coordinate (pixels)")
    axes[0, 0].set_ylabel("Y-coordinate (pixels)")
    axes[0, 0].set_title("Point of Gaze Coordinates Over Time (Every Second)")
    axes[0, 0].legend()
    axes[0, 0].grid(True)

    # Plot saccadic movements
    for i in range(len(point_of_gaze_x) - 1):
        axes[0, 1].plot([point_of_gaze_x[i], point_of_gaze_x[i+1]], [point_of_gaze_y[i], point_of_gaze_y[i+1]], c='red')
    axes[0, 1].scatter(point_of_gaze_x, point_of_gaze_y, c='blue', label='Point of Gaze')
    axes[0, 1].set_xlabel("X-coordinate (pixels)")
    axes[0, 1].set_ylabel("Y-coordinate (pixels)")
    axes[0, 1].set_title("Saccadic Movements")
    axes[0, 1].legend()
    axes[0, 1].grid(True)

    # Plot corneal reflections over time
    axes[1, 2].plot(df["Time(s)"], df["Corneal Reflection"], c='purple')
    axes[1, 2].set_xlabel("Time (seconds)")
    axes[1, 2].set_ylabel("Corneal Reflection")
    axes[1, 2].set_title("Corneal Reflection Over Time")
    axes[1, 2].grid(True)

    # Plot movement pursuit
    for i in range(len(seconds) - 1):
        # Get start and end points
        x0, y0 = point_of_gaze_x[i], point_of_gaze_y[i]
        x1, y1 = point_of_gaze_x[i+1], point_of_gaze_y[i+1]

        # Number of bends
        num_bends = np.random.randint(3, 10)  # Random number of bends between 3 and 10
        bend_factors = np.random.rand(num_bends) * 0.2  # Random bend factors
        bend_points_x = np.linspace(x0, x1, num_bends + 2)[1:-1]  # Bend points along X-axis
        bend_points_y = np.linspace(y0, y1, num_bends + 2)[1:-1]  # Bend points along Y-axis

        # Apply random bend factors
        bend_points_x += (np.random.choice([-1, 1], size=num_bends) * bend_factors * (x1 - x0))
        bend_points_y += (np.random.choice([-1, 1], size=num_bends) * bend_factors * (y1 - y0))

        # Combine all points
        all_points_x = np.concatenate([[x0], bend_points_x, [x1]])
        all_points_y = np.concatenate([[y0], bend_points_y, [y1]])

        # Plot line segments
        for j in range(len(all_points_x) - 1):
            axes[0, 2].plot([all_points_x[j], all_points_x[j+1]], [all_points_y[j], all_points_y[j+1]], c='green', alpha=0.5)

    axes[0, 2].scatter(point_of_gaze_x, point_of_gaze_y, c='blue', label='Point of Gaze')
    axes[0, 2].set_xlabel("X-coordinate (pixels)")
    axes[0, 2].set_ylabel("Y-coordinate (pixels)")
    axes[0, 2].set_title("Movement Pursuit")
    axes[0, 2].legend()
    axes[0, 2].grid(True)

    # Plot pupil dilation over time
    window_size = 10  # Adjust the window size for smoothing
    smoothed_pupil_dilation = moving_average(df["Pupil Dilation"], window_size)
    axes[1, 0].plot(df["Time(s)"], smoothed_pupil_dilation, c='orange')
    axes[1, 0].set_xlabel("Time (seconds)")
    axes[1, 0].set_ylabel("Pupil Dilation")
    axes[1, 0].set_title(f"Smoothed Pupil Dilation (Moving Average, Window Size = {window_size})")
    axes[1, 0].grid(True)

    # Plot a heatmap of Point of Gaze locations using the "plasma" colormap for clarity
    sns.kdeplot(x=df["Point of Gaze X"], y=df["Point of Gaze Y"], cmap="plasma", fill=True, thresh=0, levels=100, clip=[(0, 1920), (0, 1080)], ax=axes[1, 1], alpha=0.8, cbar=True, cbar_kws={'label': 'Density'})

    # Adjust the brightness of the heatmap
    axes[1, 1].collections[0].set_alpha(0.4)  # Adjust alpha value to make bright spots brighter

    axes[1, 1].set_xlabel("X-coordinate (pixels)")
    axes[1, 1].set_ylabel("Y-coordinate (pixels)")
    axes[1, 1].set_title("Heatmap of Point of Gaze Locations")

    # Plot window IDs in a table
    table_ax = axes[2, 0].table(cellText=[[i, j] for i, j in zip(seconds[::10], window_ids[::10])], loc='center')
    table_ax.auto_set_font_size(False)
    table_ax.set_fontsize(10)
    table_ax.scale(1, 1.5)

    # Add a title to the table subplot with lowered position
    table_ax_title = axes[2, 0].text(0.5, 0.95, 'Window ID per 10 Seconds', fontsize=12, ha='center')

    # Remove axis for the window ID subplot
    axes[2, 0].axis('off')
    axes[2, 2].remove()
    
    # Plot fixations with clusters
    axes[2, 1].scatter(point_of_gaze_x, point_of_gaze_y, c='blue', label='Point of Gaze')

    # Identify fixations
    fixations = identify_fixations(point_of_gaze_x, point_of_gaze_y, proximity_threshold=100)

    # Plot fixations with larger circles
    for fixation in fixations:
        x, y = fixation
        radius = 50  # Adjust the radius of the circle as needed
        circle = plt.Circle((x, y), radius, color='red', fill=False)
        axes[2, 1].add_artist(circle)

    axes[2, 1].set_xlabel("X-coordinate (pixels)")
    axes[2, 1].set_ylabel("Y-coordinate (pixels)")
    axes[2, 1].set_title("Fixations with Clusters")
    axes[2, 1].legend()
    axes[2, 1].grid(True)

    # Adjust spacing between subplots
    plt.tight_layout()

    # Adjust spacing between subplots to avoid overlap of titles
    plt.subplots_adjust(hspace=0.35)

    return fig

# Define NUM_USERS
NUM_USERS = 3

# Call the function for each user's CSV file
all_figures = []
for user_id in range(1, NUM_USERS + 1):
    fig = generate_diagrams(user_id)
    if fig is not None:
        all_figures.append(fig)

# Display all figures together
plt.show()
