"""This code defines objects and functions that are useful across the GUI.

AUTHOR        :  Brian Ha (summer intern)
MENTOR        :  Gene Massion
MBARI TEAM    :  Chemical Sensor Group
PROJECT       :  Coastal Profiling Floats (CPF)
DATE CREATED  :  2018-06-29
LAST REVISION :  2018-07-31
"""

# Python Standard Library Imports
# =============================================================================
import os
import json
import time
from datetime import datetime, timedelta


# 3rd Party Library Imports
# =============================================================================
import dash_html_components as html
import dash_core_components as dcc


# CPF MCS-Specific Imports
# =============================================================================
import gui.css as css


# Constants
# =============================================================================
from cpf_constants import CPF_IDs
from cpf_constants import JSON_DIRECTORY
from cpf_constants import MBARI_LOGO
from cpf_constants import NUM_SCANS_PER_SECOND, DESCENT_VELOCITY
from cpf_constants import ISO_TIMESTAMP_FORMAT, TIMESTAMP_FORMAT, PARK_TIME_FORMAT
from cpf_constants import SURFACE_DEPTH

# CPF Dropdown Menu Options
cpf_dropdown = []

for imei_num in CPF_IDs:
    dropdown_label = 'CPF-' + str(CPF_IDs[imei_num]).zfill(3)
    cpf_dropdown.append({'label': dropdown_label, 'value': imei_num})


# Navigation Bar for all pages
nav_bar = html.Div([
            html.Div([
                html.Ul([
                    html.Li(
                        dcc.Link('Home', href='/', style=css.nav_bar_text_style),
                        style=css.nav_bar_li_style),

                    html.Li(html.P(['|'], style={'color': 'white', 'fontSize': 20}),
                            style=css.nav_bar_li_style),

                    html.Li(
                        dcc.Link('Fleet Overview', href='/fleet_overview',
                                 style=css.nav_bar_text_style),
                        style=css.nav_bar_li_style),

                    html.Li(html.P(['|'], style={'color': 'white', 'fontSize': 20}),
                            style=css.nav_bar_li_style),

                    html.Li(
                        dcc.Link('Float Viewer', href='/float_viewer', style=css.nav_bar_text_style),
                        style=css.nav_bar_li_style),

                    html.Li(html.P(['|'], style={'color': 'white', 'fontSize': 20}),
                            style=css.nav_bar_li_style),

                    html.Li(
                        dcc.Link('Command', href='/command', style=css.nav_bar_text_style),
                        style=css.nav_bar_li_style),

                ], style={'marginBottom': 0}),
            ], style={'float': 'left', 'marginLeft': 15, 'marginTop': 10}),
            html.Div([
                html.Img(src=MBARI_LOGO, style=css.mbari_logo_style),
            ], style={'textAlign': 'right'}),
        ], style=css.nav_bar_div_style)


# Functions
# =============================================================================
def get_cpf_json_from_id(cpf_id):

    json_file_path = JSON_DIRECTORY + 'CPF_' + str(cpf_id).zfill(3) + '.json'

    with open(json_file_path, 'r') as infile:
        raw_string = infile.read()
        json_data = json.loads(raw_string)

    return json_data


def validate_int_range_input(input_value, lower_limit, upper_limit):

    try:
        input_value = int(input_value)
    except:
        return False

    if input_value < lower_limit:
        return False
    elif input_value > upper_limit:
        return False
    else:
        return True


def reconstruct_complete_pressure_history(cpf_id):
    """This function returns two arrays: a timestamp array and depth measurement array for all time for single CPF."""

    all_pressure_timestamps = []
    all_pressure_measurements = []

    # Read CPF .json file
    json_data = get_cpf_json_from_id(cpf_id)

    # Reconstruct depth history backwards in time (starting from the most recent)
    reversed_profile_end_times = list(reversed(json_data['Timestamps']))
    reversed_depth_measurements = list(reversed(json_data['Pressure_[decibar]']))
    reversed_park_times = list(reversed(json_data['Park_Time']))
    reversed_num_scans = list(reversed(json_data['Num_Scans']))

    for profile_end_time, profile_depth_measurements, park_time, num_scans_array in zip(reversed_profile_end_times,
                                                                                        reversed_depth_measurements,
                                                                                        reversed_park_times,
                                                                                        reversed_num_scans):

        # If there aren't any depth measurements for a profile, skip that profile
        if len(profile_depth_measurements) == 0:
            continue

        # Surface (End of Profile)
        profile_end_time = str(datetime.strptime(profile_end_time, ISO_TIMESTAMP_FORMAT))
        all_pressure_timestamps.append(profile_end_time)
        all_pressure_measurements.append(profile_depth_measurements[0])

        # Ascent Phase
        for depth_measurement, num_scans in zip(profile_depth_measurements[1:], num_scans_array[1:]):

            previous_timestamp = datetime.strptime(all_pressure_timestamps[-1], TIMESTAMP_FORMAT)
            time_delta = num_scans / NUM_SCANS_PER_SECOND  # [seconds]
            time_delta_object = timedelta(seconds=time_delta)
            measurement_timestamp = previous_timestamp - time_delta_object

            all_pressure_measurements.append(depth_measurement)
            all_pressure_timestamps.append(str(measurement_timestamp))

        # Park Phase
        park_depth = all_pressure_measurements[-1]
        park_time_end = datetime.strptime(all_pressure_timestamps[-1], TIMESTAMP_FORMAT)
        park_time_duration = time.strptime(park_time, PARK_TIME_FORMAT)
        time_delta_object = timedelta(hours=park_time_duration.tm_hour,
                                      minutes=park_time_duration.tm_min,
                                      seconds=park_time_duration.tm_sec)
        park_time_start = park_time_end - time_delta_object

        all_pressure_measurements.append(park_depth)  # The park phase assumes constant depth
        all_pressure_timestamps.append(str(park_time_start))

        # Descent Phase
        descent_duration = park_depth / DESCENT_VELOCITY  # [seconds]
        time_delta_object = timedelta(seconds=descent_duration)

        all_pressure_measurements.append(SURFACE_DEPTH)
        profile_start_time = park_time_start - time_delta_object
        all_pressure_timestamps.append(str(profile_start_time))

    # Reverse the arrays so that they are in chronological order
    all_pressure_measurements.reverse()
    all_pressure_timestamps.reverse()


    # Multiply by -1 so that depth measurements are negative
    all_pressure_measurements = [-1*measurement for measurement in all_pressure_measurements]

    return all_pressure_timestamps, all_pressure_measurements
