import argparse
import os
import logging, logging.handlers

def setup_logger(idx, queue, log_dir, DEBUG=False):
    """ logger setup """
    global logger
    info_log_path = os.path.join(log_dir, f"info_process_{idx}.log")
    logger = logging.getLogger(f'logger_{idx}')
    logger.setLevel(logging.DEBUG)  
    logger.propagate = False
    formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')

    # set up info handler
    info_handler = logging.FileHandler(info_log_path)  
    info_handler.setLevel(logging.INFO)  
    info_handler.setFormatter(formatter)  
    # set up error handler
    error_handler = logging.handlers.QueueHandler(queue)  
    error_handler.setLevel(logging.ERROR)  
    error_handler.setFormatter(formatter)
    # set up stream handler
    stream_handler = logging.StreamHandler()  
    stream_handler.setFormatter(formatter)  
    stream_handler.setLevel(logging.INFO)

    # add handlers
    logger.addHandler(info_handler)
    logger.addHandler(error_handler)
    logger.addHandler(stream_handler)

    if DEBUG:
        debug_log_path = os.path.join(log_dir, f"debug_{idx}.log")
        # set up debug handler
        debug_handler = logging.FileHandler(debug_log_path)
        debug_handler.setLevel(logging.DEBUG)
        debug_handler.setFormatter(formatter)
        logger.addHandler(debug_handler)
    return logger
    

class FileManager:  
    """ FileManager for multiprocessing """
    def __init__(self, files, manager):  
        self.files = files  
        self.index = manager.Value('i', 0)  # trace value
        self.lock = manager.Lock() # lock to synchronize processes  
  
    def get_next_file(self):  
        with self.lock:  
            if self.index.value >= len(self.files):  
                return None, None 
            index = self.index.value 
            file = self.files[self.index.value]  # get file  
            self.index.value += 1  # add index
            return index, file
        
    def get_index(self):
        return self.index
    

def parse_range(range_str):  
    start, end = map(int, range_str.split('-'))  
    return list(range(start, end+1)) 

def parse_list(list_str):  
    res = [int(i) for i in list_str.split(',')]  
    return res

def parse_bool(v):  
    if isinstance(v, bool):  
       return v  
    if v.lower() in ('yes', 'true', 't', 'y', '1'):  
        return True  
    elif v.lower() in ('no', 'false', 'f', 'n', '0'):  
        return False  
    else:  
        raise argparse.ArgumentTypeError('Boolean value expected.')  

def execute(s):
    """ execute str to get its value """
    import math
    from scipy.special import lambertw
    s = s.replace("^", "**").replace("sqrt", "math.sqrt")
    s = s.replace("pi", "math.pi").replace("log", "math.log")
    s = s.replace("exp", "math.exp").replace("LambertW", "lambertw")
    s = s.replace("asin", "math.asin").replace("acos", "math.acos")
    s = s.replace("sin", "math.sin").replace("cos", "math.cos")
    return eval(s)
    
import re

def parse_answer(sol):
    """ parse the answer from the solution """
    match = re.search(r'####(.*)', sol)  
    if match:
        return match.group(1)
    else:
        return 0

def is_equiv(answer, solution):
    """ check whether the answer is equivalent to the solution """
    try:
        if eval(f"abs({answer.replace(',','')} - {solution.replace(',','')})") < 1e-5:
            return True
        else:
            return False
    except (SyntaxError, TypeError, NameError):
        return False