# Path: edisgo/flex_opt/costs.py
# def line_expansion_costs(edisgo_obj, lines_names):
#     """
#     Returns costs for earthworks and per added cable as well as voltage level
#     for chosen lines in edisgo_obj.
# 
#     Parameters
#     -----------
#     edisgo_obj : :class:`~.edisgo.EDisGo`
#         eDisGo object of which lines of lines_df are part
#     lines_names: list of str
#         List of names of evaluated lines
# 
#     Returns
#     -------
#     costs: :pandas:`pandas.DataFrame<DataFrame>`
#         Dataframe with names of lines as index and entries for
#         'costs_earthworks', 'costs_cable', 'voltage_level' for each line
# 
#     """
#     lines_df = edisgo_obj.topology.lines_df.loc[lines_names, ["length"]]
#     mv_lines = lines_df[
#         lines_df.index.isin(edisgo_obj.topology.mv_grid.lines_df.index)
#     ].index
#     lv_lines = lines_df[~lines_df.index.isin(mv_lines)].index
# 
#     # get population density in people/km^2
#     # transform area to calculate area in km^2
#     projection = proj2equidistant(int(edisgo_obj.config["geo"]["srid"]))
#     sqm2sqkm = 1e6
#     population_density = edisgo_obj.topology.grid_district["population"] / (
#         transform(projection, edisgo_obj.topology.grid_district["geom"]).area
#         / sqm2sqkm
#     )
#     if population_density <= 500:
#         population_density = "rural"
#     else:
#         population_density = "urban"
# 
#     costs_cable_mv = float(edisgo_obj.config["costs_cables"]["mv_cable"])
#     costs_cable_lv = float(edisgo_obj.config["costs_cables"]["lv_cable"])
#     costs_cable_earthwork_mv = float(
#         edisgo_obj.config["costs_cables"][
#             "mv_cable_incl_earthwork_{}".format(population_density)
#         ]
#     )
#     costs_cable_earthwork_lv = float(
#         edisgo_obj.config["costs_cables"][
#             "lv_cable_incl_earthwork_{}".format(population_density)
#         ]
#     )
# 
#     costs_lines = pd.DataFrame(
#         {
#             "costs_earthworks": (costs_cable_earthwork_mv - costs_cable_mv)
#             * lines_df.loc[mv_lines].length,
#             "costs_cable": costs_cable_mv * lines_df.loc[mv_lines].length,
#             "voltage_level": ["mv"] * len(mv_lines),
#         },
#         index=mv_lines,
#     )
# 
#     costs_lines = costs_lines.append(
#         pd.DataFrame(
#             {
#                 "costs_earthworks": (costs_cable_earthwork_lv - costs_cable_lv)
#                 * lines_df.loc[lv_lines].length,
#                 "costs_cable": costs_cable_lv * lines_df.loc[lv_lines].length,
#                 "voltage_level": ["lv"] * len(lv_lines),
#             },
#             index=lv_lines,
#         )
#     )
#     return costs_lines.loc[lines_df.index]


import numpy as np
import pandas as pd
from edisgo.flex_opt.costs import line_expansion_costs
from pypsa.descriptors import Dict


def preprocess_pypsa_opf_structure(edisgo_grid, psa_network, hvmv_trafo=False):
    """
    Prepares pypsa network for OPF problem.

    * adds line costs
    * adds HV side of HV/MV transformer to network
    * moves slack to HV side of HV/MV transformer

    Parameters
    ----------
    edisgo_grid : :class:`~.edisgo.EDisGo`
    psa_network : :pypsa:`pypsa.Network<network>`
    hvmv_trafo : :obj:`Boolean`
        If True, HV side of HV/MV transformer is added to buses and Slack
        generator is moved to HV side.

    """
    mode = "mv"
    mv_grid = edisgo_grid.topology.mv_grid

    # add line expansion costs for all lines
    # ToDo annualize line costs!!
    if (
        not hasattr(psa_network.lines, "costs_cable")
        or psa_network.lines.costs_earthworks.dropna().empty
    ):
        line_names = psa_network.lines.index

# Path: edisgo/flex_opt/costs.py
# def line_expansion_costs(edisgo_obj, lines_names):
#     """
#     Returns costs for earthworks and per added cable as well as voltage level
#     for chosen lines in edisgo_obj.
# 
#     Parameters
#     -----------
#     edisgo_obj : :class:`~.edisgo.EDisGo`
#         eDisGo object of which lines of lines_df are part
#     lines_names: list of str
#         List of names of evaluated lines
# 
#     Returns
#     -------
#     costs: :pandas:`pandas.DataFrame<DataFrame>`
#         Dataframe with names of lines as index and entries for
#         'costs_earthworks', 'costs_cable', 'voltage_level' for each line
# 
#     """
#     lines_df = edisgo_obj.topology.lines_df.loc[lines_names, ["length"]]
#     mv_lines = lines_df[
#         lines_df.index.isin(edisgo_obj.topology.mv_grid.lines_df.index)
#     ].index
#     lv_lines = lines_df[~lines_df.index.isin(mv_lines)].index
# 
#     # get population density in people/km^2
#     # transform area to calculate area in km^2
#     projection = proj2equidistant(int(edisgo_obj.config["geo"]["srid"]))
#     sqm2sqkm = 1e6
#     population_density = edisgo_obj.topology.grid_district["population"] / (
#         transform(projection, edisgo_obj.topology.grid_district["geom"]).area
#         / sqm2sqkm
#     )
#     if population_density <= 500:
#         population_density = "rural"
#     else:
#         population_density = "urban"
# 
#     costs_cable_mv = float(edisgo_obj.config["costs_cables"]["mv_cable"])
#     costs_cable_lv = float(edisgo_obj.config["costs_cables"]["lv_cable"])
#     costs_cable_earthwork_mv = float(
#         edisgo_obj.config["costs_cables"][
#             "mv_cable_incl_earthwork_{}".format(population_density)
#         ]
#     )
#     costs_cable_earthwork_lv = float(
#         edisgo_obj.config["costs_cables"][
#             "lv_cable_incl_earthwork_{}".format(population_density)
#         ]
#     )
# 
#     costs_lines = pd.DataFrame(
#         {
#             "costs_earthworks": (costs_cable_earthwork_mv - costs_cable_mv)
#             * lines_df.loc[mv_lines].length,
#             "costs_cable": costs_cable_mv * lines_df.loc[mv_lines].length,
#             "voltage_level": ["mv"] * len(mv_lines),
#         },
#         index=mv_lines,
#     )
# 
#     costs_lines = costs_lines.append(
#         pd.DataFrame(
#             {
#                 "costs_earthworks": (costs_cable_earthwork_lv - costs_cable_lv)
#                 * lines_df.loc[lv_lines].length,
#                 "costs_cable": costs_cable_lv * lines_df.loc[lv_lines].length,
#                 "voltage_level": ["lv"] * len(lv_lines),
#             },
#             index=lv_lines,
#         )
#     )
#     return costs_lines.loc[lines_df.index]


# Path: edisgo/tools/preprocess_pypsa_opf_structure.py
import numpy as np
import pandas as pd
from edisgo.flex_opt.costs import line_expansion_costs
from pypsa.descriptors import Dict



def preprocess_pypsa_opf_structure(edisgo_grid, psa_network, hvmv_trafo=False):
    """
    Prepares pypsa network for OPF problem.

    * adds line costs
    * adds HV side of HV/MV transformer to network
    * moves slack to HV side of HV/MV transformer

    Parameters
    ----------
    edisgo_grid : :class:`~.edisgo.EDisGo`
    psa_network : :pypsa:`pypsa.Network<network>`
    hvmv_trafo : :obj:`Boolean`
        If True, HV side of HV/MV transformer is added to buses and Slack
        generator is moved to HV side.

    """
    mode = "mv"
    mv_grid = edisgo_grid.topology.mv_grid

    # add line expansion costs for all lines
    # ToDo annualize line costs!!
    if (
        not hasattr(psa_network.lines, "costs_cable")
        or psa_network.lines.costs_earthworks.dropna().empty
    ):
        line_names = psa_network.lines.index

        linecosts_df = line_expansion_costs(edisgo_grid, line_names)