# Path: data_cube_utilities/plotter_utils_consts.py

# 
# Path: data_cube_utilities/scale.py
# def np_scale(arr, pop_arr=None, pop_min_max=None, pop_mean_std=None, min_max=None, scaling='norm'):
#     """
#     Scales a NumPy array with standard scaling or norm scaling, default to norm scaling.
# 
#     Parameters
#     ----------
#     arr: numpy.ndarray
#         The NumPy array to scale.
#     pop_arr: numpy.ndarray, optional
#         The NumPy array to treat as the population.
#         If specified, all members of `arr` must be within the range of `pop_arr`
#         or `min_max` must be specified.
#     pop_min_max: list-like, optional
#         The population minimum and maximum, in that order.
#         Supercedes `pop_arr` when normalizing.
#     pop_mean_std: list-like, optional
#         The population mean and standard deviation, in that order.
#         Supercedes `pop_arr` when standard scaling.
#     min_max: list-like, optional
#         The desired minimum and maximum of the final output, in that order.
#         If all values are the same, all values will become `min_max[0]`.
#     scaling: str, optional
#         The options are ['std', 'norm'].
#         The option 'std' standardizes. The option 'norm' normalizes (min-max scales).
#     """
#     if len(arr) == 0:
#         return arr
#     pop_arr = arr if pop_arr is None else pop_arr
#     if scaling == 'norm':
#         pop_min, pop_max = (pop_min_max[0], pop_min_max[1]) if pop_min_max is not None \
#             else (np.nanmin(pop_arr), np.nanmax(pop_arr))
#         numerator, denominator = arr - pop_min, pop_max - pop_min
#     elif scaling == 'std':
#         mean, std = pop_mean_std if pop_mean_std is not None else (np.nanmean(pop_arr), np.nanstd(pop_arr))
#         numerator, denominator = arr - mean, std
#     # Primary scaling
#     new_arr = arr
#     if denominator > 0:
#         new_arr = numerator / denominator
#     # Optional final scaling.
#     if min_max is not None:
#         if denominator > 0:
#             new_arr = np.interp(new_arr, (np.nanmin(new_arr), np.nanmax(new_arr)), min_max)
#         else: # The values are identical - set all values to the low end of the desired range.
#             new_arr = np.full_like(new_arr, min_max[0])
#     return new_arr
# 
# Path: data_cube_utilities/dc_time.py
# def _n64_to_datetime(n64):
#     """
#     Converts Numpy 64 bit timestamps to datetime objects. Units in seconds
#     """
#     return datetime.utcfromtimestamp(n64.tolist() / 1e9)
# 
# def _n64_datetime_to_scalar(dt64):
#     """
#     Converts a NumPy datetime64 object to the number of seconds since 
#     midnight, January 1, 1970, as a NumPy float64.
#     
#     Returns
#     -------
#     scalar: numpy.float64
#         The number of seconds since midnight, January 1, 1970, as a NumPy float64.
#     """
#     return (dt64 - np.datetime64('1970-01-01T00:00:00Z')) / np.timedelta64(1, 's')
# 
# def _scalar_to_n64_datetime(scalar):
#     """
#     Converts a floating point number to a NumPy datetime64 object.
#     
#     Returns
#     -------
#     dt64: numpy.datetime64
#         The NumPy datetime64 object representing the datetime of the scalar argument.
#     """
#     return (scalar * np.timedelta64(1, 's')) + np.datetime64('1970-01-01T00:00:00Z')


from collections import OrderedDict
from matplotlib.ticker import FuncFormatter
from matplotlib.colors import LinearSegmentedColormap
from pathlib import Path
from tqdm.auto import tqdm
from skimage.exposure import rescale_intensity
from matplotlib.animation import FuncAnimation
from .plotter_utils_consts import n_pts_smooth, default_fourier_n_harm
from .scale import np_scale
from .dc_time import _n64_to_datetime, _n64_datetime_to_scalar, _scalar_to_n64_datetime
    from scipy.interpolate import interp1d
    from scipy import stats
    from scipy.interpolate import CubicSpline
    from .curve_fitting import gaussian_fit, gaussian_filter_fit, poly_fit, fourier_fit
    from scipy.interpolate import CubicSpline
    from .curve_fitting import gaussian_fit, gaussian_filter_fit, poly_fit, fourier_fit
    from .raster_filter import lone_object_filter
    from mpl_toolkits.axes_grid1 import make_axes_locatable
import re
import copy
import numpy as np
import pandas as pd
import xarray as xr
import matplotlib as mpl
import matplotlib.patches as mpatches
import matplotlib.pyplot as plt
import time
import warnings
import matplotlib.patheffects as PathEffects
    import seaborn as sns
    import string


def month_ints_to_month_names(month_ints):
    """
    Converts ordinal numbers for months (in range [1,12]) to their 3-letter names.
    """
    return [month_names_short[i - 1] for i in month_ints]


def week_int_to_month_name(week_int):
    month_ind = np.argmax(week_int <= last_week_int_per_month)
    return month_names_short[month_ind]


def week_ints_to_month_names(week_ints):
    return [week_int_to_month_name(week_int) for week_int in week_ints]


def naive_months_ticks_by_week(week_ints=None):
    """
    Given a list of week numbers (in range [1,54]), returns a list of month strings separated by spaces.
    Covers 54 weeks if no list-like of week numbers is given.
    This is only intended to be used for labeling axes in plotting.
    """
    month_ticks_by_week = []
    week_ints = list(range(1, 55)) if week_ints is None else week_ints
    month_ticks_by_week = remove_non_unique_ordered_list_str(week_ints_to_month_names(week_ints))
    return month_ticks_by_week

def n64_to_month_and_year(n64):

# Path: data_cube_utilities/plotter_utils_consts.py

# 
# Path: data_cube_utilities/scale.py
# def np_scale(arr, pop_arr=None, pop_min_max=None, pop_mean_std=None, min_max=None, scaling='norm'):
#     """
#     Scales a NumPy array with standard scaling or norm scaling, default to norm scaling.
# 
#     Parameters
#     ----------
#     arr: numpy.ndarray
#         The NumPy array to scale.
#     pop_arr: numpy.ndarray, optional
#         The NumPy array to treat as the population.
#         If specified, all members of `arr` must be within the range of `pop_arr`
#         or `min_max` must be specified.
#     pop_min_max: list-like, optional
#         The population minimum and maximum, in that order.
#         Supercedes `pop_arr` when normalizing.
#     pop_mean_std: list-like, optional
#         The population mean and standard deviation, in that order.
#         Supercedes `pop_arr` when standard scaling.
#     min_max: list-like, optional
#         The desired minimum and maximum of the final output, in that order.
#         If all values are the same, all values will become `min_max[0]`.
#     scaling: str, optional
#         The options are ['std', 'norm'].
#         The option 'std' standardizes. The option 'norm' normalizes (min-max scales).
#     """
#     if len(arr) == 0:
#         return arr
#     pop_arr = arr if pop_arr is None else pop_arr
#     if scaling == 'norm':
#         pop_min, pop_max = (pop_min_max[0], pop_min_max[1]) if pop_min_max is not None \
#             else (np.nanmin(pop_arr), np.nanmax(pop_arr))
#         numerator, denominator = arr - pop_min, pop_max - pop_min
#     elif scaling == 'std':
#         mean, std = pop_mean_std if pop_mean_std is not None else (np.nanmean(pop_arr), np.nanstd(pop_arr))
#         numerator, denominator = arr - mean, std
#     # Primary scaling
#     new_arr = arr
#     if denominator > 0:
#         new_arr = numerator / denominator
#     # Optional final scaling.
#     if min_max is not None:
#         if denominator > 0:
#             new_arr = np.interp(new_arr, (np.nanmin(new_arr), np.nanmax(new_arr)), min_max)
#         else: # The values are identical - set all values to the low end of the desired range.
#             new_arr = np.full_like(new_arr, min_max[0])
#     return new_arr
# 
# Path: data_cube_utilities/dc_time.py
# def _n64_to_datetime(n64):
#     """
#     Converts Numpy 64 bit timestamps to datetime objects. Units in seconds
#     """
#     return datetime.utcfromtimestamp(n64.tolist() / 1e9)
# 
# def _n64_datetime_to_scalar(dt64):
#     """
#     Converts a NumPy datetime64 object to the number of seconds since 
#     midnight, January 1, 1970, as a NumPy float64.
#     
#     Returns
#     -------
#     scalar: numpy.float64
#         The number of seconds since midnight, January 1, 1970, as a NumPy float64.
#     """
#     return (dt64 - np.datetime64('1970-01-01T00:00:00Z')) / np.timedelta64(1, 's')
# 
# def _scalar_to_n64_datetime(scalar):
#     """
#     Converts a floating point number to a NumPy datetime64 object.
#     
#     Returns
#     -------
#     dt64: numpy.datetime64
#         The NumPy datetime64 object representing the datetime of the scalar argument.
#     """
#     return (scalar * np.timedelta64(1, 's')) + np.datetime64('1970-01-01T00:00:00Z')


# Path: data_cube_utilities/plotter_utils.py
from collections import OrderedDict
from matplotlib.ticker import FuncFormatter
from matplotlib.colors import LinearSegmentedColormap
from pathlib import Path
from tqdm.auto import tqdm
from skimage.exposure import rescale_intensity
from matplotlib.animation import FuncAnimation
from .plotter_utils_consts import n_pts_smooth, default_fourier_n_harm
from .scale import np_scale
from .dc_time import _n64_to_datetime, _n64_datetime_to_scalar, _scalar_to_n64_datetime
    from scipy.interpolate import interp1d
    from scipy import stats
    from scipy.interpolate import CubicSpline
    from .curve_fitting import gaussian_fit, gaussian_filter_fit, poly_fit, fourier_fit
    from scipy.interpolate import CubicSpline
    from .curve_fitting import gaussian_fit, gaussian_filter_fit, poly_fit, fourier_fit
    from .raster_filter import lone_object_filter
    from mpl_toolkits.axes_grid1 import make_axes_locatable
import re
import copy
import numpy as np
import pandas as pd
import xarray as xr
import matplotlib as mpl
import matplotlib.patches as mpatches
import matplotlib.pyplot as plt
import time
import warnings
import matplotlib.patheffects as PathEffects
    import seaborn as sns
    import string



def month_ints_to_month_names(month_ints):
    """
    Converts ordinal numbers for months (in range [1,12]) to their 3-letter names.
    """
    return [month_names_short[i - 1] for i in month_ints]


def week_int_to_month_name(week_int):
    month_ind = np.argmax(week_int <= last_week_int_per_month)
    return month_names_short[month_ind]


def week_ints_to_month_names(week_ints):
    return [week_int_to_month_name(week_int) for week_int in week_ints]


def naive_months_ticks_by_week(week_ints=None):
    """
    Given a list of week numbers (in range [1,54]), returns a list of month strings separated by spaces.
    Covers 54 weeks if no list-like of week numbers is given.
    This is only intended to be used for labeling axes in plotting.
    """
    month_ticks_by_week = []
    week_ints = list(range(1, 55)) if week_ints is None else week_ints
    month_ticks_by_week = remove_non_unique_ordered_list_str(week_ints_to_month_names(week_ints))
    return month_ticks_by_week

def n64_to_month_and_year(n64):

    datetime_val = _n64_to_datetime(n64)