import os, yaml
import warnings
from datetime import datetime
import numpy as np


config_aim = os.path.dirname(os.path.dirname(__file__))
config_aim = os.path.join(config_aim, 'data/refdata_path.yaml')


# def set_config(refdata_path=None):
#     if refdata_path is None:
#         print("input cpism refencence data folder")
#         refdata_path = input()
#         refdata_path = os.path.abspath(refdata_path)
#     with open(config_aim, 'w') as f:
#         yaml.dump(refdata_path, f)
#     return refdata_path
    

# try:
#     with open(config_aim, 'r') as f:
#         cpism_refdata = yaml.load(f, Loader=yaml.FullLoader)
#     if not os.path.isdir(cpism_refdata):
#         raise FileNotFoundError('cpism refdata path not found')
#     config_set = True
# except FileNotFoundError:
#     warnings.warn(f'refdata not setup yet, set it before use')
#     cpism_refdata = set_config()


def load_refdata_path(config_aim):
    """Load refdata path.
    The refdata path is stored in config_aim file. If not found, set it.

    Parameters
    ----------
    config_aim : str
        config_aim file path
    
    
    """
    with open(config_aim, 'r') as f:
        refdata_list = yaml.load(f, Loader=yaml.FullLoader)

    for refdata in refdata_list:
        if os.path.isdir(refdata):
            return refdata
        
    print("csst_cpic_sim refdata folder not found, please input cpism refencence data folder")
    refdata = input()
    refdata = os.path.abspath(refdata)
    if os.path.isdir(refdata):
        refdata_list.append(refdata)
    with open(config_aim, 'w') as f:
        yaml.dump(refdata_list, f)
    exit()
    

cpism_refdata = load_refdata_path(config_aim)

config = {}
config['cpism_refdata'] = cpism_refdata
config['utc0'] = '2024-05-01T00:00:00'
config['hybrid_model'] = f'{cpism_refdata}/target_model/hybrid_model.fits'
config['bcc_model'] = f'{cpism_refdata}/target_model/bccmodels'
config['mag_system'] = 'abmag'
config['apm_file'] = f'{cpism_refdata}/optics/apm.fits'
config['actuator_file'] = f'{cpism_refdata}/optics/actuator.fits'
config['aberration'] = f'{cpism_refdata}/optics/initial_phase_aberration.fits'
config['mask_width'] = 0.4
config['check_fits_header'] = False
config['bands'] = {
    'f661': f'{cpism_refdata}/throughtput/f661_total.fits',
    'f743': f'{cpism_refdata}/throughtput/f743_total.fits',
    'f883': f'{cpism_refdata}/throughtput/f883_total.fits',
    'f565': f'{cpism_refdata}/throughtput/f565_total.fits',
    'f520': f'{cpism_refdata}/throughtput/f520.fits',
    'f662': f'{cpism_refdata}/throughtput/f662.fits',
    'f850': f'{cpism_refdata}/throughtput/f850.fits',
    'f720': f'{cpism_refdata}/throughtput/f720.fits',
}
config['diameter'] = 2 # in meters
config['platescale'] = 0.016153
config['datamodel'] = f'{cpism_refdata}/io/csst-cpic-l0.yaml'

config['log_dir'] = f'{cpism_refdata}/log'
config['log_level'] = f'info'
config['output'] = f'./'
config['sp2teff_model'] = f'{cpism_refdata}/target_model/sptype2teff_lut.json'
config['dm_pickle'] = f'{cpism_refdata}/optics/dm_model.pkl'
config['pysyn_refdata'] = f'{cpism_refdata}/starmodel/grp/redcat/trds'
config['catalog_folder'] = f'{cpism_refdata}/demo_catalog'
config['csst_format'] = True
config['nsample'] = 5

update_able_keys = [
    'apm_file', 'actuator_file', 'aberration', 'log_dir', 'log_level', 'catalog_folder', 'nsample', 'csst_format', 'output', 'check_fits_header'
]

def replace_cpism_refdata(
        config: dict, 
        output: str = '$') -> None:
    """Replace the cpism_refdata in the config. 
    In the config file, we use ${cpism_refdata} to indicate the cpism_refdata.
    This function is used to replace the cpism_refdata in the config, or replace back.
    
    Parameters
    ----------
    config: dict
        config dict. 
    output: str
        '$' or 'other'. If output is '$', then replace the cpism_refdata in the config with ${cpism_refdata}. 
        If output is 'other', then replace the ${cpism_refdata} in the config file with the real path.
        '$' is used meanly to generate a demo config file.
    """
    aim = cpism_refdata
    target = '${cpism_refdata}'
    if output != '$':
        aim, target = target, aim
    for key, value in config.items():
        if isinstance(value, str):
            config[key] = value.replace(aim, target)
        if isinstance(value, dict):
            replace_cpism_refdata(value, output)


with open(cpism_refdata + '/cpism_config.yaml', 'r') as f:
    new_config = yaml.load(f, Loader=yaml.FullLoader)
    replace_cpism_refdata(new_config, None)
    config.update(new_config)

if os.environ.get('PYSYN_CDBS') is None:
    os.environ['PYSYN_CDBS'] = config['pysyn_refdata']
__version__ = '2.0.0'

# we need to ignore the warning from pysynphot, because we only use the star models.
with warnings.catch_warnings():  # pragma: no cover
    warnings.filterwarnings("ignore")
    import pysynphot as S


def setup_config(new_config):
    """Set up config from a dict. 
    Some of the configs need to calcuate.

    Parameters
    ----------
    new_config: dict
        new config dict. 
    
    Returns
    -------
    None
    """
    config.update(new_config)
    config['utc0_float'] = datetime.timestamp(datetime.fromisoformat(config['utc0']))
    config['solar_spectrum'] = f"{os.environ['PYSYN_CDBS']}/grid/solsys/solar_spec.fits"
    config['aperature_area'] = (config['diameter'] * 50)**2 * np.pi # cm^2
    config['default_band'] = list(config['bands'].keys())[0]
    config['default_filter'] = config['bands'][config['default_band']]

setup_config({})

def which_focalplane(band):
    """
    Return the name of the focalplane which the band belongs to.
    right now only support vis

    Parameters
    -----------
    band: str
        The name of the band.
        

    Returns
    --------
    str
        The name of the focalplane.
        'vis' or 'nir' or 'wfs'

    Raises
    -------
    ValueError
        If the band is not in ['f565', 'f661', 'f743', 'f883', 'f940', 'f1265', 'f1425', 'f1542', 'wfs']
    """
    # band = band.lower()
    # if band in ['f565', 'f661', 'f743', 'f883']:
    #     return 'vis'
    # if band in ['f940', 'f1265', 'f1425', 'f1542']:
    #     return 'nir'
    # if band in ['wfs']:
    #     return 'wfs'
    return 'vis'
    # raise ValueError(f"未知的波段{band}")

def iso_time(time):
    """Transfer relative time to iso time format
    
    Parameters
    ----------
    time: str or float
        The relative time in seconds.
    
    Returns
    -------
    str
        The iso time format.
    
    """

    if isinstance(time, str):
        _  = datetime.fromisoformat(time)
        return time
    
    utc0 = config['utc0']
    time0 = datetime.timestamp(datetime.fromisoformat(utc0))
    time = datetime.fromtimestamp(time0 + time)
    return time.isoformat()

def relative_time(time):
    """Transfer iso time format to relative time in seconds

    Parameters
    ----------
    time: str or float
        The iso time format.
    
    Returns
    -------
    float
        The relative time in seconds.
    """

    if isinstance(time, float):
        return time
    if isinstance(time, int):
        return float(time)
    
    utc0 = config['utc0']
    time0 = datetime.timestamp(datetime.fromisoformat(utc0))
    return datetime.timestamp(datetime.fromisoformat(time)) - time0
    