# ===========================================================================
# Copyright 2013 University of Limerick
#
# This file is part of DREAM.
#
# DREAM is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# DREAM is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with DREAM.  If not, see <http://www.gnu.org/licenses/>.
# ===========================================================================
'''
Created on 13 Feb 2013

@author: Ioannis
'''

'''
models a entity/job assigned operator (manager)
'''

# from SimPy.Simulation import Resource, now
import simpy
from Operator import Operator

# ===========================================================================
#                 the resource that operates the machines
# ===========================================================================
class OperatorManagedJob(Operator):
    
    def __init__(self, id, name, capacity=1,schedulingRule="FIFO",**kw):
        Operator.__init__(self,id=id,name=name,capacity=capacity,schedulingRule=schedulingRule)
        from Globals import G
        G.OperatorManagedJobsList.append(self) 
    
    # =======================================================================
    #                    checks if the worker is available
    #     it is called only by the have to dispose of a QueueManagedJob
    #                         and its sortEntities
    # =======================================================================       
    def checkIfResourceIsAvailable(self,callerObject=None):
        activeResourceQueue = self.getResourceQueue()
        workingOn=self.workingStation
        # in case the operator has the flag operatorAssigned raised 
        #    (meaning the operator will explicitly be assigned to a machine)
        #    return false even if he is free
        # If there is no callerObject defined then do not check the operatorAssignedTo variable
        if callerObject:
            # if the operator is not yet assigned then return the default behaviour
            if self.operatorAssignedTo==None:
                return len(activeResourceQueue)<self.capacity
            # otherwise, in case the operator is occupied, check if the occupier is the 
            #     object the operator is assigned to
            else:
                if len(activeResourceQueue):
#                     if self.operatorAssignedTo==activeResourceQueue[0].victim:
                    if self.operatorAssignedTo==workingOn:
                        return self.operatorAssignedTo==callerObject
                return (self.operatorAssignedTo==callerObject) and len(activeResourceQueue)<self.capacity
        # otherwise, (if the callerObject is None) check if the operator is assigned and if yes
        #     then perform the default behaviour
        else:
#             if self.operatorAssignedTo==None:
#                 return False
            return len(activeResourceQueue)<self.capacity
    
    #=======================================================================
    # findCandidateEntities method finding the candidateEntities of the operator  
    #=======================================================================
    def findCandidateEntities(self, pendingEntities=[]):
        if pendingEntities:
            for entity in [x for x in pendingEntities if x.currentStation.canDeliver(x) and x.manager==self]:
                self.candidateEntities.append(entity)
                
    #===========================================================================
    # recursive method that searches for entities with available receivers
    #===========================================================================
    def findAvailableEntity(self):
        from Globals import G
        router=G.Router
        # if the candidateEntities and the entitiesWithOccupiedReceivers lists are identical then return None 
        if len(set(self.candidateEntities).intersection(router.entitiesWithOccupiedReceivers))==len(self.candidateEntities):
            return None
        availableEntity=next(x for x in self.candidateEntities if not x in router.entitiesWithOccupiedReceivers)
        receiverAvailability=False
        if availableEntity:
            for receiver in availableEntity.candidateReceivers:
                if not receiver in router.occupiedReceivers:
                    receiverAvailability=True
                    break
            # if there are no available receivers for the entity
            if not receiverAvailability:
                router.entitiesWithOccupiedReceivers.append(availableEntity)
                return self.findAvailableEntity()
        return availableEntity
    
    #===========================================================================
    # method that finds a candidate entity for an operator
    #===========================================================================
    def findCandidateEntity(self):
        from Globals import G
        router=G.Router
        # pick a candidateEntity
        candidateEntity=self.findAvailableEntity()
        if not candidateEntity:
            candidateEntity=next(x for x in self.candidateEntities)
            router.conflictingEntities.append(candidateEntity)
        return candidateEntity