import argparse
import os
import torch
from exp.exp_mmd import Exp_Long_Term_Forecast
from utils.print_args import print_args
import random
import numpy as np
import numpy as np
import pandas as pd
import re
def get_single_result(domain1,model1,pred_len1,seed1):
    lines = open('/localscratch/hliu763/24Fall/Time-Series-Library-Benchmarking-Multimodal_0527/result_long_term_forecast_0526_1653pm', 'r').readlines()
    name_ptn = re.compile('long_term_forecast_(.*?)_([0-9]{4})_[0-9]*_([0-9]*)_(.*?)_custom_.*')
    result_ptn = re.compile('mse:(.*?), mae:(.*?), maape:(.*?), mspe:(.*)')

    domains = ['Security', 'Traffic', 'Entertainment', 'Economy', 'Algriculture', 'Environment', 'Climate', 'SocialGood', 'Public_Health', 'Energy']
    models = ['iTransformer', 'Crossformer', 'Nonstationary_Transformer', 'FiLM', 'Reformer', 'Informer', 'DLinear', 'FEDformer', 'TimeMixer', 'TimesNet', 'PatchTST', 'TSMixer', 'Autoformer', 'Transformer', 'LightTS', 'Pyraformer']
    single_results = {}
    seeds = [1998, 2021, 2024]
    for domain in domains:
        single_results[domain] = {}
        for model in models:
            single_results[domain][model] = {}

    for i in range(0, len(lines), 4):
        name = lines[i]
        results = lines[i+1]
        domain, seed, pred_len, model = name_ptn.findall(name)[0]
        mse, mae, maape, mspe = result_ptn.findall(results)[0]
        pred_len = int(pred_len)
        seed = int(seed)
        # mse, mae, maape, mspe
        if pred_len not in single_results[domain][model].keys():
            single_results[domain][model][pred_len] = {}
        if seed not in single_results[domain][model][pred_len].keys():
            single_results[domain][model][pred_len][seed] = []
        single_results[domain][model][pred_len][seed].append(float(mse))
    try:
        # 尝试访问指定的嵌套字典路径
        value = single_results[domain1][model1][pred_len1][seed1]
        return True,value[0]
    except (KeyError, IndexError, TypeError):
        return False,-1
def get_multi_result(domain1,model1,pred_len1,seed1):
    new_lines = open('/localscratch/hliu763/24Fall/Time-Series-Library-Benchmarking-Multimodal_0527/result_encodegpt2_0525_0527_2349pm', 'r').readlines()

    name_ptn = re.compile('long_term_forecast_(.*?)_([0-9]{4})_[0-9]*_([0-9]*)_fullLLM_0_(.*?)_custom_.*')
    result_ptn = re.compile('mse:(.*?), mae:(.*?), rmse:(.*?), mape:(.*?), mspe:(.*)')
    domains = ['Security', 'Traffic', 'Entertainment', 'Economy', 'Algriculture', 'Environment', 'Climate', 'SocialGood', 'Public_Health', 'Energy']
    models = ['iTransformer', 'Crossformer', 'Nonstationary_Transformer', 'FiLM', 'Reformer', 'Informer', 'DLinear', 'FEDformer', 'TimeMixer', 'TimesNet', 'PatchTST', 'TSMixer', 'Autoformer', 'Transformer', 'LightTS', 'Pyraformer']

    multi_results = {}
    seeds = [1998, 2021, 2024]
    for domain in domains:
        multi_results[domain] = {}
        for model in models:
            multi_results[domain][model] = {}

    for i in range(0, len(new_lines), 3):
        name = new_lines[i]
        #print(f"Processing line {i}: {name}")  # 打印当前处理的行

        results = new_lines[i+1]
        domain, seed, pred_len, model = name_ptn.findall(name)[0]
        mse, mae, rmse, maape, mspe = result_ptn.findall(results)[0]
        pred_len = int(pred_len)
        seed = int(seed)
        # mse, mae, maape, mspe
        if pred_len not in multi_results[domain][model].keys():
            multi_results[domain][model][pred_len] = {}
        if seed not in multi_results[domain][model][pred_len].keys():
            multi_results[domain][model][pred_len][seed] = []
        multi_results[domain][model][pred_len][seed].append(float(mse))
    try:
        # 尝试访问指定的嵌套字典路径
        values = multi_results[domain1][model1][pred_len1][seed1]
        if values:  # 确保列表不为空
            min_value = min(values)
            return True, min_value
        else:
            return False, None
    except (KeyError, IndexError, TypeError):
        return False,-1
import os
os.environ["TOKENIZERS_PARALLELISM"] = "true"
if __name__ == '__main__':
    
    parser = argparse.ArgumentParser(description='TimesNet')

    # basic config
    parser.add_argument('--task_name', type=str, required=True, default='long_term_forecast',
                        help='task name, options:[long_term_forecast, short_term_forecast, imputation, classification, anomaly_detection]')
    parser.add_argument('--train_only', type=bool, required=False, default=False, help='perform training on full input dataset without validation and testing')
    parser.add_argument('--is_training', type=int, required=True, default=1, help='status')
    parser.add_argument('--model_id', type=str, required=True, default='test', help='model id')
    parser.add_argument('--model', type=str, required=True, default='Autoformer',
                        help='model name, options: [Autoformer, Transformer, TimesNet]')
    parser.add_argument('--predict_path', type=str, default='synthetic_val_dataset.pth', help='predata file')
    parser.add_argument('--do_predict', action='store_true', help='whether to predict unseen future data')
    
    # data loader
    parser.add_argument('--data', type=str, required=True, default='synthetic', help='dataset type')
    parser.add_argument('--root_path', type=str, default='./dataset/', help='root path of the data file')
    parser.add_argument('--traindata_path', type=str, default='synthetic_train_dataset.pth', help='traindata file')
    parser.add_argument('--valdata_path', type=str, default='synthetic_val_dataset.pth', help='valdata file')
    parser.add_argument('--features', type=str, default='S',
                        help='forecasting task, options:[M, S, MS]; M:multivariate predict multivariate, S:univariate predict univariate, MS:multivariate predict univariate')
    parser.add_argument('--target', type=str, default='OT', help='target feature in S or MS task')
    parser.add_argument('--freq', type=str, default='d',
                        help='freq for time features encoding, options:[s:secondly, t:minutely, h:hourly, d:daily, b:business days, w:weekly, m:monthly], you can also use more detailed freq like 15min or 3h')
    parser.add_argument('--checkpoints', type=str, default='./checkpoints/', help='location of model checkpoints')

    # forecasting task
    parser.add_argument('--seq_len', type=int, default=200, help='input sequence length')
    parser.add_argument('--label_len', type=int, default=30, help='start token length')
    parser.add_argument('--pred_len', type=int, default=30, help='prediction sequence length')
    parser.add_argument('--seasonal_patterns', type=str, default='Monthly', help='subset for M4')
    parser.add_argument('--inverse', action='store_true', help='inverse output data', default=False)

    # inputation task
    parser.add_argument('--mask_rate', type=float, default=0.25, help='mask ratio')

    # anomaly detection task
    parser.add_argument('--anomaly_ratio', type=float, default=0.25, help='prior anomaly ratio (%)')

    # model define
    parser.add_argument('--class_strategy', type=str, default='projection', help='projection/average/cls_token')
    parser.add_argument('--expand', type=int, default=2, help='expansion factor for Mamba')
    parser.add_argument('--d_conv', type=int, default=4, help='conv kernel size for Mamba')
    parser.add_argument('--top_k', type=int, default=5, help='for TimesBlock')
    parser.add_argument('--num_kernels', type=int, default=6, help='for Inception')
    parser.add_argument('--enc_in', type=int, default=1, help='encoder input size')
    parser.add_argument('--dec_in', type=int, default=1, help='decoder input size')
    parser.add_argument('--c_out', type=int, default=1, help='output size')
    parser.add_argument('--d_model', type=int, default=512, help='dimension of model')
    parser.add_argument('--n_heads', type=int, default=8, help='num of heads')
    parser.add_argument('--e_layers', type=int, default=2, help='num of encoder layers')
    parser.add_argument('--d_layers', type=int, default=1, help='num of decoder layers')
    parser.add_argument('--d_ff', type=int, default=2048, help='dimension of fcn')
    parser.add_argument('--moving_avg', type=int, default=25, help='window size of moving average')
    parser.add_argument('--factor', type=int, default=1, help='attn factor')
    parser.add_argument('--distil', action='store_false',
                        help='whether to use distilling in encoder, using this argument means not using distilling',
                        default=True)
    parser.add_argument('--dropout', type=float, default=0.1, help='dropout')
    parser.add_argument('--embed', type=str, default='timeF',
                        help='time features encoding, options:[timeF, fixed, learned]')
    parser.add_argument('--activation', type=str, default='gelu', help='activation')
    parser.add_argument('--output_attention', action='store_true', help='whether to output attention in ecoder')
    parser.add_argument('--channel_independence', type=int, default=1,
                        help='0: channel dependence 1: channel independence for FreTS model')
    parser.add_argument('--decomp_method', type=str, default='moving_avg',
                        help='method of series decompsition, only support moving_avg or dft_decomp')
    parser.add_argument('--use_norm', type=int, default=1, help='whether to use normalize; True 1 False 0')
    parser.add_argument('--down_sampling_layers', type=int, default=0, help='num of down sampling layers')
    parser.add_argument('--down_sampling_window', type=int, default=1, help='down sampling window size')
    parser.add_argument('--down_sampling_method', type=str, default=None,
                        help='down sampling method, only support avg, max, conv')
    parser.add_argument('--seg_len', type=int, default=48,
                        help='the length of segmen-wise iteration of SegRNN')

    # optimization
    parser.add_argument('--num_workers', type=int, default=10, help='data loader num workers')
    parser.add_argument('--itr', type=int, default=2, help='experiments times')
    parser.add_argument('--train_epochs', type=int, default=10, help='train epochs')
    parser.add_argument('--batch_size', type=int, default=32, help='batch size of train input data')
    parser.add_argument('--patience', type=int, default=5, help='early stopping patience')
    parser.add_argument('--learning_rate', type=float, default=0.0001, help='optimizer learning rate')
    parser.add_argument('--des', type=str, default='test', help='exp description')
    parser.add_argument('--loss', type=str, default='MSE', help='loss function')
    parser.add_argument('--lradj', type=str, default='type1', help='adjust learning rate')
    parser.add_argument('--use_amp', action='store_true', help='use automatic mixed precision training', default=False)

    # GPU
    parser.add_argument('--use_gpu', type=bool, default=True, help='use gpu')
    parser.add_argument('--gpu', type=int, default=0, help='gpu')
    parser.add_argument('--use_multi_gpu', action='store_true', help='use multiple gpus', default=False)
    parser.add_argument('--devices', type=str, default='0,1,2,3', help='device ids of multile gpus')

    # de-stationary projector params
    parser.add_argument('--p_hidden_dims', type=int, nargs='+', default=[128, 128],
                        help='hidden layer dimensions of projector (List)')
    parser.add_argument('--p_hidden_layers', type=int, default=2, help='number of hidden layers in projector')

    #  new pars
    parser.add_argument('--llm_model', type=str, default='GPT2', help='LLM model') # LLAMA, GPT2, BERT
    parser.add_argument('--llm_dim', type=int, default='768', help='LLM model dimension')# LLama7b:4096; GPT2-small:768; BERT-base:768
    parser.add_argument('--llm_layers', type=int, default=6)
    parser.add_argument('--text_path', type=str, default="None")
    parser.add_argument('--type_tag', type=str, default="#F#")
    parser.add_argument('--text_len', type=int, default=3)
    parser.add_argument('--learning_rate2', type=float, default=1e-2, help='mlp learning rate')
    parser.add_argument('--learning_rate3', type=float, default=1e-3, help='proj learning rate')
    parser.add_argument('--prompt_weight', type=float, default=0.01, help='prompt weight')#please tune this hyperparameter
    parser.add_argument('--pool_type', type=str, default='avg', help='pooling type') #avg min max
    parser.add_argument('--date_name', type=str, default='end_date', help='matching date name in csv') #mlp linear
    parser.add_argument('--addHisRate', type=float, default=0.5, help='add historical rate')
    parser.add_argument('--init_method', type=str, default='normal', help='init method of combined weight')
    parser.add_argument('--learning_rate_weight', type=float, default=0.0001, help='optimizer learning rate')
    parser.add_argument('--seed', type=int, default=2021, help='random seed')
    parser.add_argument('--save_name', type=str, default='result.txt', help='save name')
    parser.add_argument('--use_fullmodel', type=int, default=0, help='use full model or just encoder')
    args = parser.parse_args()
    # domain= re.search(r'/([^/]+)$', args.root_path).group(1)
    print("now running on model {} ".format(args.model))
    if args.model=="LightTS":   
        if args.pred_len<args.seq_len:
            args.seq_len=args.pred_len
    if args.llm_model=="BERT":
        args.llm_dim=768
    elif args.llm_model=="GPT2":
        args.llm_dim=768
    elif args.llm_model=="LLAMA2":
        args.llm_dim=4096
    elif args.llm_model=="LLAMA3":
        args.llm_dim=4096
    elif args.llm_model=="GPT2M":
        args.llm_dim=1024
    elif args.llm_model=="GPT2L":
        args.llm_dim=1280
    elif args.llm_model=="GPT2XL":
        args.llm_dim=1600
    args.features = 'S'
    args.enc_in = 1
    args.dec_in = 1
    args.c_out = 1
    args.text_emb = args.pred_len
    print("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!overwrite features to 'S' for univariate time series data and dim=1")
    fix_seed = args.seed
    print("Now using seed {}".format(fix_seed))
    random.seed(fix_seed)
    torch.manual_seed(fix_seed)
    np.random.seed(fix_seed)
    # args.use_gpu = True if torch.cuda.is_available() and args.use_gpu else False
    args.use_gpu = True if torch.cuda.is_available() else False

    print(torch.cuda.is_available())

    if args.use_gpu and args.use_multi_gpu:
        args.devices = args.devices.replace(' ', '')
        device_ids = args.devices.split(',')
        args.device_ids = [int(id_) for id_ in device_ids]
        args.gpu = args.device_ids[0]

    print('Args in experiment:')
    print_args(args)

    Exp = Exp_Long_Term_Forecast

    if args.is_training:
        for ii in range(args.itr):
            # setting record of experiments
            exp = Exp(args)  # set experiments
            setting = '{}_{}_{}_sl{}_ll{}_pl{}_seed{}_{}'.format(args.model_id,
                                                    args.model,
                                                    args.data,
                                                    args.seq_len,
                                                    args.label_len,
                                                    args.pred_len,
                                                    args.seed,
                                                    ii)
            print('>>>>>>>start training : {}>>>>>>>>>>>>>>>>>>>>>>>>>>'.format(setting))
            exp.train(setting)

            if not args.train_only:
                print('>>>>>>>testing : {}<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<'.format(setting))
                exp.test(setting)

            if args.do_predict:
                print('>>>>>>>predicting : {}<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<'.format(setting))
                exp.predict(setting, True)

        torch.cuda.empty_cache()
    else:
        ii = 0
        setting = '{}_{}_{}_sl{}_ll{}_pl{}_seed{}_{}'.format(args.model_id,
                                                    args.model,
                                                    args.data,
                                                    args.seq_len,
                                                    args.label_len,
                                                    args.pred_len,
                                                    args.seed,
                                                    ii)

        exp = Exp(args)  # set experiments
        # if not args.train_only:
        #     print('>>>>>>>testing : {}<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<'.format(setting))
        #     now_mse=exp.test(setting, test=1)
        if args.do_predict:
            print('>>>>>>>predicting : {}<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<'.format(setting))
            exp.predict(setting, True)
        torch.cuda.empty_cache()
    