# -*- coding: utf-8 -*-
"""
Created on Wed Sep 30 12:04:56 2026

@author: chuffard
"""

#Plot by hour of day

import numpy as np
from datetime import datetime
import numpy as np
import pandas as pd
import sys
import os.path
import warnings
import glob
import seaborn as sns
import matplotlib.pyplot as plt
from matplotlib.backends.backend_pdf import PdfPages
import matplotlib.colors as mcolors
warnings.filterwarnings("ignore")

atnOG = pd.read_csv('W:/Analyses/2026 Mooring/Mooring2026atn_POC_red.csv')

atnOG["DateTime"] = pd.to_datetime(atnOG['DateTime'])
atnOG = atnOG.sort_values(by='DateTime')
atnOG['atn'] = pd.to_numeric(atnOG['atn'], errors='coerce')
atnOG['rounded_hour'] = atnOG['DateTime'].dt.round('H')
numeric_colsatn = atnOG.select_dtypes(include=['number']).columns
atnOG.columns
# Index(['Unnamed: 0.1', 'Unnamed: 0', 'file_name', 'atn', 'date', 'time',
#        'timestamp', 'DateTime', 'F_atn', 'POCmodel_mg', 'POCmodel_g',
#        'rounded_hour'],
#       dtype='object')
atnOGHour = atnOG.groupby('rounded_hour')[['atn', 'POCmodel_mg']].mean(numeric_only=True).reset_index()
atnOGHour['rm150atn'] = atnOGHour['atn'].rolling(150, min_periods=23).mean()
atnOGHour['Slope_rm_150atn'] = atnOGHour['rm150atn'].diff()
atnOGHour['rm150POCmg'] = atnOGHour['POCmodel_mg'].rolling(150, min_periods=23).mean()
atnOGHour['Slope_rm_150POCmg'] = atnOGHour['rm150POCmg'].diff()

#import fluorescence
fluorescence = pd.read_csv("W:/Deployments/CFE_DP26_1/Data/dep-2026.04.10-02-SED-CFE-DP26_1/fluorometer.csv")
fluorescence["DateTime"] = pd.to_datetime(fluorescence["SES Timestamp (ISO601)"])
fluorescence = fluorescence.sort_values(by='DateTime')
fluorescence['Fluorescense_690'] = fluorescence['Fluorescense 690 Counts']-fluorescence['Measure Ref 690 Counts']
fluorescence['rounded_hour'] = fluorescence['DateTime'].dt.round('H')
fluorescence = fluorescence.groupby('rounded_hour').mean(numeric_only=True).reset_index()
#Round to nearest hour first. This will make it easier to combine later.
fluorescence['rm150Fluorescense_690'] = fluorescence['Fluorescense_690'].rolling(150, min_periods=23).mean()
fluorescence['Slope_rm_150fluoro'] = fluorescence['rm150Fluorescense_690'].diff()
fluorescence.columns
# Index(['rounded_hour', 'Dark Ref Integration Time', 'Dark Ref 690 Counts',
#        'Dark Ref 590 Counts', 'Measure Ref Integration Time',
#        'Measure Ref 690 Counts', 'Measure Ref 590 Counts',
#        'Luminescense Integration Time', 'Luminescense 690 Counts',
#        'Luminescense 590 Counts', 'Fluorescense Integration Time',
#        'Fluorescense 690 Counts', 'Fluorescense 590 Counts',
#        'Phosphorescense Integration Time', 'Phosphorescense 690 Counts',
#        'Phosphorescense 590 Counts',
#        'Constant Target Fluorescense Integration Time',
#        'Constant Target Fluorescense 690 Counts',
#        'Constant Target Fluorescense 590 Counts', 'Code', 'Fluorescense_690',
#        'rm150Fluorescense_690', 'Slope_rm_150fluoro'],
#       dtype='object')
fluorescence_use= fluorescence[['rounded_hour', 'Fluorescense_690','rm150Fluorescense_690', 'Slope_rm_150fluoro']]

merged_atn_fluoro = pd.merge(atnOGHour, fluorescence_use, on='rounded_hour')
merged_atn_fluoro['hour'] = merged_atn_fluoro['rounded_hour'].dt.hour
merged_atn_fluoro['fluoro_per_POCmg'] = merged_atn_fluoro['Fluorescense_690']/merged_atn_fluoro['POCmodel_mg']
merged_atn_fluoro.columns
# Index(['rounded_hour', 'atn', 'POCmodel_mg', 'rm150atn', 'Slope_rm_150atn',
#        'rm150POCmg', 'Slope_rm_150POCmg', 'Fluorescense_690',
#        'rm150Fluorescense_690', 'Slope_rm_150fluoro', 'hour',
#        'fluoro_per_POCmg'],
#       dtype='object')



#Now add triggering

lowtrigger_atn = 0.007 
hightrigger_atn = 0.011
hightrigger_fluoro= 2.4
lowtrigger_fluoro= 1.8  
use_min_period = 24
nrec=150

# Trigger code


#atn trigger base
merged_atn_fluoro["TriggerHigh_atn"] = np.where(merged_atn_fluoro['Slope_rm_150atn'] >= hightrigger_atn, 'Trigger', 'Wait')
merged_atn_fluoro['Rate_HighTrigger_atn'] = (merged_atn_fluoro['TriggerHigh_atn'] == 'Trigger').rolling(144, min_periods=2).sum()
merged_atn_fluoro['Max_HighTrigger_rate_atn'] =  merged_atn_fluoro['Rate_HighTrigger_atn'].cummax()

merged_atn_fluoro["TriggerLow_atn"] = np.where(merged_atn_fluoro['Slope_rm_150atn'] >= lowtrigger_atn, 'Trigger', 'Wait')
merged_atn_fluoro['Rate_LowTrigger_atn'] = (merged_atn_fluoro['TriggerLow_atn'] == 'Trigger').rolling(144, min_periods=2).sum()
merged_atn_fluoro['Max_LowTrigger_rate_atn'] =  merged_atn_fluoro['Rate_LowTrigger_atn'].cummax()

conditionsatn = [
    (merged_atn_fluoro['Rate_LowTrigger_atn']  <= 1), #fire one low, then switch to high trigger
    (merged_atn_fluoro['Rate_HighTrigger_atn'] <= 1), #fire two high, then switch to higher trigger
    (merged_atn_fluoro['Rate_HighTrigger_atn'] ==3), #fire more 3 highs then even higher trigger
    (merged_atn_fluoro['Max_HighTrigger_rate_atn'] >= 4),#fire four or more highs, then use max trigger
    ((merged_atn_fluoro['Rate_HighTrigger_atn']).rolling(window=240, min_periods=1).sum()==0), #more than 1.5 week since last trigger, use the lower gear
    ]


# Define corresponding choices (results)
choicesatn = [np.where(merged_atn_fluoro['Slope_rm_150atn'] >= lowtrigger_atn, 'FireLow', 'Wait'), 
           np.where(merged_atn_fluoro['Slope_rm_150atn'] >= hightrigger_atn, 'FireHigh', 'Wait'), 
           np.where(merged_atn_fluoro['Slope_rm_150atn'] >= (hightrigger_atn*1.1), 'Fire1_1High', 'Wait'),
           np.where(merged_atn_fluoro['Slope_rm_150atn'] >= (hightrigger_atn*1.2), 'Fire1_2High', 'Wait'),
           np.where(merged_atn_fluoro['Slope_rm_150atn'] >= (hightrigger_atn), 'Fire_High', 'Wait')]
           
merged_atn_fluoro["Fire_trigger_atn"] = np.select(conditionsatn, choicesatn, default='Wait')

#fluoro trigger base
merged_atn_fluoro["TriggerHigh_fluoro"] = np.where(merged_atn_fluoro['Slope_rm_150fluoro'] >= hightrigger_fluoro, 'Trigger', 'Wait')
merged_atn_fluoro['Rate_HighTrigger_fluoro'] = (merged_atn_fluoro['TriggerHigh_fluoro'] == 'Trigger').rolling(144, min_periods=2).sum()
merged_atn_fluoro['Max_HighTrigger_rate_fluoro'] =  merged_atn_fluoro['Rate_HighTrigger_fluoro'].cummax()

merged_atn_fluoro["TriggerLow_fluoro"] = np.where(merged_atn_fluoro['Slope_rm_150fluoro'] >= lowtrigger_fluoro, 'Trigger', 'Wait')
merged_atn_fluoro['Rate_LowTrigger_fluoro'] = (merged_atn_fluoro['TriggerLow_fluoro'] == 'Trigger').rolling(144, min_periods=2).sum()
merged_atn_fluoro['Max_LowTrigger_rate_fluoro'] =  merged_atn_fluoro['Rate_LowTrigger_fluoro'].cummax()


conditionsfluoro = [
    (merged_atn_fluoro['Rate_LowTrigger_fluoro']  <= 1), #fire one low, then switch to high trigger
    (merged_atn_fluoro['Rate_HighTrigger_fluoro'] <= 2), #fire two high, then switch to higher trigger
    (merged_atn_fluoro['Rate_HighTrigger_fluoro'] ==3), #fire more 3 highs then even higher trigger
    (merged_atn_fluoro['Max_HighTrigger_rate_fluoro'] >= 4),#fire four or more highs, then use max trigger
    ((merged_atn_fluoro['Rate_HighTrigger_fluoro']).rolling(window=240, min_periods=1).sum()==0), #more than 1.5 week since last trigger, use the lower gear
    ]


# Define corresponding choices (results)
choicesfluoro = [np.where(merged_atn_fluoro['Slope_rm_150fluoro'] >= lowtrigger_fluoro, 'FireLow', 'Wait'), 
           np.where(merged_atn_fluoro['Slope_rm_150fluoro'] >= hightrigger_fluoro, 'FireHigh', 'Wait'), 
           np.where(merged_atn_fluoro['Slope_rm_150fluoro'] >= (hightrigger_fluoro*1.1), 'Fire1_1High', 'Wait'),
           np.where(merged_atn_fluoro['Slope_rm_150fluoro'] >= (hightrigger_fluoro*1.2), 'Fire1_2High', 'Wait'),
           np.where(merged_atn_fluoro['Slope_rm_150fluoro'] >= (hightrigger_fluoro), 'Fire_High', 'Wait')]
           
merged_atn_fluoro["Fire_trigger_fluoro"] = np.select(conditionsfluoro, choicesfluoro, default='Wait')

#Combine both channels
merged_atn_fluoro['combined_fire'] = np.where(merged_atn_fluoro['Fire_trigger_atn'].str.contains('ire')|merged_atn_fluoro['Fire_trigger_fluoro'].str.contains('ire'), 'Fire', 'Wait')

#Send trigger signal on "Get_down == "Fire!"
merged_atn_fluoro["Get_Down"] = np.where(
    (merged_atn_fluoro['combined_fire'] != 'Wait').rolling(window=48, min_periods=1).sum()==1,
    np.where((merged_atn_fluoro['combined_fire'] != 'Wait'), 'Fire!', 'Wait'),
    np.where(((merged_atn_fluoro['combined_fire'] == 'Wait').rolling(336, min_periods=2).sum()==0), 'Fire!', 'Wait'),
)

bins = [-1, 0, 5, 12, 19, 23]
labels = ['background', 'peak', 'background', 'peak', 'background']

merged_atn_fluoro['Category'] = pd.cut(merged_atn_fluoro['hour'], bins=bins, labels=labels, ordered=False)

####################Now find daily average and add whether the day triggered, also find avg for peak and offpeak hours
merged_atn_fluoro['date_only'] = merged_atn_fluoro['rounded_hour'].dt.date

daily_mean_atn_fluoro = (
    merged_atn_fluoro.groupby(merged_atn_fluoro['date_only'])
      .mean(numeric_only=True)
      .rename(columns=lambda col: f"{col}_daily_mean")
      .reset_index()
)

#And for categories
bins = [-1, 0, 5, 12, 19, 23]
labels = ['background', 'peak', 'background', 'peak', 'background']

merged_atn_fluoro['Category'] = pd.cut(merged_atn_fluoro['hour'], bins=bins, labels=labels, ordered=False)

daily_by_cat = merged_atn_fluoro.groupby(['date_only', 'Category'])[["atn", "POCmodel_mg", "Fluorescense_690", 'rm150atn', 'rm150Fluorescense_690', "fluoro_per_POCmg"]].mean().unstack()
daily_by_cat.columns = ['_'.join(col) for col in daily_by_cat.columns]

full_hourly_daily = pd.merge(merged_atn_fluoro, daily_mean_atn_fluoro, on='date_only')
full_hourly_daily = pd.merge(full_hourly_daily, daily_by_cat, on='date_only')

full_hourly_daily['Fraction_POC_during_peak']=(full_hourly_daily['POCmodel_mg_daily_mean']-full_hourly_daily['POCmodel_mg_background'])/(full_hourly_daily['POCmodel_mg_daily_mean'])
full_hourly_daily['Fraction_fluoro_during_peak']=(full_hourly_daily['Fluorescense_690_daily_mean']-full_hourly_daily['Fluorescense_690_background'])/(full_hourly_daily['Fluorescense_690_daily_mean'])
full_hourly_daily['Fraction_atn_during_peak']=(full_hourly_daily['rm150atn_daily_mean']-full_hourly_daily['rm150atn_background'])/(full_hourly_daily['rm150atn_daily_mean'])
full_hourly_daily['Fraction_fluoroatn_during_peak']=(full_hourly_daily['fluoro_per_POCmg_daily_mean']-full_hourly_daily['fluoro_per_POCmg_background'])/(full_hourly_daily['fluoro_per_POCmg_daily_mean'])

target_string = 'Fire!'
has_string_daily = (
    full_hourly_daily['Get_Down']
    .str.contains(target_string, na=False)
    .groupby(merged_atn_fluoro['date_only'])
    .any()
    .reset_index(name='has_target_string')
)

# Step 3: Ensure dates match types and merge into your daily dataset
df_final = pd.merge(full_hourly_daily, has_string_daily, on='date_only', how='left')

# Optional: Fill NaN values with False if a daily date wasn't present in the hourly data
df_final['has_target_string'] = df_final['has_target_string'].fillna(False)

column_list = df_final.columns.tolist()
print(column_list)
# ['rounded_hour', 'atn', 'POCmodel_mg', 'rm150atn', 'Slope_rm_150atn', 'rm150POCmg', 'Slope_rm_150POCmg', 'Fluorescense_690', 
#  'rm150Fluorescense_690', 'Slope_rm_150fluoro', 'hour', 'fluoro_per_POCmg', 'TriggerHigh_atn', 'Rate_HighTrigger_atn', 'Max_HighTrigger_rate_atn',
#  'TriggerLow_atn', 'Rate_LowTrigger_atn', 'Max_LowTrigger_rate_atn', 'Fire_trigger_atn', 'TriggerHigh_fluoro', 'Rate_HighTrigger_fluoro',
#  'Max_HighTrigger_rate_fluoro', 'TriggerLow_fluoro', 'Rate_LowTrigger_fluoro', 'Max_LowTrigger_rate_fluoro', 'Fire_trigger_fluoro', 'combined_fire', 'Get_Down',
#  'Category', 'date_only', 'atn_daily_mean', 'POCmodel_mg_daily_mean', 'rm150atn_daily_mean', 'Slope_rm_150atn_daily_mean', 'rm150POCmg_daily_mean',
#  'Slope_rm_150POCmg_daily_mean', 'Fluorescense_690_daily_mean', 'rm150Fluorescense_690_daily_mean', 'Slope_rm_150fluoro_daily_mean', 'hour_daily_mean', 
#  'fluoro_per_POCmg_daily_mean', 'Rate_HighTrigger_atn_daily_mean', 'Max_HighTrigger_rate_atn_daily_mean', 'Rate_LowTrigger_atn_daily_mean', 'Max_LowTrigger_rate_atn_daily_mean',
#  'Rate_HighTrigger_fluoro_daily_mean', 'Max_HighTrigger_rate_fluoro_daily_mean', 'Rate_LowTrigger_fluoro_daily_mean', 'Max_LowTrigger_rate_fluoro_daily_mean',
#  'atn_background', 'atn_peak', 'POCmodel_mg_background', 'POCmodel_mg_peak', 'Fluorescense_690_background', 'Fluorescense_690_peak', 'rm150atn_background',
#  'rm150atn_peak', 'rm150Fluorescense_690_background', 'rm150Fluorescense_690_peak', 'fluoro_per_POCmg_background', 'fluoro_per_POCmg_peak', 'Fraction_POC_during_peak', 
#  'Fraction_fluoro_during_peak', 'Fraction_atn_during_peak', 'Fraction_fluoroatn_during_peak','has_target_string']

plt.plot= (df_final['rounded_hour'], df_final['fluoro_per_POCmg'])




palette =["#3776ab", "#FF8C00"]
custom_sizes = {"Wait": 50, "Fire!": 400}

# 3. Create the first scatterplot (Top)
fig, axes = plt.subplots(2, 1, figsize=(10, 10))

# 2. Plot on the first axis (axes[0])
sns.scatterplot(data=df_final, x='rounded_hour', y='rm150atn', hue='Get_Down',size="Get_Down",sizes=custom_sizes, ax=axes[0], alpha = 0.3)

# 3. Plot on the second axis (axes[1])
sns.scatterplot(data=df_final, x='rounded_hour', y='rm150Fluorescense_690', hue='Get_Down',size="Get_Down",sizes=custom_sizes,  ax=axes[1], alpha = 0.3)






custom_sizes = {"Wait": 50, "Fire!": 400}

# 3. Create the first scatterplot (Top)
fig, axes = plt.subplots(2, 1, figsize=(10, 10))

# 2. Plot on the first axis (axes[0])
sns.scatterplot(data=df_final, x='rounded_hour', y='rm150atn', hue='Fraction_fluoroatn_during_peak',size="Get_Down",sizes=custom_sizes, ax=axes[0], alpha = 0.3)

# 3. Plot on the second axis (axes[1])
sns.scatterplot(data=df_final, x='rounded_hour', y='rm150Fluorescense_690', hue='Fraction_fluoroatn_during_peak',size="Get_Down",sizes=custom_sizes,  ax=axes[1], alpha = 0.3)





import matplotlib.colors as mcolors
norm = mcolors.TwoSlopeNorm(
    vcenter=0.0, vmin=df_final["Fraction_POC_during_peak"].min(), vmax=df_final["Fraction_POC_during_peak"].max()
)

sm = plt.cm.ScalarMappable(cmap="vlag", norm=norm)



custom_sizes = {"Wait": 50, "Fire!": 400}
fig, axes = plt.subplots(3, 1, figsize=(10, 10), sharex=True)

# 2. Plot on the first axis (axes[0])
sns.scatterplot(data=df_final, x='rounded_hour', y='POCmodel_mg', hue='Fraction_POC_during_peak',palette="vlag",hue_norm=norm, size="Get_Down",sizes=custom_sizes, legend=False,  ax=axes[0])
sns.lineplot(data=df_final, x='date_only', y='POCmodel_mg', ax=axes[0], alpha = 0.3)

# 3. Plot on the second axis (axes[1])
sns.scatterplot(data=df_final, x='rounded_hour', y='Fluorescense_690', hue='Fraction_POC_during_peak',palette="vlag",hue_norm=norm,size="Get_Down",sizes=custom_sizes,  legend=False,  ax=axes[1])
sns.lineplot(data=df_final, x='date_only', y='Fluorescense_690', ax=axes[1], alpha = 0.3)

# 3. Plot on the second axis (axes[1])
sns.scatterplot(data=df_final, x='rounded_hour', y='fluoro_per_POCmg', hue='Fraction_POC_during_peak',palette="vlag",hue_norm=norm,size="Get_Down",sizes=custom_sizes,  legend=False,  ax=axes[2])
sns.lineplot(data=df_final, x='date_only', y='fluoro_per_POCmg_background', ax=axes[2], alpha = 0.3)
#axes[2].set_ylim(-10, 60) 
axes[2].set_yscale("log") 

# Adjust layout to prevent overlap
plt.tight_layout()
plt.show()

















y_columns = ["atn", "POCmodel_mg", "Fluorescense_690", "fluoro_per_POCmg",'Slope_rm_150POCmg','Slope_rm_150fluoro']

# 3. Define the column to use as the X-axis
x_column = "hour"

# 4. Loop through the list and generate a separate box plot for each
for col in y_columns:
    plt.figure(figsize=(6, 4))
    sns.boxplot(data=merged_atn_fluoro, x=x_column, y=col,showfliers=False)

    plt.title(f"Box Plot of {col} by {x_column}")
    plt.tight_layout()
    plt.show()
    
    
    
    
# #An illustration of the calculation of flux attributable to twice daily peaks using carbon flux measures from 29 Jun 2023. 
# Daily carbon flux was calculated for each day as the average of the converted values from 20-minute optical measurements, the upper horizontal dashed line. 
# Twelve hours of data each day, shaded in gray, defined the background between the peaks and was averaged to estimate the background level of flux, illustrated by the lower horizontal line. 
# The flux accounted for by the elevation in flux during the observed twice-daily peaks was calculated as the difference between these two values

###MAKE SURE THESE ARE THE RIGHT AMOUNTS
merged_atn_fluoro['date_only'] = merged_atn_fluoro['rounded_hour'].dt.date

bins = [-1, 0, 5, 12, 19, 23]
labels = ['background', 'peak', 'background', 'peak', 'background']

merged_atn_fluoro['Category'] = pd.cut(merged_atn_fluoro['hour'], bins=bins, labels=labels, ordered=False)

daily_merged_atn_fluoro_cat = merged_atn_fluoro.groupby(['date_only', 'Category'])[["atn", "POCmodel_mg", 'rm150atn', "Fluorescense_690", "fluoro_per_POCmg",'Slope_rm_150POCmg','Slope_rm_150fluoro']].mean().unstack()
daily_merged_atn_fluoro_cat.columns = ['_'.join(col) for col in daily_merged_atn_fluoro_cat.columns]

# 3. Find the average for all totals (overall mean of the categories)
dailyOnly_merged_atn_fluoro = merged_atn_fluoro.groupby(['date_only'])[["atn", "POCmodel_mg", "Fluorescense_690", 'rm150Fluorescense_690', "fluoro_per_POCmg",'Slope_rm_150POCmg','Slope_rm_150fluoro']].mean()

DailyMerged= pd.merge(daily_merged_atn_fluoro_cat, dailyOnly_merged_atn_fluoro, on='date_only')
column_list = DailyMerged.columns.tolist()
print(column_list)
#['atn_background', 'atn_peak', 'POCmodel_mg_background', 
# 'POCmodel_mg_peak', 'Fluorescense_690_background', 
# 'Fluorescense_690_peak', 'fluoro_per_POCmg_background', 
# 'fluoro_per_POCmg_peak', 'Slope_rm_150POCmg_background', 
# 'Slope_rm_150POCmg_peak', 'Slope_rm_150fluoro_background', 
# 'Slope_rm_150fluoro_peak', 'atn', 'POCmodel_mg', 'Fluorescense_690', 
# 'fluoro_per_POCmg', 'Slope_rm_150POCmg', 'Slope_rm_150fluoro']

#From fig S4 in DVM paper
DailyMerged['Fraction_POC_during_peak']=(DailyMerged['POCmodel_mg']-DailyMerged['POCmodel_mg_background'])/(DailyMerged['POCmodel_mg'])
DailyMerged['Fraction_fluoro_during_peak']=(DailyMerged['Fluorescense_690']-DailyMerged['Fluorescense_690_background'])/(DailyMerged['Fluorescense_690'])
DailyMerged['Fraction_atn_during_peak']=(DailyMerged['Slope_rm_150POCmg']-DailyMerged['Slope_rm_150POCmg_background'])/(DailyMerged['Slope_rm_150POCmg'])
DailyPlotting = DailyMerged.reset_index()

plt.plot(DailyPlotting['date_only'], DailyPlotting['Fraction_POC_during_peak'], color='blue', linestyle='-', linewidth=2, marker='o')

plt.plot(DailyPlotting['date_only'], DailyPlotting['Fraction_fluoro_during_peak'], color='blue', linestyle='-', linewidth=2, marker='o')

plt.plot(DailyPlotting['date_only'], DailyPlotting['Fraction_atn_during_peak'], color='blue', linestyle='-', linewidth=2, marker='o')

DVMscorebins = [-30, 0, 0.1, 0.2, 0.3, 1]
DVMscorelabels = ['POC_non_DVM', 'POC_low_DVM', 'POC_mid_DVM', 'POC_high_DVM', 'POC_v_high_DVM']

DailyPlotting["DVM_score"] = pd.cut(DailyPlotting['Fraction_POC_during_peak'], bins=DVMscorebins, labels=DVMscorelabels, ordered=False)

sns.scatterplot(data=DailyPlotting, x="date_only", y="POCmodel_mg", hue="DVM_score")
plt.show()


limit = DailyPlotting["Fraction_POC_during_peak"].abs().max()

# 2. Plot directly from the DataFrame
DailyPlotting.plot.scatter(
    x="date_only",
    y="POCmodel_mg",
    c="Fraction_POC_during_peak",
    cmap="coolwarm",
    vmin=-limit,
    vmax=limit,
    colorbar=True,
)

# 3. Calculate and save the daily mean and std with your custom suffixes
merged_atn_fluoro['POCmodel_mg_mean'] = merged_atn_fluoro.groupby('date_only')['POCmodel_mg'].transform('mean')
merged_atn_fluoro['POCmodel_mg_std'] = merged_atn_fluoro.groupby('date_only')['POCmodel_mg'].transform('std')

# 4. Apply the z-score formula using the new suffix columns
merged_atn_fluoro['POCmodel_mghourly_daily_z_score'] = (merged_atn_fluoro['POCmodel_mg'] - merged_atn_fluoro['POCmodel_mg_mean']) / merged_atn_fluoro['POCmodel_mg_std']
merged_atn_fluoro['plot_order'] = np.where((merged_atn_fluoro['hour'] >= 5) & (merged_atn_fluoro['hour'] <= 23), 
                                    merged_atn_fluoro['hour'] - 4, 
                                    merged_atn_fluoro['hour'] + 19)


sns.boxplot(data=merged_atn_fluoro, x='hour', y='POCmodel_mghourly_daily_z_score',showfliers=False)


# 1. Clear any active plots to prevent axis caching
plt.clf()

# 2. Extract the hours in the exact sequence dictated by plot_order
# This drops duplicate rows and sorts cleanly by your rank column
ordered_hours = (
    merged_atn_fluoro.drop_duplicates(subset=["hour"])
    .sort_values(by="plot_order")["hour"]
    .tolist()
)

# 3. Create a plotting dataframe and lock in the sorting order
plot_df = merged_atn_fluoro.copy()
plot_df["hour"] = pd.Categorical(
    plot_df["hour"], 
    categories=ordered_hours, 
    ordered=True
)

# 4. Create the base violin plot
ax = sns.violinplot(
    data=plot_df, 
    x="hour", 
    y="POCmodel_mg", 
    linewidth=0, 
    inner=None,
    color="lightgray"
)

# 5. Overlay a line plot showing the MEAN (Average) without dots
sns.pointplot(
    data=plot_df, 
    x="hour", 
    y="POCmodel_mg", 
    estimator=np.mean, 
    errorbar=None,      
    color="black", 
    linewidth=2,
    markers=""          
)

# 6. Overlay individual DOTS showing the MEDIAN
sns.pointplot(
    data=plot_df, 
    x="hour", 
    y="POCmodel_mg", 
    estimator=np.median, 
    errorbar=None, 
    color="black", 
    linestyles="none",  
    markers="o", 
    markersize=2
)

plt.ylim(bottom=0, top=500)
plt.show()


#Now add triggering

lowtrigger_atn = 0.007 
hightrigger_atn = 0.011
hightrigger_fluoro= 2.4
lowtrigger_fluoro= 1.8  
use_min_period = 24
nrec=150

# Trigger code


#atn trigger base
merged_atn_fluoro["TriggerHigh_atn"] = np.where(merged_atn_fluoro['Slope_rm_150atn'] >= hightrigger_atn, 'Trigger', 'Wait')
merged_atn_fluoro['Rate_HighTrigger_atn'] = (merged_atn_fluoro['TriggerHigh_atn'] == 'Trigger').rolling(144, min_periods=2).sum()
merged_atn_fluoro['Max_HighTrigger_rate_atn'] =  merged_atn_fluoro['Rate_HighTrigger_atn'].cummax()

merged_atn_fluoro["TriggerLow_atn"] = np.where(merged_atn_fluoro['Slope_rm_150atn'] >= lowtrigger_atn, 'Trigger', 'Wait')
merged_atn_fluoro['Rate_LowTrigger_atn'] = (merged_atn_fluoro['TriggerLow_atn'] == 'Trigger').rolling(144, min_periods=2).sum()
merged_atn_fluoro['Max_LowTrigger_rate_atn'] =  merged_atn_fluoro['Rate_LowTrigger_atn'].cummax()

conditionsatn = [
    (merged_atn_fluoro['Rate_LowTrigger_atn']  <= 1), #fire one low, then switch to high trigger
    (merged_atn_fluoro['Rate_HighTrigger_atn'] <= 1), #fire two high, then switch to higher trigger
    (merged_atn_fluoro['Rate_HighTrigger_atn'] ==3), #fire more 3 highs then even higher trigger
    (merged_atn_fluoro['Max_HighTrigger_rate_atn'] >= 4),#fire four or more highs, then use max trigger
    ((merged_atn_fluoro['Rate_HighTrigger_atn']).rolling(window=240, min_periods=1).sum()==0), #more than 1.5 week since last trigger, use the lower gear
    ]


# Define corresponding choices (results)
choicesatn = [np.where(merged_atn_fluoro['Slope_rm_150atn'] >= lowtrigger_atn, 'FireLow', 'Wait'), 
           np.where(merged_atn_fluoro['Slope_rm_150atn'] >= hightrigger_atn, 'FireHigh', 'Wait'), 
           np.where(merged_atn_fluoro['Slope_rm_150atn'] >= (hightrigger_atn*1.1), 'Fire1_1High', 'Wait'),
           np.where(merged_atn_fluoro['Slope_rm_150atn'] >= (hightrigger_atn*1.2), 'Fire1_2High', 'Wait'),
           np.where(merged_atn_fluoro['Slope_rm_150atn'] >= (hightrigger_atn), 'Fire_High', 'Wait')]
           
merged_atn_fluoro["Fire_trigger_atn"] = np.select(conditionsatn, choicesatn, default='Wait')

#fluoro trigger base
merged_atn_fluoro["TriggerHigh_fluoro"] = np.where(merged_atn_fluoro['Slope_rm_150fluoro'] >= hightrigger_fluoro, 'Trigger', 'Wait')
merged_atn_fluoro['Rate_HighTrigger_fluoro'] = (merged_atn_fluoro['TriggerHigh_fluoro'] == 'Trigger').rolling(144, min_periods=2).sum()
merged_atn_fluoro['Max_HighTrigger_rate_fluoro'] =  merged_atn_fluoro['Rate_HighTrigger_fluoro'].cummax()

merged_atn_fluoro["TriggerLow_fluoro"] = np.where(merged_atn_fluoro['Slope_rm_150fluoro'] >= lowtrigger_fluoro, 'Trigger', 'Wait')
merged_atn_fluoro['Rate_LowTrigger_fluoro'] = (merged_atn_fluoro['TriggerLow_fluoro'] == 'Trigger').rolling(144, min_periods=2).sum()
merged_atn_fluoro['Max_LowTrigger_rate_fluoro'] =  merged_atn_fluoro['Rate_LowTrigger_fluoro'].cummax()


conditionsfluoro = [
    (merged_atn_fluoro['Rate_LowTrigger_fluoro']  <= 1), #fire one low, then switch to high trigger
    (merged_atn_fluoro['Rate_HighTrigger_fluoro'] <= 2), #fire two high, then switch to higher trigger
    (merged_atn_fluoro['Rate_HighTrigger_fluoro'] ==3), #fire more 3 highs then even higher trigger
    (merged_atn_fluoro['Max_HighTrigger_rate_fluoro'] >= 4),#fire four or more highs, then use max trigger
    ((merged_atn_fluoro['Rate_HighTrigger_fluoro']).rolling(window=240, min_periods=1).sum()==0), #more than 1.5 week since last trigger, use the lower gear
    ]


# Define corresponding choices (results)
choicesfluoro = [np.where(merged_atn_fluoro['Slope_rm_150fluoro'] >= lowtrigger_fluoro, 'FireLow', 'Wait'), 
           np.where(merged_atn_fluoro['Slope_rm_150fluoro'] >= hightrigger_fluoro, 'FireHigh', 'Wait'), 
           np.where(merged_atn_fluoro['Slope_rm_150fluoro'] >= (hightrigger_fluoro*1.1), 'Fire1_1High', 'Wait'),
           np.where(merged_atn_fluoro['Slope_rm_150fluoro'] >= (hightrigger_fluoro*1.2), 'Fire1_2High', 'Wait'),
           np.where(merged_atn_fluoro['Slope_rm_150fluoro'] >= (hightrigger_fluoro), 'Fire_High', 'Wait')]
           
merged_atn_fluoro["Fire_trigger_fluoro"] = np.select(conditionsfluoro, choicesfluoro, default='Wait')

#Combine both channels
merged_atn_fluoro['combined_fire'] = np.where(merged_atn_fluoro['Fire_trigger_atn'].str.contains('ire')|merged_atn_fluoro['Fire_trigger_fluoro'].str.contains('ire'), 'Fire', 'Wait')

#Send trigger signal on "Get_down == "Fire!"
merged_atn_fluoro["Get_Down"] = np.where(
    (merged_atn_fluoro['combined_fire'] != 'Wait').rolling(window=48, min_periods=1).sum()==1,
    np.where((merged_atn_fluoro['combined_fire'] != 'Wait'), 'Fire!', 'Wait'),
    np.where(((merged_atn_fluoro['combined_fire'] == 'Wait').rolling(336, min_periods=2).sum()==0), 'Fire!', 'Wait'),
)

#save results
merged_atn_fluoro.to_csv('//Thalassa/ProjectLibrary/902305_Event_Detection_with_SES/Analyses/2026 Mooring/merged_atn_fluoroTriggers.csv')


palette =["#3776ab", "#FF8C00"]
custom_sizes = {"Wait": 50, "Fire!": 400}
fig, axes = plt.subplots(3, 1, figsize=(8, 10), sharex=True)

# 3. Create the first scatterplot (Top)
fig, axes = plt.subplots(2, 1, figsize=(10, 10))

# 2. Plot on the first axis (axes[0])
sns.scatterplot(data=merged_atn_fluoro, x='rounded_hour', y='rm150atn', hue='Get_Down',size="Get_Down",sizes=custom_sizes, ax=axes[0], alpha = 0.3)

# 3. Plot on the second axis (axes[1])
sns.scatterplot(data=merged_atn_fluoro, x='rounded_hour', y='rm150Fluorescense_690', hue='Get_Down',size="Get_Down",sizes=custom_sizes,  ax=axes[1], alpha = 0.3)



# Adjust layout to prevent overlap
plt.tight_layout()
plt.show()

#bring in the fraction of POC during peak from daily and then plot that color onto this
column_list = DailyPlotting.columns.tolist()
print(column_list)

# ['date_only', 'atn_background', 'atn_peak', 'POCmodel_mg_background', 'POCmodel_mg_peak', '
#  Fluorescense_690_background', 'Fluorescense_690_peak', 'fluoro_per_POCmg_background', 
#  'fluoro_per_POCmg_peak', 'Slope_rm_150POCmg_background', 'Slope_rm_150POCmg_peak', 
#  'Slope_rm_150fluoro_background', 'Slope_rm_150fluoro_peak', 'atn', 'POCmodel_mg', 'Fluorescense_690', 
#  'fluoro_per_POCmg', 'Slope_rm_150POCmg', 'Slope_rm_150fluoro', 'Fraction_POC_during_peak', 
#  'Fraction_fluoro_during_peak', 'Fraction_atn_during_peak', 'DVM_score']
dailytomerge = DailyPlotting[['date_only','Fraction_POC_during_peak', 'Fraction_fluoro_during_peak']]


a = pd.merge(dailytomerge, merged_atn_fluoro, on='date_only')
a.to_csv('//Thalassa/ProjectLibrary/902305_Event_Detection_with_SES/Analyses/2026 Mooring/merged_atn_fluoroTriggers_a.csv')



target_string = 'Fire!'
has_string_daily = (
    merged_atn_fluoro['Get_Down']
    .str.contains(target_string, na=False)
    .groupby(merged_atn_fluoro['date_only'])
    .any()
    .reset_index(name='has_target_string')
)

# Step 3: Ensure dates match types and merge into your daily dataset
df_final = pd.merge(DailyPlotting, has_string_daily, on='date_only', how='left')

# Optional: Fill NaN values with False if a daily date wasn't present in the hourly data
df_final['has_target_string'] = df_final['has_target_string'].fillna(False)



limit = df_final["Fraction_POC_during_peak"].abs().max()

# 2. Plot directly from the DataFrame
df_final.plot.scatter(
    x="date_only",
    y="POCmodel_mg",
    c="Fraction_POC_during_peak",
    cmap="coolwarm",
    vmin=-limit,
    vmax=limit,
    colorbar=True,
)



import matplotlib.colors as mcolors
norm = mcolors.TwoSlopeNorm(
    vcenter=0.0, vmin=df_final["Fraction_POC_during_peak"].min(), vmax=df_final["Fraction_POC_during_peak"].max()
)

sm = plt.cm.ScalarMappable(cmap="vlag", norm=norm)


custom_sizes = {np.False_: 50, np.True_: 400}
fig, axes = plt.subplots(3, 1, figsize=(8, 10), sharex=True)

# 3. Create the first scatterplot (Top)
fig, axes = plt.subplots(2, 1, figsize=(10, 10))

# 2. Plot on the first axis (axes[0])
sns.scatterplot(data=df_final, x='date_only', y='POCmodel_mg', hue='Fraction_POC_during_peak',palette="vlag",hue_norm=norm, size="has_target_string",sizes=custom_sizes, legend=False,  ax=axes[0])
sns.lineplot(data=df_final, x='date_only', y='POCmodel_mg', ax=axes[0], alpha = 0.3)
cbar = ax.figure.colorbar(sm, ax=axes[0])
cbar.set_label("(daily avg POC-background)/peak POC", rotation=270, labelpad=15)

# 3. Plot on the second axis (axes[1])
sns.scatterplot(data=df_final, x='date_only', y='Fluorescense_690', hue='Fraction_POC_during_peak',palette="vlag",hue_norm=norm,size="has_target_string",sizes=custom_sizes,  legend=False,  ax=axes[1])

sns.lineplot(data=df_final, x='date_only', y='Fluorescense_690',ax=axes[1], alpha = 0.3)
cbar = ax.figure.colorbar(sm, ax=axes[1])
cbar.set_label("(daily avg POC-background)/peak POC", rotation=270, labelpad=15)

# Adjust layout to prevent overlap
plt.tight_layout()
plt.show()


import matplotlib.colors as mcolors
norm = mcolors.TwoSlopeNorm(
    vcenter=0.0, vmin=df_final["Fraction_POC_during_peak"].min(), vmax=df_final["Fraction_POC_during_peak"].max()
)

sm = plt.cm.ScalarMappable(cmap="vlag", norm=norm)


custom_sizes = {np.False_: 50, np.True_: 400}
fig, axes = plt.subplots(3, 1, figsize=(8, 10), sharex=True)

# 3. Create the first scatterplot (Top)
fig, axes = plt.subplots(2, 1, figsize=(10, 10))

# 2. Plot on the first axis (axes[0])
sns.scatterplot(data=df_final, x='date_only', y='rm150atn', hue='Fraction_POC_during_peak',palette="vlag",hue_norm=norm, size="has_target_string",sizes=custom_sizes, legend=False,  ax=axes[0])
sns.lineplot(data=df_final, x='date_only', y='rm150atn', ax=axes[0], alpha = 0.3)
cbar = ax.figure.colorbar(sm, ax=axes[0])
cbar.set_label("(daily avg POC-background)/peak POC", rotation=270, labelpad=15)

# 3. Plot on the second axis (axes[1])
sns.scatterplot(data=df_final, x='date_only', y='rm150Fluorescense_690', hue='Fraction_POC_during_peak',palette="vlag",hue_norm=norm,size="has_target_string",sizes=custom_sizes,  legend=False,  ax=axes[1])

sns.lineplot(data=df_final, x='date_only', y='rm150Fluorescense_690',ax=axes[1], alpha = 0.3)
cbar = ax.figure.colorbar(sm, ax=axes[1])
cbar.set_label("(daily avg POC-background)/peak POC", rotation=270, labelpad=15)

# Adjust layout to prevent overlap
plt.tight_layout()
plt.show()

column_list = df_final.columns.tolist()
print(column_list)

# ['date_only', 'atn_background', 'atn_peak', 'POCmodel_mg_background', 'POCmodel_mg_peak', 
#  'Fluorescense_690_background', 'Fluorescense_690_peak', 'fluoro_per_POCmg_background', 
#  'fluoro_per_POCmg_peak', 'Slope_rm_150POCmg_background', 'Slope_rm_150POCmg_peak', 'Slope_rm_150fluoro_background', 
#  'Slope_rm_150fluoro_peak', 'atn', 'POCmodel_mg', 'Fluorescense_690', 'fluoro_per_POCmg', 'Slope_rm_150POCmg', 
#  'Slope_rm_150fluoro', 'Fraction_POC_during_peak', 'Fraction_fluoro_during_peak', 'Fraction_atn_during_peak', 
#  'DVM_score', 'has_target_string']