# coding: utf-8

from __future__ import unicode_literals

import sys

import six

from django.utils.encoding import force_bytes, force_text
from py.code import Source, Traceback

try:
    from serializable import serialize
    from workflow_constants import WORKFLOW_CONTAINER_RESPONSE
    from workflow_exceptions import WorkflowError, WorkflowSyntaxError, Return
except ImportError:
    from idm.core.constants.workflow import WORKFLOW_CONTAINER_RESPONSE
    from idm.core.workflow.exceptions import WorkflowError, WorkflowSyntaxError, Return
    from idm.core.workflow.sandbox.serializable import serialize


class ContainerWorkflowExecutor(object):
    def __init__(self, code, context):
        if six.PY2:
            self.code = force_bytes(code)
            if b'coding:' not in self.code:
                self.code = b'# coding: utf-8\n' + self.code

            self.code = self.code.replace(b'\r', b'').strip()
        else:
            self.code = force_text(code)
            self.code = self.code.replace('\r', '').strip()

        self.source = Source(self.code)
        self.code_object = None
        self.context = context

    def compile(self):
        if not self.code_object:
            self.code_object = self.source.compile()

    def serialize_exception(self, skip_context=False):
        _, exception, tb = sys.exc_info()
        traceback = Traceback(tb)
        error_source = traceback.getcrashentry()
        line = force_text(error_source.statement)
        lineno = error_source.lineno
        return serialize(WorkflowSyntaxError(
            exception, line, lineno,
            {} if skip_context else error_source.locals,
            {} if skip_context else self.context
        ))

    def run(self):
        try:
            from runner import runner
        except ImportError:
            from .runner import runner

        try:
            self.compile()
        except SyntaxError as e:
            runner.connection.send_data(
                WORKFLOW_CONTAINER_RESPONSE.ERROR,
                self.serialize_exception(skip_context=True)
            )
            return

        context = self.context.flatten()
        try:
            exec(self.code_object, context)
        except Return:
            pass
        except WorkflowError as e:
            runner.connection.send_data(
                WORKFLOW_CONTAINER_RESPONSE.ERROR,
                serialize(e)
            )
        except Exception:
            runner.connection.send_data(
                WORKFLOW_CONTAINER_RESPONSE.ERROR,
                self.serialize_exception()
            )

        self.context.update(context)
