import threading
import io
import sys
import os
import os.path
import traceback
import ast
from damaris.data import Resultable
from damaris.gui.ExperimentHandling import StopExperiment

class ResultHandling(threading.Thread):
    """
    runs the result script in sandbox
    """

    def __init__(self, script_data, result_iterator, data_pool):
        threading.Thread.__init__(self,name="result handler")
        self.script=script_data
        self.results=result_iterator
        self.data_space=data_pool
        self.quit_flag=self.results.quit_flag
        if self.data_space is not None:
            self.data_space["__recentresult"]=-1

    def run(self):
        # execute it
        dataspace={}
        data_classes = __import__('damaris.data', dataspace, dataspace, ['*'])
        for name in dir(data_classes):
            if name[:2]=="__" and name[-2:]=="__": continue
            dataspace[name]=data_classes.__dict__[name]
        del data_classes
        dataspace["results"]=self
        dataspace["data"]=self.data_space
        dataspace["sleep"]=self.sleep
        self.raised_exception=None
        self.location = None

        # check for time.sleep()
        try:
            tree = ast.parse(self.script)
            for node in ast.walk(tree):
                found_blocking_sleep = False
                if isinstance(node, ast.Call):
                    func = node.func
                    if isinstance(func, ast.Attribute) and func.attr == 'sleep':
                        if isinstance(func.value, ast.Name) and func.value.id == 'time':
                            found_blocking_sleep = True
                elif isinstance(node, ast.ImportFrom) and node.module == 'time':
                    for alias in node.names:
                        if alias.name == 'sleep':
                            found_blocking_sleep = True
                
                if found_blocking_sleep:
                    print("Warning: time.sleep() detected in result script. This is blocking and not interruptible. Please use sleep() instead.")
                    break
        except Exception:
            pass

        try:
            exec(self.script, dataspace)
        except StopExperiment:
            return
        except Exception as e:
            self.raised_exception=e
            self.location=traceback.extract_tb(sys.exc_info()[2])[-1][1:3]
            traceback_file=io.StringIO()
            traceback.print_tb(sys.exc_info()[2], None, traceback_file)
            self.traceback=traceback_file.getvalue()
            traceback_file=None
            return
        if not "result" in dataspace:
            dataspace=None
            return
        try:
            dataspace["result"]()
        except StopExperiment:
            pass
        except Exception as e:
            self.raised_exception=e
            self.location=traceback.extract_tb(sys.exc_info()[2])[-1][1:3]
            traceback_file=io.StringIO()
            traceback.print_tb(sys.exc_info()[2], None, traceback_file)
            self.traceback=traceback_file.getvalue()
            traceback_file=None
            dataspace=None

    def sleep(self, seconds):
        self.quit_flag.wait(seconds)
        if self.quit_flag.isSet():
            raise StopExperiment

    def __iter__(self):
        if self.quit_flag.isSet():
            self.results=None
            return
        for i in self.results:
            if hasattr(self.results, "in_advance"):
                self.data_space["__resultsinadvance"]=self.results.in_advance
            if self.quit_flag.isSet():
                self.results=None
                return
            if isinstance(i, Resultable.Resultable):
                if self.data_space is not None:
                    self.data_space["__recentresult"]=i.job_id+0
            yield i
            if self.quit_flag.isSet():
                self.results=None
                return

    def stop(self):
        self.quit_flag.set()
