from pyjamas_core import Supermodel
from pyjamas_core.util import Input, Output, Property
from datetime import datetime, timedelta
from Models._utils.time import datetime2utc_time, utc_time2datetime
import numpy as np
from pytz import timezone
import json
from scipy.interpolate import griddata
import pandas as pd
import requests
import os

# define the model class and inherit from class "Supermodel"
class Model(Supermodel):
    # model constructor
    def __init__(self, id, name: str):
        # instantiate supermodel
        super(Model, self).__init__(id, name)

        # define inputs
        self.inputs['stock_ex_price'] = Input(name='Stock exchange price', unit='€/J', info="stock exchange price")
        self.inputs['distnet_costs'] = Input(name='Distribution network cost', unit='{-, €/J}', info="distribution network cost")
        self.inputs['service_cost'] = Input(name='Service cost', unit='€/J', info="service cost")
        self.inputs['taxes'] = Input(name='Taxes', unit='€/J', info="taxes")
        self.inputs['futures'] = Input(name='Futures', unit='s', info="Futures")

        # define outputs
        self.outputs['el_rate'] = Output(name='Electricity rate', unit='€/J', info='electricity rate')
        self.outputs['times'] = Output(name='Times', unit='s', info='Times')
        self.outputs['y_scaling'] = Output(name='Scaling of y axis', unit='', info='Scaling of y axis')
        self.outputs['y_unit'] = Output(name='Unit of y axis', unit='', info='Unit of y axis')
        self.outputs['y_label'] = Output(name='y label', unit='', info='Label of y axis')

        # define properties
        ET_def = {"location": ["Baden"],
                  "border": [[-1.0, -0.5, -0.2, 0.0, 0.2, 0.5, 1.0]],
                  "weight": [[-0.3, -0.6, -0.8, 1.1, 1.3, 1.5]]}
        NT_def = {"location": ["Baden"],
                  "border": [[-1.0, -0.8, -0.5, 0.0, 0.4, 0.8, 1.0]],
                  "weight": [[-0.5, -0.6, -0.8, 1.2, 1.5, 1.8]]}
        ET_def = json.dumps(ET_def)
        NT_def = json.dumps(NT_def)
        self.properties['weight_ET'] = Property(default=ET_def, data_type=str, name='energy tiers', unit='-',
                                                info='borders and weights of energy tiers', example=ET_def)
        self.properties['weight_NT'] = Property(default=NT_def, data_type=str, name='net tiers', unit='-',
                                                info='borders and weights of net tiers', example=NT_def)
        self.properties["scaling"] = Property(default=1, data_type=float, name='Scaling factor', unit='-',
                                              info='Scaling factor for y axis', example='3.6e9')
        self.properties["y_unit"] = Property(default='€/MWh', data_type=str, name='unit of y label', unit='-',
                                             info='Unit of label for y axis', example='[€/MWh]')
        self.properties["y_labeling"] = Property(default='Price', data_type=str, name='y label', unit='-',
                                                 info='Label for y axis', example='Price [€/MWh]')

        # define persistent variables
        self.weight_ET = None
        self.weight_NT = None
        self.y_scaling = None
        self.y_unit = None
        self.y_labeling = None

    async def func_birth(self):
        pass

    async def func_amend(self, keys=[]):

        if 'weight_ET' in keys:
            weight_ET_i = self.get_property('weight_ET')
            self.weight_ET = json.loads(weight_ET_i)

        if 'weight_NT' in keys:
            weight_NT_i = self.get_property('weight_NT')
            self.weight_NT = json.loads(weight_NT_i)

        if 'scaling' in keys:
            self.y_scaling = self.get_property("scaling")

        if 'y_unit' in keys:
            self.y_unit = self.get_property("y_unit")

        if 'y_labeling' in keys:
            self.y_labeling = self.get_property("y_labeling")

    async def func_peri(self, prep_to_peri=None):

        # locations information
        loc_tiers = self.weight_ET['location']

        # read prices
        stock_prices_input = await self.get_input('stock_ex_price')

        # read distribution costs
        dn_costs_input = await self.get_input('distnet_costs')
        loc_distnet = dn_costs_input['distribution_networks']
        len_loc_distnet = len(loc_distnet)

        # DLK
        DLK_val = await self.get_input('service_cost')

        # Abgaben
        abgaben_val = await self.get_input('taxes')

        # electricity rate
        el_rate = []
        border_tiers = []
        border_val = []
        ET_val = []
        NT_val = []
        MP_val = []
        for nt in range(0, len_loc_distnet):

            # compare location of distribution network with tiers locations, in it?
            if loc_distnet[nt] in loc_tiers:
                idx = loc_tiers.index(loc_distnet[nt])
            else:           # if not in list, take default values
                idx = 0

            # distribution cost
            dist_costs = dn_costs_input['costs'][nt]

            # read and determine borders and tiers
            border_tiers_i = self.det_border_tiers(idx)

            # stock prices
            stock_prices = stock_prices_input['prices'][nt]

            el_rate_i = []
            for i_mt in range(0, len(stock_prices)):
                # stock price
                mt = stock_prices[i_mt]
                # electricity rate
                el_rate_ii = np.multiply(mt, border_tiers_i['ET_tiers']) + np.multiply(dist_costs, border_tiers_i['NT_tiers']) + DLK_val + abgaben_val
                el_rate_ii = el_rate_ii.tolist()
                el_rate_i.append(el_rate_ii)

            el_rate.append(el_rate_i)
            border_tiers.append(border_tiers_i)

            border_val_i = []
            ET_val_i = []
            NT_val_i = []
            border_i = border_tiers[nt]
            len_border_i = len(border_i["borders"])

            for ni in range(0, len_border_i):
                if ni == 0:
                    border_val_i.append(border_i["borders"][ni])
                    ET_val_i.append(border_i["ET_tiers"][ni])
                    NT_val_i.append(border_i["NT_tiers"][ni])
                if ni == (len_border_i-1):
                    border_val_i.append(border_i["borders"][ni])
                    ET_val_i.append(border_i["ET_tiers"][ni-1])
                    NT_val_i.append(border_i["NT_tiers"][ni-1])
                if ((ni>0) & (ni<(len_border_i-1))):
                    border_val_i.append(border_i["borders"][ni])
                    ET_val_i.append(border_i["ET_tiers"][ni-1])
                    NT_val_i.append(border_i["NT_tiers"][ni-1])
                    border_val_i.append(border_i["borders"][ni])
                    ET_val_i.append(border_i["ET_tiers"][ni])
                    NT_val_i.append(border_i["NT_tiers"][ni])


            border_val.append(border_val_i)
            ET_val.append(ET_val_i)
            NT_val.append(NT_val_i)

            MP_val_data = []
            for mi in range(0, el_rate_i.__len__()):
                MP_val_i = [];
                for ni in range(0, len_border_i):
                    erate_i = el_rate_i[mi]
                    if ni == 0:
                        MP_val_i.append(erate_i[ni])
                    if ni == (len_border_i - 1):
                        MP_val_i.append(erate_i[ni-1])
                    if ((ni > 0) & (ni < (len_border_i - 1))):
                        MP_val_i.append(erate_i[ni-1])
                        MP_val_i.append(erate_i[ni])

                MP_val_data.append(MP_val_i)
            MP_val.append(MP_val_data)


        tier_val = [ET_val, NT_val]
        border_lines = {"borders": border_val,
                        "tiers": ["ET Tiers", "NT Tiers"],
                        "tier_values": tier_val,
                        "prices": MP_val}

        output = {'Stao_ID': loc_distnet,
                  'values': el_rate,
                  'borders': border_tiers,
                  'border_lines': border_lines
                  }

        # set output
        self.set_output("el_rate", output)
        self.set_output("times", await self.get_input('futures'))
        self.set_output("y_scaling", self.y_scaling)
        self.set_output("y_unit", self.y_unit)
        self.set_output("y_label", self.y_labeling)

    def det_border_tiers(self, it):

        # read borders
        ET_border = self.weight_ET["border"][it]
        NT_border = self.weight_NT["border"][it]

        ET_border = np.array(ET_border)
        NT_border = np.array(NT_border)

        # merge
        borders = np.append(ET_border, NT_border)
        borders = np.unique(borders)

        # read tiers
        ET_tiers_orig = self.weight_ET["weight"][it]
        NT_tiers_orig = self.weight_NT["weight"][it]

        # create tiers corresponding to border
        ind_ET = 0
        ind_NT = 0
        ET_tiers = np.array(ET_tiers_orig[ind_ET])
        NT_tiers = np.array(NT_tiers_orig[ind_NT])
        for it in range(1, len(borders) - 1):

            # ET
            if ET_border[ind_ET+1] <= borders[it]:
                ind_ET = ind_ET + 1
                ET_tiers = np.append(ET_tiers, ET_tiers_orig[ind_ET])
            else:
                ET_tiers = np.append(ET_tiers, ET_tiers_orig[ind_ET])

            # NT
            if NT_border[ind_NT+1] <= borders[it]:
                ind_NT = ind_NT + 1
                NT_tiers = np.append(NT_tiers, NT_tiers_orig[ind_NT])
            else:
                NT_tiers = np.append(NT_tiers, NT_tiers_orig[ind_NT])

            #print(it)

        # return dict
        border_tiers = {'borders': borders.tolist(),
                        'ET_tiers': ET_tiers.tolist(),
                        'NT_tiers': NT_tiers.tolist()}

        return border_tiers

if __name__ == "__main__":

    # input
    stock_ex_price = {'distribution_networks': ['Baden', 'Brugg'],
                      'prices': [[1, 2, 3], [1.1, 2.2, 3.3]]}
    distnet_costs = {'distribution_networks': stock_ex_price['distribution_networks'],
                     'costs': [100, 111]}
    DLK = [0.5]
    abgaben = [0.25]

    # properties
    ET = {"location": ['Baden', 'Brugg'],
          "border": [[-1., -0.8, -0.3, 0., 0.3, 0.8, 1.], [-1., -0.85, -0.35, 0., 0.35, 0.85, 1.]],
          "weight": [[-2., -1.25, -0.75, 0.75, 1.25, 2.], [-2., -1.3, -0.8, 0.8, 1.3, 2.]]}
    NT = {"location": ['Baden', 'Brugg'],
          "border": [[-1., -0.7, -0.4, 0., 0.4, 0.7, 1.], [-1., -0.75, -0.45, 0., 0.45, 0.75, 1.]],
          "weight": [[-1.75, -1., -0.5, 0.5, 1., 1.75], [-1.8, -1.05, -0.55, 0.55, 1.05, 1.8]]}
    ET = json.dumps(ET)
    NT = json.dumps(NT)

    inputs = {'stock_ex_price': stock_ex_price,
              'distnet_costs': distnet_costs,
              'service_cost': DLK,
              'taxes': abgaben}
    props = {'weight_ET': ET,
             'weight_NT': NT}

    outputs = Model.test(inputs, props)
