## Wave transmission through a Breakwater
# Author: AJMR
# December 14, 2021

# %%  Setup Project
from mikeio import Dfs0, Dfs1
from mikeio.eum import ItemInfo, EUMUnit, EUMType

import pandas as pd
import pathlib as pl
import numpy as np
import geopandas as gp
import math
import matplotlib.pyplot as plt
import contextily as ctx

# %% Read Model Log

pth = pl.Path("//srv-mad3.baird.com/", "Projects", "13632.101 South Shore Breakwater", "06_Models",
              "13632.101.W.zzAJMR.Rev0_ModelLog.xlsx")
runLog = pd.read_excel(pth.as_posix(), "ModelLog")

# %% Read in Point Sh
breakwaterPTS = gp.read_file("//srv-mad3.baird.com/Projects/"
                             "13632.101 South Shore Breakwater/08_CADD/Outgoing/"
                             "20211211_Toe Extents (to Alexander)/"
                             "20211211_Toe_Extents_NAD83_WISStatePlaneSZn_USFt_Lines_OffshoreClipSimple_10m_vertexUTM.shp")

# %% Read in Breakwater crest
breakCrest = pd.read_csv(
    '//srv-mad3.baird.com/Projects/13632.101 South Shore Breakwater/08_CADD/Outgoing/20211214_Crest Points (to Alexander)/20211214_Crest_Points_m.csv',
    header=None, names=['x', 'y', 'z'])
breakCrest_gdf = gp.GeoDataFrame(breakCrest, crs='EPSG:32154',
                                 geometry=gp.points_from_xy(breakCrest.x, breakCrest.y))
breakCrest_gdf.to_crs('EPSG:32616', inplace=True)

breakwaterPTS_Crest = breakwaterPTS.sjoin_nearest(breakCrest_gdf)
breakwaterPTS_Crest.rename(columns={'x': 'breakCrest_x', 'y': 'breakCrest_y', 'z': 'Crest'}, inplace=True)

# %% Read Model Results
modelPlot = [8]

for i in modelPlot:
    dfsIN = Dfs0(pl.Path(runLog['Run Location'][i],
                         runLog['Run Name'][i] + '.sw - Result Files', 'BreakPts.dfs0').as_posix())

    dfsIN_read = dfsIN.read()

    MIKEds = dfsIN_read.to_dataframe()

    # %% Merge with dataframe
    breakwaterPTS_times = None

    for t in range(0, MIKEds.shape[0]):
        breakwaterPTS_merge = None
        breakwaterPTS_merge = breakwaterPTS_Crest

        for i in range(0, MIKEds.shape[1]):
            paramName = MIKEds.columns.values[i][MIKEds.columns.values[i].find('"', 5, -1) + 3:]
            if paramName not in breakwaterPTS_merge.columns:
                breakwaterPTS_merge[paramName] = np.full([breakwaterPTS_merge.shape[0], 1], np.nan)
                # MIKEds_dict[paramName]   = []
                # MIKEid_dict[paramName]   = []
            tmpFID = int(MIKEds.columns.values[i][1:MIKEds.columns.values[i].find('"', 1)])
            tmpIND = int(MIKEds.columns.values[i][5:MIKEds.columns.values[i].find('"', 5)])

            breakwaterPTS_merge.loc[((breakwaterPTS_merge.FID == tmpFID) &
                                     (breakwaterPTS_merge.vertex_ind == tmpIND)),
                                    paramName] = MIKEds.iloc[t, i]

        breakwaterPTS_merge['Time'] = MIKEds.index[t]

        breakwaterPTS_times = pd.concat([breakwaterPTS_times, breakwaterPTS_merge], ignore_index=True)

    breakwaterPTS_times.rename(columns={'index_right': 'index_right_old'}, inplace=True)

    # %% Conversion
    breakwaterPTS_times['transmitted'] = np.full([breakwaterPTS_times.shape[0], 1], np.nan)
    gamma = 1.2

    transmit_paramA = 0.81 * gamma
    transmit_paramB = 0.45 * gamma - 0.3 * gamma * \
                      (breakwaterPTS_times['Crest'] - breakwaterPTS_times['Surface elevation']) / \
                      breakwaterPTS_times['Sign. Wave Height']

    # Identify different overtopping regimes
    breakMask_A = (breakwaterPTS_times['Crest'] - breakwaterPTS_times['Surface elevation']) / \
                  breakwaterPTS_times['Sign. Wave Height'] >= 1.2
    breakMask_B = (((breakwaterPTS_times['Crest'] - breakwaterPTS_times['Surface elevation']) /
                    breakwaterPTS_times['Sign. Wave Height'] < 1.2) &
                   (transmit_paramA < transmit_paramB))
    breakMask_C = (((breakwaterPTS_times['Crest'] - breakwaterPTS_times['Surface elevation']) /
                    breakwaterPTS_times['Sign. Wave Height'] < 1.2) &
                   (transmit_paramA > transmit_paramB))

    # breakMask_B = (breakwaterPTS_times['Crest']-breakwaterPTS_times['Surface elevation'])/ \
    #                                            breakwaterPTS_times['Sign. Wave Height']<=-1.2

    # breakMask_C = (((breakwaterPTS_times['Crest']-breakwaterPTS_times['Surface elevation'])/
    #                                            breakwaterPTS_times['Sign. Wave Height']>=-1.2) &
    #               ((breakwaterPTS_times['Crest']-breakwaterPTS_times['Surface elevation'])/
    #                                            breakwaterPTS_times['Sign. Wave Height']<1.2))


    # breakwaterPTS_times['transmitted'][breakMask_A] = breakwaterPTS_times['Sign. Wave Height'][breakMask_A]* 0.1
    # breakwaterPTS_times['transmitted'][breakMask_B] = breakwaterPTS_times['Sign. Wave Height'][breakMask_B]* 0.82
    # breakwaterPTS_times['transmitted'][breakMask_C] = breakwaterPTS_times['Sign. Wave Height'][breakMask_C]* \
    #                                                     (0.46-0.3*(breakwaterPTS_times['Crest'][breakMask_C]
    #                                                        -breakwaterPTS_times['Surface elevation'][breakMask_C])/
    #                                                         breakwaterPTS_times['Sign. Wave Height'][breakMask_C])


    # Updated approach
    breakwaterPTS_times['transmitted'][breakMask_A] = breakwaterPTS_times['Sign. Wave Height'][breakMask_A] * 0.09 * gamma
    breakwaterPTS_times['transmitted'][breakMask_B] = breakwaterPTS_times['Sign. Wave Height'][breakMask_B] *\
                                                      transmit_paramA
    breakwaterPTS_times['transmitted'][breakMask_C] = breakwaterPTS_times['Sign. Wave Height'][breakMask_C] * \
                                                      transmit_paramB[breakMask_C]

    # %% Merge with breakwater transformed waves
    breakwaterPTS_end = breakwaterPTS_times['Time'] == breakwaterPTS_times['Time'].max()
    # innerBound_gdf = innerBound_gdf.sjoin_nearest(breakwaterPTS_times.loc[breakwaterPTS_end,:])
    # %% Read Inner Boundary Points North
    innerBoundNorth = pd.read_csv(
        '//srv-mad3.baird.com/Projects/13632.101 South Shore Breakwater/06_Models/02_Mike21SW/InnerBound_North.txt',
        delimiter='\s+', header=None, names=['x', 'y'])
    innerBoundNorth.sort_values(by=['x'], inplace=True)
    innerBoundNorth_gdf = gp.GeoDataFrame(innerBoundNorth, crs='EPSG:32616',
                                          geometry=gp.points_from_xy(innerBoundNorth.x, innerBoundNorth.y))
    innerBoundNorth_gdf = innerBoundNorth_gdf.sjoin_nearest(breakwaterPTS_times.loc[breakwaterPTS_end, :])
    # %% Read Inner Boundary Points Mid
    innerBoundMid = pd.read_csv(
        '//srv-mad3.baird.com/Projects/13632.101 South Shore Breakwater/06_Models/02_Mike21SW/InnerBound_Mid.txt',
        delimiter='\s+', header=None, names=['x', 'y'])
    innerBoundMid.sort_values(by=['x'], inplace=True)
    innerBoundMid_gdf = gp.GeoDataFrame(innerBoundMid, crs='EPSG:32616',
                                        geometry=gp.points_from_xy(innerBoundMid.x, innerBoundMid.y))
    innerBoundMid_gdf = innerBoundMid_gdf.sjoin_nearest(breakwaterPTS_times.loc[breakwaterPTS_end, :])
    # %% Read Inner Boundary Points North
    innerBoundSouth = pd.read_csv(
        '//srv-mad3.baird.com/Projects/13632.101 South Shore Breakwater/06_Models/02_Mike21SW/InnerBound_South.txt',
        delimiter='\s+', header=None, names=['x', 'y'])
    innerBoundSouth.sort_values(by=['x'], inplace=True)
    innerBoundSouth_gdf = gp.GeoDataFrame(innerBoundSouth, crs='EPSG:32616',
                                          geometry=gp.points_from_xy(innerBoundSouth.x, innerBoundSouth.y))
    innerBoundSouth_gdf = innerBoundSouth_gdf.sjoin_nearest(breakwaterPTS_times.loc[breakwaterPTS_end, :])

    # %% Save as DFS1
    boundDataNorth = pd.DataFrame(
        innerBoundNorth_gdf.loc[:, ['Time', 'transmitted', 'Peak Wave Period', 'Mean Wave Direction']])
    boundDataNorth.set_index('Time', inplace=True)
    boundDataNorth.rename(columns={'transmitted': 'Significant wave height', 'Peak Wave Period': 'Peak wave period',
                                   'Mean Wave Direction': 'Mean wave direction'},
                          inplace=True)

    dfsfilename = '//oak-spillway.baird.com/F/13632.101 South Shore Breakwater/SetupFiles/InnerBounds/' + \
                  runLog['Run Name'][modelPlot[0]] + 'ProdInnerNorth1.dfs1'

    items = [ItemInfo("Significant wave height", EUMType.Significant_wave_height, EUMUnit.meter),
             ItemInfo("Peak wave period", EUMType.Wave_period, EUMUnit.second),
             ItemInfo("Mean wave direction", EUMType.Mean_Wave_Direction, EUMUnit.degree),
             ItemInfo("Directional Standard Deviation, n", EUMType.Directional_Standard_Deviation, EUMUnit.degree)]

    dfs = Dfs1()

    dfs.write(filename=dfsfilename,
              data=[np.array((boundDataNorth['Significant wave height'], boundDataNorth['Significant wave height'])),
                    np.array((boundDataNorth['Peak wave period'], boundDataNorth['Peak wave period'])),
                    np.array((boundDataNorth['Mean wave direction'], boundDataNorth['Mean wave direction'])),
                    np.ones((2, boundDataNorth.shape[0])) * 23.28],
              start_time=breakwaterPTS_times['Time'].min(),
              dt=86400,
              datetimes=None,
              items=items,
              title=None)


    # %% Save as DFS1- Mid
    boundDataMid = pd.DataFrame(
        innerBoundMid_gdf.loc[:, ['Time', 'transmitted', 'Peak Wave Period', 'Mean Wave Direction']])
    boundDataMid.set_index('Time', inplace=True)
    boundDataMid.rename(columns={'transmitted': 'Significant wave height', 'Peak Wave Period': 'Peak wave period',
                                 'Mean Wave Direction': 'Mean wave direction'},
                        inplace=True)

    dfsfilename = '//oak-spillway.baird.com/F/13632.101 South Shore Breakwater/SetupFiles/InnerBounds/' + \
                  runLog['Run Name'][modelPlot[0]] + 'ProdInnerMid.dfs1'

    items = [ItemInfo("Significant wave height", EUMType.Significant_wave_height, EUMUnit.meter),
             ItemInfo("Peak wave period", EUMType.Wave_period, EUMUnit.second),
             ItemInfo("Mean wave direction", EUMType.Mean_Wave_Direction, EUMUnit.degree),
             ItemInfo("Directional Standard Deviation, n", EUMType.Directional_Standard_Deviation, EUMUnit.degree)]

    dfs = Dfs1()
    dfs.write(filename=dfsfilename,
              data=[np.array((boundDataMid['Significant wave height'], boundDataMid['Significant wave height'])),
                    np.array((boundDataMid['Peak wave period'], boundDataMid['Peak wave period'])),
                    np.array((boundDataMid['Mean wave direction'], boundDataMid['Mean wave direction'])),
                    np.ones((2, boundDataMid.shape[0])) * 23.28],
              start_time=breakwaterPTS_times['Time'].min(),
              dt=86400,
              items=items)

    # %% Save as DFS1- South
    boundDataSouth = pd.DataFrame(
        innerBoundSouth_gdf.loc[:, ['Time', 'transmitted', 'Peak Wave Period', 'Mean Wave Direction']])
    boundDataSouth.set_index('Time', inplace=True)
    boundDataSouth.rename(columns={'transmitted': 'Significant wave height', 'Peak Wave Period': 'Peak wave period',
                                   'Mean Wave Direction': 'Mean wave direction'},
                          inplace=True)

    dfsfilename = '//oak-spillway.baird.com/F/13632.101 South Shore Breakwater/SetupFiles/InnerBounds/' + \
                  runLog['Run Name'][modelPlot[0]] + 'ProdInnerSouth.dfs1'

    items = [ItemInfo("Significant wave height", EUMType.Significant_wave_height, EUMUnit.meter),
             ItemInfo("Peak wave period", EUMType.Wave_period, EUMUnit.second),
             ItemInfo("Mean wave direction", EUMType.Mean_Wave_Direction, EUMUnit.degree),
             ItemInfo("Directional Standard Deviation, n", EUMType.Directional_Standard_Deviation, EUMUnit.degree)]

    dfs = Dfs1()
    dfs.write(filename=dfsfilename,
              data=[np.array((boundDataSouth['Significant wave height'], boundDataSouth['Significant wave height'])),
                    np.array((boundDataSouth['Peak wave period'], boundDataSouth['Peak wave period'])),
                    np.array((boundDataSouth['Mean wave direction'], boundDataSouth['Mean wave direction'])),
                    np.ones((2, boundDataSouth.shape[0])) * 23.28],
              start_time=breakwaterPTS_times['Time'].min(),
              dt=86400,
              items=items)


# %% Test Plot
mapbox = 'https://api.mapbox.com/styles/v1/alexander0042/ckemxgtk51fgp19nybfmdcb1e/tiles/256/{z}/{x}/{y}@2x?access_token=pk.eyJ1IjoiYWxleGFuZGVyMDA0MiIsImEiOiJjazVmdG4zbncwMHY4M2VrcThwZGUzZDFhIn0.w6oDHoo1eCeRlSBpwzwVtw'

fig, axes = plt.subplots(nrows=1, ncols=1, figsize=(9, 9), constrained_layout=True)

breakwaterPTS_merge.plot(ax=axes, column='Sign. Wave Height', cmap='viridis')
# breakwaterPTS_merge.plot(ax=axes, column='Crest', cmap='viridis', legend=True)
innerBound_gdf.plot(ax=axes, column='transmitted', cmap='viridis')

ctx.add_basemap(axes, source=mapbox, crs='EPSG:32616')

plt.show()
