"""
work queue specification

"""

import re
import types

from six import iteritems

from pandaserver.taskbuffer.GlobalShares import Share

RESOURCE = 'Resource'
ACTIVE_FUNCTIONS = [RESOURCE]


class WorkQueue(object):
    # attributes
    _attributes = ('queue_id', 'queue_name', 'queue_type', 'VO', 'queue_share', 'queue_order',
                   'criteria', 'variables', 'partitionID', 'stretchable', 'status', 'queue_function')

    # parameters for selection criteria
    _paramsForSelection = ('prodSourceLabel', 'workingGroup', 'processingType', 'coreCount',
                           'site', 'eventService', 'splitRule', 'campaign')

    # correspondence with Global Shares attributes and parameters
    _attributes_gs_conversion_dic = {'name': 'queue_name',
                                     'value': 'queue_share',
                                     'prodsourcelabel': 'queue_type',
                                     'queue_id': 'queue_id',
                                     'vo': 'VO'}

    _params_gs_conversion_dic = {'prodsourcelabel': 'prodSourceLabel',
                                 'workinggroup': 'workingGroup',
                                 'campaign': 'campaign',
                                 'processingtype': 'processingType'}

    def __init__(self):
        """
        Constructor
        """
        # install attributes
        for attr in self._attributes:
            setattr(self, attr, None)

        # global share is by default false
        self.is_global_share = False
        # throttled is set to True by default. Some Global Shares will overwrite it to False
        self.throttled = True

    def __str__(self):
        """
        String representation of a workqueue
        :return: string with the representation of the work queue
        """
        return str(self.queue_name)

    def dump(self):
        """
        Creates a human-friendly string with the work queue information
        :return: string representation of the work queue
        """
        dump_str = 'id:{0} order:{1} name:{2} share:{3} '.format(self.queue_id, self.queue_order,
                                                                 self.queue_name, self.queue_share)

        # normal queue
        if self.is_global_share:
            dump_str += 'gs_name:{0} (global share)'.format(self.queue_name)
        else:
            dump_str += 'criteria:{0} var:{1} eval:{2}'.format(self.criteria, str(self.variables), self.evalString)

        return dump_str

    def getID(self):
        """
        get ID
        :return: returns a list with the ID of the work queue
        """
        return self.queue_id

    def pack(self, values):
        """
        Packs tuple into the object
        :param values: list with the values in the order declared in the attributes section
        :return: nothing
        """
        for i, attr in enumerate(self._attributes):
            val = values[i]
            setattr(self, attr, val)

        # disallow negative share
        if self.queue_share is not None and self.queue_share < 0:
            self.queue_share = 0

        # convert variables string to a map of bind-variables
        tmp_map = {}
        try:
            for item in self.variables.split(','):
                # look for key: value
                item = item.strip()
                items = item.split(':')
                if len(items) != 2:
                    continue
                # add
                tmp_map[':%s' % items[0]] = items[1]
        except Exception:
            pass
        # assign map
        self.variables = tmp_map
        # make a python statement for eval
        if self.criteria in ['', None]:
            # catch all
            self.evalString = 'True'
        else:
            tmp_eval_str = self.criteria
            # replace IN/OR/AND to in/or/and
            tmp_eval_str = re.sub(' IN ', ' in ', tmp_eval_str, re.I)
            tmp_eval_str = re.sub(' OR ', ' or ', tmp_eval_str, re.I)
            tmp_eval_str = re.sub(' AND ', ' and ', tmp_eval_str, re.I)
            # replace = to ==
            tmp_eval_str = tmp_eval_str.replace('=', '==')
            # replace LIKE
            tmp_eval_str = re.sub('(?P<var>[^ \(]+)\s+LIKE\s+(?P<pat>[^ \(]+)',
                                  "re.search(\g<pat>,\g<var>,re.I) is not None",
                                  tmp_eval_str, re.I)
            # NULL
            tmp_eval_str = re.sub(' IS NULL', '==None', tmp_eval_str)
            tmp_eval_str = re.sub(' IS NOT NULL', "!=None", tmp_eval_str)
            # replace NOT to not
            tmp_eval_str = re.sub(' NOT ', ' not ', tmp_eval_str, re.I)
            # fomat cases
            for tmp_param in self._paramsForSelection:
                tmp_eval_str = re.sub(tmp_param, tmp_param, tmp_eval_str, re.I)
            # replace bind-variables
            for tmp_key, tmp_val in iteritems(self.variables):
                if '%' in tmp_val:
                    # wildcard
                    tmp_val = tmp_val.replace('%', '.*')
                    tmp_val = "'^%s$'" % tmp_val
                else:
                    # normal variable
                    tmp_val = "'%s'" % tmp_val
                tmp_eval_str = tmp_eval_str.replace(tmp_key, tmp_val)
            # assign
            self.evalString = tmp_eval_str

    def pack_gs(self, gshare):
        """
        Packs tuple into the object
        :param gshare: global share
        :return: nothing
        """

        # the object becomes a global share wq
        self.is_global_share = True
        try:
            tmp_map = {}
            for i, attr in enumerate(gshare._attributes):
                # global share attributes can be mapped to a wq attribute(1), to a wq param(2), or to none of both
                # 1. if the gs attribute is mapped to a wq attribute, do a get and a set
                if attr in self._attributes_gs_conversion_dic:
                    attr_wq = self._attributes_gs_conversion_dic[attr]
                    val = getattr(gshare, attr)
                    setattr(self, attr_wq, val)
                # 2. if the gs attribute is mapped to a wq param, add it to the bind variables dictionary
                # Probably we don't need this, we just care about matching the gs name
                if attr in self._params_gs_conversion_dic:
                    param_wq = self._params_gs_conversion_dic[attr]
                    val = getattr(gshare, attr)
                    tmp_map[':{0}'.format(param_wq)] = val

                # 3. Special case for throttled. This is defined additionally, since it's not present in WQs
                if attr == 'throttled' and gshare.throttled == 'N':
                    self.throttled = False

            self.variables = tmp_map
        except Exception:
            pass

    # evaluate in python
    def evaluate(self, param_map):
        # only active queues are evaluated
        if self.isActive():
            # normal queue
            # expand parameters to local namespace
            for tmp_param_key, tmp_param_val in iteritems(param_map):
                if isinstance(tmp_param_val, str):
                    # add quotes for string
                    exec('{0}="{1}"'.format(tmp_param_key, tmp_param_val), globals())
                else:
                    exec('{0}={1}'.format(tmp_param_key, tmp_param_val), globals())
            # add default parameters if missing
            for tmp_param in self._paramsForSelection:
                if tmp_param not in param_map:
                    exec('{0}=None'.format(tmp_param), globals())
            # evaluate
            exec("ret_var = {0}".format(self.evalString), globals())
            return self, ret_var

        # return False
        return self, False

    # check if active
    def isActive(self):
        if self.status != 'inactive':  # and self.queue_function in ACTIVE_FUNCTIONS:
            return True
        return False

    # check if its eligible after global share alignment
    def isAligned(self):
        if self.queue_function == RESOURCE or self.is_global_share:
            return True
        return False

    # check if there is workload
    def hasWorkload(self, queue_list):
        # inactive
        if not self.isActive():
            return False

        if self.queue_id in queue_list:
            return True

        # not found
        return False

    # return column names for INSERT
    def column_names(cls):
        ret = ""
        for attr in cls._attributes:
            if ret != "":
                ret += ','
            ret += attr
        return ret
    column_names = classmethod(column_names)
