import numpy as np

import AvenirCommon.Util.Util as acu

import SpectrumCommon.Const.GB.GBConst as gbc
import SpectrumCommon.Const.PC.PCConst as pcc
import SpectrumCommon.Const.PC.PCConstLink as pccl
import SpectrumCommon.Const.PC.PCModVarFields as pcmvf
import SpectrumCommon.Util.PC.PCUtilLink as pcul
import SpectrumCommon.Modvars.PC.PCUtil as pcu

#######################################################################################################
#                                                                                                     #
#                   Area data record - contains basically everything in PC for a                      #
#                   particular programme area, delivery channel, package, or national programme       #
#                                                                                                     #
#######################################################################################################

def PC_create_area_data_dict(area_type_mst_ID: int, ID: str, num_years: int, costing_mode: int):
    num_rows = pcc.PC_NUM_STANDARD_ROWS

    if (costing_mode == gbc.GB_IHT_COSTING_MODE) and (ID == str(pccl.PC_IH_HIV_AIDS_MST_ID)):
        num_rows = pcc.PC_TOTAL_NUM_HIV_ROWS

    return {
        pcmvf.PC_AREA_TYPE_MST_ID : area_type_mst_ID,
        pcmvf.PC_AREA_ID          : ID,
        pcmvf.PC_MAIN_ROW_COSTS   : np.full((num_rows, num_years), 0.0).tolist(),
    }


# def init_area_dict_list(area_ID_list: list, num_years: int):
#     area_dict_list = []

#     for area_ID in area_ID_list:
#         area_data_dict = create_area_data_dict(area_ID, num_years)
#         area_dict_list.append(area_data_dict)

#     return area_dict_list


def PC_init_area_dict_list(area_type_mst_ID: int, prog_area_mod_var_dict_list: list, num_years: int, costing_mode: int):
    area_dict_list = []

    if area_type_mst_ID == pccl.PC_IH_PROG_AREA_MST_ID:
        for area_mod_var_dict in prog_area_mod_var_dict_list:
            prog_area_ID = pcul.PC_get_IH_prog_area_ID(area_mod_var_dict)
            area_data_dict = PC_create_area_data_dict(area_type_mst_ID, prog_area_ID, num_years, costing_mode)
            area_dict_list.append(area_data_dict)

    return area_dict_list


def PC_create_prog_mgmt_data_dict(mod_mst_ID: int, num_years: int):
    num_rows = pcc.PC_NUM_STANDARD_ROWS

    if mod_mst_ID != gbc.GB_IS:
        num_rows = pcc.PC_PM_NUM_STANDARD_ROWS
    else:
        num_rows = pcc.PC_PM_TOTAL_NUM_IS_ROWS

    return {
        pcmvf.PC_MAIN_ROW_COSTS: np.full((num_rows, num_years), 0.0).tolist(),
    }

#######################################################################################################
#                                                                                                     #
#                                     Cost category records                                           #
#                                                                                                     #
#######################################################################################################

def PC_create_cost_cat_rec(ID: str, name: str, string_const: str):

    return {
        pcmvf.PC_COST_CAT_ID         : ID,
        pcmvf.PC_COST_CAT_NAME       : name,
        pcmvf.PC_COST_CAT_STR_CONST  : string_const,
        pcmvf.PC_COST_CAT_CUSTOM     : False,
    }

#######################################################################################################
#                                                                                                     #
#                                              Cost input records                                     #
#                                                                                                     #
#######################################################################################################

def PC_create_cost_input_rec(ID: str, name: str, cost_cat_ID: int):

    return {
        pcmvf.PC_COST_INPUT_ID                        : ID,
        pcmvf.PC_COST_INPUT_NAME                      : name,
        pcmvf.PC_COST_INPUT_STR_CONST                 : '',
        pcmvf.PC_COST_INPUT_CAT_ID                    : cost_cat_ID,
        pcmvf.PC_COST_INPUT_CUSTOM                    : False,
        pcmvf.PC_COST_INPUT_NOTE                      : '',
        pcmvf.PC_COST_INPUT_SOURCE                    : '',
        pcmvf.PC_COST_INPUT_UNIT_OF_MEASURE           : '',
        pcmvf.PC_COST_INPUT_UNIT_OF_MEASURE_STR_CONST : '',
        pcmvf.PC_COST_INPUT_UNIT_COST                 : 0.0,
    }

#######################################################################################################
#                                                                                                     #
#                                 Costing section and costing subsection records                      #
#                                                                                                     #
#######################################################################################################

def PC_create_costing_section_rec(area_type_ID: int, area_ID: str, ID: str, name: str, string_const: str, num_years: int, costing_mode: int):

    costing_section_rec = {
        pcmvf.PC_COSTING_SECT_AREA_TYPE_ID    : area_type_ID,
        pcmvf.PC_COSTING_SECT_AREA_ID         : area_ID,
        pcmvf.PC_COSTING_SECT_ID              : ID,
        pcmvf.PC_COSTING_SECT_NAME            : name,
        pcmvf.PC_COSTING_SECT_STR_CONST       : string_const,
        pcmvf.PC_COSTING_SECT_CUSTOM          : False,
    }

    if costing_mode == gbc.GB_LIST_COSTING_MODE:
        costing_section_rec[pcmvf.PC_COSTING_SECT_ENTRY_MODE_CS] = gbc.GB_PERCENT
        costing_section_rec[pcmvf.PC_COSTING_SECT_COSTS_CS] = np.full((num_years), 0.0).tolist()
    else:
        costing_section_rec[pcmvf.PC_COSTING_SECT_SUBSECT_RECORDS] = []

    return costing_section_rec

# Area ID should be the programme area (currently just NCD)
def PC_create_costing_section_rec_PPLI(area_ID: str, ID: str, name: str, string_const: str, num_years: int):
    rec = PC_create_costing_section_rec(pccl.PC_IH_PPLI_MST_ID, area_ID, ID, name, string_const, num_years, gbc.GB_IHT_COSTING_MODE)

    # Taxation tab (For Tobacco control, alcohol control, sugar-sweetened beverage control sections); Will vary a bit for alcohol and beverages though,
    # So only do this for Tobacco control for now...

    # Upper table
    rec[pcmvf.PC_COSTING_SECT_PRICE_ELASTICITY_PPLI] = np.full((pcc.PC_NUM_TAX_COLS_PPLI), 0.0).tolist()
    rec[pcmvf.PC_COSTING_SECT_INCOME_ELASTICITY_PPLI] = np.full((pcc.PC_NUM_TAX_COLS_PPLI), 0.0).tolist()
    rec[pcmvf.PC_COSTING_SECT_PRICE_INCOME_ONLY_PPLI] = np.full((pcc.PC_NUM_TAX_COLS_PPLI), 0).tolist()
    rec[pcmvf.PC_COSTING_SECT_NON_PRICE_MEASURES_PPLI] = np.full((pcc.PC_NUM_TAX_COLS_PPLI), 0).tolist()
    rec[pcmvf.PC_COSTING_SECT_ALL_MPOWER_PPLI] = np.full((pcc.PC_NUM_TAX_COLS_PPLI), 0).tolist()

    # Lower table
    rec[pcmvf.PC_COSTING_SECT_TAX_ENTRY_MODE_PPLI] = pcc.PC_ENTER_REAL_RETAIL_PRICE_PPLI_MST_ID
    rec[pcmvf.PC_COSTING_SECT_REAL_RETAIL_PRICE_PPLI] = np.full((pcc.PC_NUM_YEAR_ENDPOINTS_PPLI), 0).tolist()
    rec[pcmvf.PC_COSTING_SECT_EXCISE_SHARE_PPLI] = np.full((pcc.PC_NUM_YEAR_ENDPOINTS_PPLI), 0).tolist()
    rec[pcmvf.PC_COSTING_SECT_TAX_SHARE_PPLI] = np.full((pcc.PC_NUM_YEAR_ENDPOINTS_PPLI), 0).tolist()
    rec[pcmvf.PC_COSTING_SECT_REAL_EXCISE_PER_PACK_PPLI] = np.full((pcc.PC_NUM_YEAR_ENDPOINTS_PPLI), 0).tolist()
    rec[pcmvf.PC_COSTING_SECT_REAL_TAX_PER_PACK_PPLI] = np.full((pcc.PC_NUM_YEAR_ENDPOINTS_PPLI), 0).tolist()
    rec[pcmvf.PC_COSTING_SECT_REAL_INDUSTRY_PRICE_PPLI] = np.full((pcc.PC_NUM_YEAR_ENDPOINTS_PPLI), 0).tolist()
    rec[pcmvf.PC_COSTING_SECT_TAX_ADJUST_CONSUMP_PPLI] = np.full((pcc.PC_NUM_YEAR_ENDPOINTS_PPLI), 0).tolist()
    rec[pcmvf.PC_COSTING_SECT_EXCISE_REVENUE_PPLI] = np.full((pcc.PC_NUM_YEAR_ENDPOINTS_PPLI), 0).tolist()
    rec[pcmvf.PC_COSTING_SECT_TAX_REVENUE_PPLI] = np.full((pcc.PC_NUM_YEAR_ENDPOINTS_PPLI), 0).tolist()
    rec[pcmvf.PC_COSTING_SECT_USAGE_RATE_PPLI] = np.full((pcc.PC_NUM_YEAR_ENDPOINTS_PPLI), 0.0).tolist()
    rec[pcmvf.PC_COSTING_SECT_NUM_USERS] = np.full((pcc.PC_NUM_YEAR_ENDPOINTS_PPLI), 0).tolist()

    return rec

def PC_create_costing_subsection_rec(ID: str, name: str, string_const: str):

    return {
        pcmvf.PC_COSTING_SUBSECT_ID        : ID,
        pcmvf.PC_COSTING_SUBSECT_NAME      : name,
        pcmvf.PC_COSTING_SUBSECT_STR_CONST : string_const,
        pcmvf.PC_COSTING_SUBSECT_CUSTOM    : False,
    }

def PC_create_costing_subsection_rec_PPLI(ID: str, name: str, string_const: str, num_years: int):
    rec = PC_create_costing_subsection_rec(ID, name, string_const)

    rec[pcmvf.PC_COSTING_SUBSECT_ADMIN_UNITS_PER_LEVEL_OPTION_PPLI] = pcc.PC_USE_NUM_ADMIN_UNITS_FROM_CONFIG_PPLI_MST_ID
    rec[pcmvf.PC_COSTING_SUBSECT_ADMIN_UNITS_STD_POP_SIZE_PPLI] = np.full((pcc.PC_PPLI_NUM_ADMIN_LEVELS), 0).tolist()
    rec[pcmvf.PC_COSTING_SUBSECT_ENTER_TOTAL_INPUTS_PER_ADMIN_REG_YEAR_PPLI] = False

    if ID == pcc.PC_RAISE_TAXES_PPLI_COSTING_SUBSECT_ID:
        rec[pcmvf.PC_COSTING_SUBSECT_RAISE_TAXES_PRICE_PPLI] = np.full((num_years), 0).tolist()
        rec[pcmvf.PC_COSTING_SUBSECT_RAISE_TAXES_EXCISE_SHARE_PPLI] = np.full((num_years), 0).tolist()
        rec[pcmvf.PC_COSTING_SUBSECT_RAISE_TAXES_TAX_SHARE_PPLI] = np.full((num_years), 0).tolist()

    phase_intensity_values = np.full((pcc.PC_NUM_PHASE_INTENSITY_ROW_TYPES_PPLI, num_years), 0)
    phase_intensity_values[pcc.PC_PHASE_ROW_PPLI_CURR_ID - 1, :] = pcc.PC_NO_ACTION_PHASE_0_PPLI_MST_ID
    rec[pcmvf.PC_COSTING_SUBSECT_PHASE_INTENSITY_VALUES_PPLI] = phase_intensity_values.tolist()

    return rec
    
#######################################################################################################
#                                                                                                     #
#                                       Activity records                                              #
#                                                                                                     #
#######################################################################################################

def PC_create_activity_rec(area_type_ID: int, area_ID: str, costing_sect_ID: str, costing_subsection_ID: str, activity_ID: str, name: str):

    return {
        pcmvf.PC_ACT_AREA_TYPE_ID       : area_type_ID,
        pcmvf.PC_ACT_AREA_ID            : area_ID,
        pcmvf.PC_ACT_COSTING_SECT_ID    : costing_sect_ID,
        pcmvf.PC_ACT_COSTING_SUBSECT_ID : costing_subsection_ID,
        pcmvf.PC_ACT_ID                 : activity_ID,
        pcmvf.PC_ACT_CUSTOM             : False,
        pcmvf.PC_ACT_NAME               : name,
        pcmvf.PC_ACT_STR_CONST          : '',
    }

#######################################################################################################
#                                                                                                     #
#                                      Activity line item (ALI) records                               #
#                                                                                                     #
#######################################################################################################

def PC_create_activity_line_item_rec(area_type_ID: int, area_ID: str, costing_sect_ID: str, costing_subsect_ID: str, act_ID: str, cost_input_ID: str, num_years: int, pop_and_policy_level_intervs: bool):

    rec = {
        pcmvf.PC_ALI_AREA_TYPE_ID        : area_type_ID, 
        pcmvf.PC_ALI_AREA_ID             : area_ID,
        pcmvf.PC_ALI_COSTING_SECT_ID     : costing_sect_ID, 
        pcmvf.PC_ALI_COSTING_SUBSECT_ID  : costing_subsect_ID,
        pcmvf.PC_ALI_ACT_ID              : act_ID,
        pcmvf.PC_ALI_COST_INPUT_ID       : cost_input_ID,
    }

    if not pop_and_policy_level_intervs:
        rec[pcmvf.PC_ALI_FREQUENCY] = np.full((num_years), 0.0).tolist()
        # We'll use these fields for PPLI too, but they need to be by admin level in that case
        rec[pcmvf.PC_ALI_NUM_UNITS] = 0
        rec[pcmvf.PC_ALI_DURATION] = 0

    return rec

# Area ID should be the programme area (currently just NCD)
def PC_create_activity_line_item_rec_PPLI(area_ID: str, costing_sect_ID: str, costing_subsect_ID: str, act_ID: str, cost_input_ID: str, num_years: int):
    rec = PC_create_activity_line_item_rec(pccl.PC_IH_PPLI_MST_ID, area_ID, costing_sect_ID, costing_subsect_ID, act_ID, cost_input_ID, num_years, True)

    # Each field needs to be specified for Inputs per admin level and Total inputs (the former feeds the latter and the latter is what is used by results)
    rec[pcmvf.PC_ALI_QUANTITY_PER_ADMIN_REG_YEAR_PPLI] = np.full((pcc.PC_NUM_PPLI_ALI_SETS, pcc.PC_PPLI_NUM_ADMIN_LEVELS, num_years), 0.0).tolist()
    rec[pcmvf.PC_ALI_NUM_UNITS] = np.full((pcc.PC_PPLI_NUM_ADMIN_LEVELS), 0.0).tolist()
    rec[pcmvf.PC_ALI_DURATION] = np.full((pcc.PC_PPLI_NUM_ADMIN_LEVELS), 0.0).tolist()

    return rec

def PC_create_costs_by_sect_rec(area_type_ID: str, area_ID: str, costing_sect_ID: str,  value: list):

    return {
        pcmvf.PC_F_AREA_TYPE_ID        : area_type_ID,
        pcmvf.PC_F_AREA_ID             : area_ID,
        pcmvf.PC_F_COSTING_SECT_ID     : costing_sect_ID,
        pcmvf.PC_F_VALUE               : value,
    }

def PC_create_costs_by_subsect_rec(area_type_ID: str, area_ID: str, costing_sect_ID: str, costing_subsect_ID: str, value: list):

    return {
        pcmvf.PC_F_AREA_TYPE_ID        : area_type_ID,
        pcmvf.PC_F_AREA_ID             : area_ID,
        pcmvf.PC_F_COSTING_SECT_ID     : costing_sect_ID,
        pcmvf.PC_F_COSTING_SUBSECT_ID  : costing_subsect_ID,
        pcmvf.PC_F_VALUE               : value,
    }

def PC_create_costs_by_act_rec(area_type_ID: str, area_ID: str, costing_sect_ID: str,  costing_subsect_ID: str, act_ID: str, value: list):

    return {
        pcmvf.PC_F_AREA_TYPE_ID        : area_type_ID,
        pcmvf.PC_F_AREA_ID             : area_ID,
        pcmvf.PC_F_COSTING_SECT_ID     : costing_sect_ID,
        pcmvf.PC_F_COSTING_SUBSECT_ID  : costing_subsect_ID,
        pcmvf.PC_F_ACT_ID              : act_ID,
        pcmvf.PC_F_VALUE               : value,
    }

def PC_create_costs_by_act_and_cost_cat_rec(area_type_ID: str, area_ID: str, costing_sect_ID: str, costing_subsect_ID: str, act_ID: str, cost_cat_ID: str, value: list):

    return {
        pcmvf.PC_F_AREA_TYPE_ID        : area_type_ID,
        pcmvf.PC_F_AREA_ID             : area_ID,
        pcmvf.PC_F_COSTING_SECT_ID     : costing_sect_ID,
        pcmvf.PC_F_COSTING_SUBSECT_ID  : costing_subsect_ID,
        pcmvf.PC_F_ACT_ID              : act_ID,
        pcmvf.PC_F_COST_CAT_ID         : cost_cat_ID,
        pcmvf.PC_F_VALUE               : value,
    }

########################################################################################################################################
########################################################################################################################################
#                                                                                                                                      #
#                                                       Meta data                                                                      #
#                                                                                                                                      #
########################################################################################################################################
########################################################################################################################################

def PC_init_row_info_meta(prog_area_ID):
    row_info_meta = []

    if prog_area_ID == pccl.PC_IH_HIV_AIDS_MST_ID:
        num_rows = pcc.PC_TOTAL_NUM_HIV_ROWS
    else:
        num_rows = pcc.PC_NUM_STANDARD_ROWS

    for row in acu.GBRange(1, num_rows):
        mst_ID = pcu.get_PC_row_mst_ID(prog_area_ID, row)

        row_info_meta.append(
            {
                "mstID"          : mst_ID,
                "name"           : pcu.get_PC_row_name(mst_ID),
                "stringConstant" : pcu.get_PC_row_string_constant(mst_ID),
                "isAggregate"    : pcu.get_PC_row_is_aggregate(mst_ID),
            }
        )

        # Some programme areas have rows that are not costed (ex: coverage rows for HIV). Add a field so we can mark which rows are costs.
        if prog_area_ID == pccl.PC_IH_HIV_AIDS_MST_ID:
            row_info_meta[row - 1]["isCost"] = pcu.get_PC_row_is_cost(prog_area_ID, mst_ID)
            row_info_meta[row - 1]["singleLine"] = pcu.get_PC_row_is_single_line(prog_area_ID, mst_ID)
            row_info_meta[row - 1]["hasData"] = pcu.get_PC_row_has_data(prog_area_ID, mst_ID)

    return row_info_meta

