# Copyright (C) 2021-2024 Battelle Memorial Institute
# See LICENSE file at https://github.com/pnnl/tesp
# file: dso_map.py

import csv
import json

import pandas as pd
import requests

'''
This script automatically creates the 200 bus meta data files.


The following DSO specific parameters are instantiated

"bus_number"
"name"
"climate_zone" - feeder taxonomy climate zone
"latitude" 
"longitude" 
"time_zone_offset" 
"BLM_zone" TODO: add BLM Zone
"utility_type" : "Urban", "Suburban", Rural
"ownership_type" :   TODO: add ownership type to inputs
"peak_season" : "Summer",
"substation": 
"random_seed": Used to ensure each DSO is different and repeatable.
"feeders": {"feeder1" : { "name": "",  "ercot": false }},
"RCI energy mix" : {
      "residential" : ,
      "commercial" : ,
      "industrial" : },
"number_of_customers" 
"number_of_gld_homes" 
"comm_customers_per_bldg"
"number_of_substations" 
"MVA_growth_rate" 
"weather_file" 
"ashrae_zone" - ASHRAE Climate Zone
"RCI customer count mix" : {
      "residential" : ,
      "commercial" : ,
      "industrial" : },
"average_load_MW" : 
"total_load_time_series_file" : "",
"industrial_load_time_series_file" : "",
"winter_peak_MW" : 
"summer_peak_MW" : 
"capacity_limit_MW" : 
"scaling_factor" : DEPRECATE?
"substation_upgrade" : {
      "greenfield_MVA" : [
        111,
        111
      ],
      "brownfield_MVA" : [
        111,
        111
      ]
    },
    "substation_transformer_count" : 111,
    "bilaterals" : {
      "price" : 11.11,
      "load_fraction" : 0.11
    },
    "DSO_system_energy_fraction" : 0.11

'''


def prepare_metadata(node, end_row, feeder_mode, high_renewables_case, DSO_Load_Threshold):
    sheet_name = node + 'BusValues'
    if high_renewables_case:
        case_file = node + "-hi-metadata-" + feeder_mode
        config_file = node + "_hi_system_case_config"
    else:
        case_file = node + "-metadata-" + feeder_mode
        config_file = node + "_system_case_config"

    if find_county:
        out = csv.writer(open("county.csv", "w"), delimiter=',', lineterminator='\n')
    if write_industrials:
        dso_list = []
        indust_load_list = []
    use_dso_list = []

    sheet = pd.read_excel(data_path + 'bus_mapping.xlsx', engine='openpyxl', sheet_name=sheet_name)

    # os.rename(case_path + case_file + '.json', case_path + case_file + '_old.json')

    with open(data_path + 'metadata-general.json') as json_file:
        data = json.load(json_file)

        # # Clear out all existing DSOs in the file
        # for key in list(data.keys()):
        #     if 'DSO' in key:
        #         del data[key]

        # Get values from spreadsheet
        for irow in range(1, end_row):
            busid = int(sheet.loc[irow, 'Bus'])
            busname = sheet.loc[irow, 'DSO Name']
            climatezone = int(sheet.loc[irow, 'Climate Zone'])
            ashrae_zone = sheet.loc[irow, 'ASHRAE Zone']
            blm_zone = int(sheet.loc[irow, 'BLM Zone'])
            latitude = sheet.loc[irow, 'Latitude']
            if len(str(latitude)) == 2:
                latitude = format(latitude, '.1f')
            longitude = sheet.loc[irow, 'Longitude']
            utiltype = sheet.loc[irow, 'Utility Type']
            if utiltype == 'Town':
                utiltype = 'Suburban'
            ownership_type = sheet.loc[irow, 'Ownership Type']
            peakseason = sheet.loc[irow, 'Seasonal Peak (5% Thresh.)']
            county = sheet.loc[irow, 'County']
            roof_top_PV_MW = sheet.loc[irow, 'Rooftop PV Rating (MW)']

            if high_renewables_case:
                max_load = sheet.loc[irow, 'Peak Load High Renewable (MW)']
            else:
                max_load = sheet.loc[irow, 'Peak Load Moderate Renewable (MW)']
            congestion_factor = sheet.loc[irow, 'Substation Congestion Factor (-)']
            bus_simulated = sheet.loc[irow, 'Simulated']

            total_average_load = sheet.loc[irow, 'Scaled Load (Average)']
            if total_average_load == 0:
                total_average_load = 0.001
            res_average_load = sheet.loc[irow, 'Scaled Load Breakup RCI (MW)']
            comm_average_load = sheet.loc[irow, 'Unnamed: 20']
            indust_average_load = sheet.loc[irow, 'Unnamed: 21']

            res_customers = int(sheet.loc[irow, 'Scaled Load Breakup RCI (Customers)'])
            comm_customers = int(sheet.loc[irow, 'Unnamed: 26'])
            indust_customers = int(sheet.loc[irow, 'Unnamed: 27'])
            total_customers = res_customers + comm_customers + indust_customers
            # Need to have non-zero residential customers for prepare case to run
            if total_customers == 0:
                total_customers = 1
                res_customers = 1
            if write_industrials:
                dso_list.append("Bus" + str(busid))
                indust_load_list.append(round(indust_average_load, 1))

            if find_county:
                # Find county by lat and longitude
                # From https://geo.fcc.gov/api/census/
                response = requests.get("https://geo.fcc.gov/api/census/area?lat=" + str(latitude) + "&lon=" + str(longitude) + "&format=json")
                test = json.loads(response.content.decode("utf-8"))
                county = test['results'][0]['county_name']
                out.writerow([latitude, longitude, county])

            # Values for lean version
            if feeder_mode == 'lean':
            #     if utiltype == "Rural":
            #         feeders = {"feeder1": {"name": "R4-25.00-1", "ercot": False},
            #                    "feeder2": {"name": "R5-12.47-3", "ercot": False}}
            #         num_gld_homes = 2192
            #     elif utiltype == "Suburban":
            #         if climatezone == 3:
            #             feeders = {"feeder1": {"name": "R3-12.47-3", "ercot": False}}
            #             num_gld_homes = 1326
            #         if climatezone == 4:
            #             feeders = {"feeder1": {"name": "R3-12.47-3", "ercot": False},
            #                        "feeder2": {"name": "R5-12.47-3", "ercot": False}}
            #             num_gld_homes = 2865
            #         if climatezone == 5:
            #             feeders = {"feeder1": {"name": "R5-12.47-1", "ercot": False},
            #                        "feeder2": {"name": "R5-12.47-5", "ercot": False}}
            #             num_gld_homes = 2541
            #     elif utiltype == "Urban":
            #         if climatezone == 3:
            #             # Omitted for now as feeder R3-12.47-1 has large static load
            #             # feeders = {"feeder1": {"name": "R3-12.47-1", "ercot": False},
            #             #            "feeder2": {"name": "R4-12.47-1", "ercot": False}}
            #             # num_gld_homes = 980
            #             feeders = {"feeder1": {"name": "R4-12.47-1", "ercot": False},
            #                        "feeder2": {"name": "R5-12.47-1", "ercot": False}}
            #             num_gld_homes = 1525
            #         if climatezone == 4:
            #             feeders = {"feeder1": {"name": "R4-12.47-1", "ercot": False},
            #                        "feeder2": {"name": "R4-12.47-2", "ercot": False}}
            #             num_gld_homes = 893
            #         if climatezone == 5:
            #             # Omitted for now as feeder R5-12.47-4 has large static load
            #             # feeders = {"feeder1": {"name": "R5-12.47-1", "ercot": False},
            #             #            "feeder2": {"name": "R5-12.47-2", "ercot": False},
            #             #            "feeder3": {"name": "R5-12.47-4", "ercot": False}}
            #             # num_gld_homes = 2234
            #             feeders = {"feeder1": {"name": "R5-12.47-1", "ercot": False},
            #                        "feeder2": {"name": "R5-12.47-2", "ercot": False},
            #                        "feeder3": {"name": "R5-12.47-5", "ercot": False}}
            #             num_gld_homes = 2847
            #
            # # Values for lean version
            # if feeder_mode == 'test':
                if utiltype == "Rural":
                    # feeders = {"feeder1": {"name": "R4-25.00-1", "ercot": False},
                    #            "feeder2": {"name": "R5-12.47-5", "ercot": False}}
                    feeders = {"feeder1": {"name": "R5-12.47-5", "ercot": False}}
                    num_gld_homes = 1539
                elif utiltype == "Suburban":
                    feeders = {"feeder1": {"name": "R5-12.47-5", "ercot": False}}
                    num_gld_homes = 1539
                    # if climatezone == 3:
                    #     feeders = {"feeder1": {"name": "R3-12.47-3", "ercot": False}}
                    #     num_gld_homes = 1326
                    # if climatezone == 4:
                    #     feeders = {"feeder1": {"name": "R3-12.47-3", "ercot": False}}
                    #     num_gld_homes = 1326
                    # if climatezone == 5:
                    #     feeders = {"feeder1": {"name": "R5-12.47-5", "ercot": False}}
                    #     num_gld_homes = 1539
                elif utiltype == "Urban":
                    if climatezone == 3:
                        feeders = {"feeder1": {"name": "R4-12.47-1", "ercot": False},
                                   "feeder2": {"name": "R5-12.47-1", "ercot": False}}
                        num_gld_homes = 1525
                    if climatezone == 4:
                        feeders = {"feeder1": {"name": "R4-12.47-1", "ercot": False},
                                   "feeder2": {"name": "R4-12.47-2", "ercot": False}}
                        num_gld_homes = 893
                    if climatezone == 5:
                        feeders = {"feeder1": {"name": "R5-12.47-1", "ercot": False},
                                   "feeder2": {"name": "R5-12.47-2", "ercot": False}}
                        num_gld_homes = 1308

            elif feeder_mode == 'stub':
                feeders = {"feeder1": {
                    "name": "GC-12.47-1",
                    "ercot": False
                }}
                num_gld_homes = 0.1

            elif feeder_mode == 'skinny':
                feeders = {
                    "feeder1": {
                        "name": "R4-25.00-1",
                        "ercot": False
                    }}
                num_gld_homes = 168

            elif feeder_mode == 'slim':
                if busid == 2:
                    feeders = {
                        "feeder1": {
                            "name": "R4-12.47-1",
                            "ercot": False
                        }}
                    num_gld_homes = 523
                else:
                    feeders = {"feeder1": {
                        "name": "GC-12.47-1",
                        "ercot": False
                    }}
                    num_gld_homes = 0.1

            if write_case_config:
                # [bus id, name, gld_scale, Pnom, Qnom, curve_scale, curve_skew, Pinit, Qinit]
                if bus_simulated:
                    use_dso_list.append([busid, "Substation_" + str(busid),
                                         (res_customers / num_gld_homes),
                                         max_load * congestion_factor, 0, 0.5, 0, total_average_load, 0])
                else:
                    use_dso_list.append([busid, "Substation_" + str(busid),
                                         (res_customers / num_gld_homes),
                                         0, 0, 0.5, 0, total_average_load, 0])

            if DSO_Load_Threshold == 'File':
                if bus_simulated:
                    dso_simulate = True
                else:
                    dso_simulate = False
            else:
                if total_average_load >= DSO_Load_Threshold:
                    dso_simulate = True
                else:
                    dso_simulate = False

            data["DSO_" + str(busid)] = {
                "bus_number": busid,
                "used": dso_simulate,  # If true the DSO will be instantiated in prepare case and simulated
                "name": busname,
                "climate_zone": climatezone,
                "county": county,
                "latitude": float(latitude),
                "longitude": float(longitude),
                "time_zone_offset": -6,
                "utility_type": utiltype,
                "ownership_type": ownership_type,
                "ashrae_zone": ashrae_zone,
                "blm_zone": blm_zone,
                "peak_season": peakseason,
                "substation": "Substation_" + str(busid),
                "random_seed": busid,
                "feeders": feeders,
                "RCI energy mix": {
                    "residential": round(res_average_load / total_average_load, 4),
                    "commercial": round(comm_average_load / total_average_load, 4),
                    "industrial": round(indust_average_load / total_average_load, 4)
                },
                "number_of_customers": total_customers,
                "number_of_gld_homes": num_gld_homes,
                "comm_customers_per_bldg": 2.09,
                "number_of_substations": 1,
                "MVA_growth_rate": 0.01,
                "weather_file": "weather_Bus_" + str(busid) + "_" + str(latitude) + "_" + str(longitude) + ".dat",
                "RCI customer count mix": {
                    "residential": round(res_customers / total_customers, 4),
                    "commercial": round(comm_customers / total_customers, 4),
                    "industrial": round(indust_customers / total_customers, 4)
                },
                "average_load_MW": total_average_load,
                "rooftop_pv_rating_MW": roof_top_PV_MW,
                "winter_peak_MW": 0,
                "summer_peak_MW": 0,
                "capacity_limit_MW": 0,
                "scaling_factor": res_customers / num_gld_homes,
                "total_other_O&M": 0,
                "bilaterals": {
                    "price": 11.11,
                    "load_fraction": 0.11
                },
                "DSO_system_energy_fraction": 0.11
            }
        print("\n=== {0:d} DSOs Defined in Metadata File =====".format(len(data) - 1))

    json_file.close()
    # write it in the original data file
    with open(data_path + case_file + '.json', 'w') as outfile:
        json.dump(data, outfile, indent=2)

    # write out FNCS array in system_config
    if write_case_config:
        with open(case_path + config_file + '.json') as caseconfig_file:
            case_data = json.load(caseconfig_file)
            case_data['DSO'] = use_dso_list
            for i in range(len(case_data['bus'])):
                if len(case_data['bus'][i]) == 13:
                    case_data['bus'][i].append(0)
                    case_data['bus'][i].append(0)
        caseconfig_file.close()
        with open(case_path + config_file + '.json', 'w') as caseoutfile:
            json.dump(case_data, caseoutfile, indent=2)

    # write out industrial load file
    if write_industrials:
        days = 35
        num_stamps = 35 * 24 * 12
        time_stamps = [n * 300 for n in range(num_stamps)]
        array = [indust_load_list for i in range(len(time_stamps))]
        indust_df = pd.DataFrame(array,
                                 index=time_stamps,
                                 columns=dso_list)
        indust_df.index.name = 'seconds'
        indust_df.to_csv(data_path + '/200_indust_p.csv')


if __name__ == '__main__':
    # ========   INPUT SETTINGS  ========================
    data_path = '../../../../examples/analysis/dsot/data/'
    case_path = '../../../../examples/analysis/dsot/code/'
    # case_path = '../../../examples/capabilities/ercot/case8/dsostub/'
    # case_path = '../../../examples/capabilities/ercot/case8/'

    write_case_config = True  # Set true when wanting to update the QMax values in the system_case_config FNCS array
    find_county = False  # Set True if you need to use API to find county as a function of latitude and longitude
    write_industrials = False  # Set True if you want to write out the industrial load tape.
    # ========   END INPUT SETTINGS  ========================

    # def prepare_metadata(node, end_row, feeder_mode, high_renewables_case, DSO_Load_Threshold):
    prepare_metadata('8', 9, 'lean', True, 0)
    # prepare_metadata('8', 10, 'skinny', True, 0)
    # prepare_metadata('8', 10, 'stub', True, 0)
    prepare_metadata('8', 9, 'lean', False, 0)
    # prepare_metadata('8', 10, 'skinny', False, 0)
    # prepare_metadata('8', 10, 'test', False, 0)
    # prepare_metadata('8', 10, 'stub', False, 0)

    # prepare_metadata('200', 202, 'lean', True, 1130)
    # prepare_metadata('200', 202, 'skinny', True, 300)
    # prepare_metadata('200', 202, 'stub', True, 300)
    # prepare_metadata('200', 202, 'lean', False, 1130)
    # prepare_metadata('200', 202, 'test', False, 1130)
    prepare_metadata('200', 201, 'lean', False, 'File')
    prepare_metadata('200', 201, 'lean', True, 'File')
    # prepare_metadata('200', 202, 'skinny', False, 300)
    # prepare_metadata('200', 202, 'stub', False, 300)
