import pandas as pd 
import numpy as np 

"""
This module handles namespace operations for external data
"""

def read_data_to_ns(data_or_path, namespace, format="xlsx", index="Year", verbose=False, filter=None, errors="skip"):
    """ 
    reads data (excel) from a path
    :param data_or_path: data pat   h, str or path-like or a dataframe
    :param namespace: the namespace to write to 
    :param format: xlsx or csv 
    :param index: the index column of the dataframe 
    :param verbose: verbose prints the extracted data
    :param errors: 'skip' or 'raise'
    """
    
    if isinstance(data_or_path, pd.DataFrame):
        df = data_or_path

    else:
        if format == "xlsx":
            df = pd.read_excel(data_or_path)
        elif format == "csv":
            df = pd.read_csv(data_or_path)
        else:
            raise RuntimeError("Please use excel or csv format. Other format are not (yet) supported!")
    
    if index is not None:
        df = df.set_index(index)

    for col in df.columns:
        if filter and col not in filter:
            continue 
    
        if verbose:
            msg = f"read {col}" 
         
        data = df[col]
        data = np.array(data, dtype=np.float32)

        success = False 
        if hasattr(namespace, col) and (getattr(namespace, col) is not None): # overwrite existing

            old_data = getattr(namespace, col)
            
            
            fmt_error = True 
            shape_error = True
            
            if (not np.isscalar(old_data)) and (not np.isscalar(data)):
                fmt_error = False 
                if data.shape == old_data.shape:
                    shape_error = False 
            elif (np.isscalar(old_data)) and (np.isscalar(data)):
                fmt_error = False 
                if data == old_data:
                    shape_error = False
            
            if not (fmt_error or shape_error):
                setattr(namespace, col, data)
                if verbose:
                    msg += "---> overwrite"
            
            if errors == "raise":
                if fmt_error:
                    raise RuntimeError("Wrong data format in ", col)
                if shape_error: 
                    raise RuntimeError("Shape mismatch in ", col, data.shape, old_data.shape)
            elif errors == "skip":
                pass 
            else:
                raise ValueError("'errors' option must be 'raise' or 'skip'")    
        else: # write new
            setattr(namespace, col, data)
            if verbose: 
                msg += " *"
        if verbose:
            print(msg)


def read_coeffs_to_ns(data_or_path, namespace, format="xlsx", filter=None, index="param", verbose=False):
    """
    Reads a table with coefficients to a namespace 

    :param data_or_path: str or path-like, or dataframe
    :param namespace: 
    :param format: 'excel' or 'csv'
    :param filter: list or None to filter rows 
    """ 

    if isinstance(data_or_path, pd.DataFrame):
        df = data_or_path
    else:
        if format == "xlsx": 
            df = pd.read_excel(data_or_path)
        elif format == "csv": 
            df = pd.read_csv(data_or_path)
        else:
            raise RuntimeError("Format must be csv or xlsx")
    
    #if verbose:
    #    print("df")
    #    print(df)

    if index is not None:
        try:
            df = df.set_index(index)
        except Exception as e:
            print(str(e))
    
    #if verbose:
    #    print("coeffs")
    #    print(df["coeff"])
    
    coeff_dict = df["coeff"].to_dict() 

    for k, v in coeff_dict.items():
        if (not filter) or (k in filter):
            if verbose: 
                print("set", k, v)
            setattr(namespace, k, v)
