import pyCreeper;
import copy;
import icrfHelper;
import constants;
import icrfAnalyser;

from helpers import dataFormatHelpers;

def getRobotWorksiteStateDataForFootbots(filePath_, maxTime_, sitesProduceReward_):
    infoEventsData = pyCreeper.crFiles.fileToArray(filePath_,"\t", False);

    infoEventTimes = pyCreeper.crData.getListColumnAsArray(infoEventsData, 0);

    sitesCompleted = [[] for i in range(maxTime_)];
    sitesSubscribedRobots = [[] for i in range(maxTime_)]
    sitesSubscribedRobotsByRecruitment = [[] for i in range(maxTime_)]

    sitesReachedRobots = [[] for i in range(maxTime_)]
    sitesLadenRobots = [[] for i in range(maxTime_)]
    sitesEarningRobots = [[] for i in range(maxTime_)]
    sitesRobotsToAbandon = [[] for i in range(maxTime_)]

    sitesInitialVolumes = [];
    sitesInitialValues = [];

    numOfSites = [0 for i in range(maxTime_)];
    totalEnvReward = [0 for i in range(maxTime_)];
    actualReward = [0 for i in range(maxTime_)];

    robotIds = []; # an array of known robot ids - robots might be numbered arbitrarily
    robotCurrentTaskId = []; # an array, indexed by robotIndex from robotIds[] with site ids where each robot works
    brokenRobotIds = [[] for i in range(maxTime_)] # an array of robots that broke and should be ignored



    printEvents = False;
    printTaskInfo =  False;
    printRewards = False;


    tasksSubscribedRobotsOldRecords = []; #used in PERFORM, to keep track of robots that were subscribed, because they need to be automatically unsubscribed but checked later
    numOfRobots = dataFormatHelpers.getNumOfRobotsForExperiment(filePath_);

    robotIds = range(numOfRobots);
    robotsTaskSubscriptions = [ [] for nr in range(numOfRobots)];
    tasksVolumesForCalcGains = [];


    useRobotsEarningFromTaskId = False;
    if ("COLLECT" in filePath_):
        useRobotsEarningFromTaskId = True;


    #--
    #-- resolve each event to sort robots into various arrays, then calculate various costs for this time step
    #--
    for t in range(maxTime_):
        #print("-------------------- t={}".format(t));

        #-- maintain actual received reward from previous time step
        if (t > 0):

            numOfSites[t] = numOfSites[t-1];
            totalEnvReward[t] = totalEnvReward[t-1];
            actualReward[t] = actualReward[t-1]

            sitesCompleted[t] = copy.deepcopy(sitesCompleted[t-1]);
            sitesSubscribedRobots[t] = copy.deepcopy(sitesSubscribedRobots[t-1]);
            sitesSubscribedRobotsByRecruitment[t] = copy.deepcopy(sitesSubscribedRobotsByRecruitment[t-1]);
            sitesReachedRobots[t] = copy.deepcopy(sitesReachedRobots[t-1]);
            sitesLadenRobots[t] = copy.deepcopy(sitesLadenRobots[t-1]);
            sitesEarningRobots[t] = copy.deepcopy(sitesEarningRobots[t-1]);
            sitesRobotsToAbandon[t] = copy.deepcopy(sitesRobotsToAbandon[t-1]);
            brokenRobotIds[t] = copy.deepcopy(brokenRobotIds[t-1]);


        if (t in infoEventTimes):
            #print("      t={}".format(t))
            #-- there was an event at this time
            for loadingTimeDataIndex, timeValue in enumerate(infoEventTimes):
                if (timeValue == t):
                    #print("      resolve t ={}".format(timeValue))
                    eventType = infoEventsData[loadingTimeDataIndex][1];
                    taskId = int(infoEventsData[loadingTimeDataIndex][3]);
                    robotId = int(infoEventsData[loadingTimeDataIndex][2]);

                    if (taskId >= 0):

                        taskValue = float(infoEventsData[loadingTimeDataIndex][4]);
                        taskVolume = int(round(infoEventsData[loadingTimeDataIndex][5]));

                        if (printEvents and robotId == 4):
                            print(str(timeValue) + "  " + eventType + "  rob " + str(robotId) + " taskId " + str(taskId));

                        if (eventType == 'E_TASK_ADDED'):
                            numOfSites[t] += 1;

                            taskReward = taskValue * taskVolume;

                            #-- maintain various lists associated with sites
                            sitesSubscribedRobots[t].append([]);
                            sitesSubscribedRobotsByRecruitment[t].append([]);
                            sitesReachedRobots[t].append([]);
                            sitesEarningRobots[t].append([]);
                            sitesLadenRobots[t].append([]);
                            sitesRobotsToAbandon[t].append([]);

                            sitesInitialVolumes.append(taskVolume);
                            sitesInitialValues.append(taskValue);

                            totalEnvReward[t] += taskReward;

                            if (printEvents):
                                print("t=" + str(t) + " ==========================[]====== NEW SITE vol=" + str(taskVolume) + " val " + str(taskValue) + "  Nt=" + str(numOfSites[t]) + " TOTAL env R " + str(totalEnvReward[t]));


                            tasksSubscribedRobotsOldRecords.append([]);
                            tasksVolumesForCalcGains.append(taskVolume);


                        if (eventType == 'E_TASK_COMPLETED' or eventType == 'E_TASK_DESTROYED'):

                            if (taskId not in sitesCompleted[t]):  # make sure this only happens once, either when site is completed by robots or destroyed by the environment

                                sitesCompleted[t].append(taskId);
                                siteReward = sitesInitialValues[taskId] * sitesInitialVolumes[taskId];
                                totalEnvReward[t] -= siteReward;

                                if (printEvents):
                                    print("t=" + str(t) + " ============================[]===== SITE DONE " + str(taskId) + " TOTAL env R " + str(totalEnvReward[t]) + " completed sites:" + str(sitesCompleted[t]));

                            #-- if robot stay at sites to get reward, clear various arrays after site deleted, assuming that not all events would be correctly recorded
                            if (sitesProduceReward_):
                                for robotId in sitesReachedRobots[t][taskId]:
                                    if (robotId in sitesSubscribedRobots[t][taskId]):
                                        sitesSubscribedRobots[t][taskId].remove(robotId);
                                        tasksSubscribedRobotsOldRecords[taskId].append(robotId);
                                    if (robotId in sitesSubscribedRobotsByRecruitment[t][taskId]):
                                        sitesSubscribedRobotsByRecruitment[t][taskId].remove(robotId);
                                    if (robotId in sitesRobotsToAbandon[t][taskId]):
                                        sitesRobotsToAbandon[t][taskId].remove(robotId);

                                    pyCreeper.crData.removeElementFromList(taskId, robotsTaskSubscriptions[robotId]);

                                    if (printEvents):
                                        print("     automatically unsibscribed robot " + str(robotId) + " from task " + str(taskId));
                                sitesReachedRobots[t][taskId] = [];
                                sitesEarningRobots[t][taskId] = [];
                                sitesLadenRobots[t][taskId] = [];

                        #-- the task value should be read from the info that was recorded when task was created, not from the robot
                        if (taskId < len(sitesInitialValues)):
                            taskValue = sitesInitialValues[taskId];
                        else:
                            taskId = handleTaskValueNotRecorded(t, robotId, numOfSites[t], taskId, eventType, sitesSubscribedRobots[t], sitesReachedRobots[t], sitesLadenRobots[t], tasksSubscribedRobotsOldRecords)

                        #-- keep track of robots that know about a task
                        if (eventType == 'TASK_SCOUTED' or eventType == 'ROBOT_RECRUITED'):

                            debugMessage = "t=" + str(t) + " robot " + str(robotId) + " subscribed to task " + str(taskId) + "  ";
                            pyCreeper.crData.addUniqueElementToList(robotId, sitesSubscribedRobots[t][taskId], debugMessage, printEvents)

                            if (eventType == 'ROBOT_RECRUITED'):
                                debugMessage = "t=" + str(t) + " robot " + str(robotId) + " recruited to task " + str(taskId) + "  ";
                                pyCreeper.crData.addUniqueElementToList(robotId, sitesSubscribedRobotsByRecruitment[t][taskId], debugMessage, printEvents)

                            clearAllRobotToAbandonRecords(filePath_, taskId, robotId, numOfSites[t], sitesRobotsToAbandon[t], sitesSubscribedRobots[t], sitesSubscribedRobotsByRecruitment[t], sitesReachedRobots[t], sitesLadenRobots[t], sitesEarningRobots[t], robotsTaskSubscriptions, printEvents, useRobotsEarningFromTaskId);

                            # check if robot is subscribed to other worksites
                            cleanupRecordsAfterSubscription(taskId, robotId, sitesSubscribedRobots[t], sitesSubscribedRobotsByRecruitment[t], sitesReachedRobots[t], sitesLadenRobots[t], sitesEarningRobots[t], robotsTaskSubscriptions, printTaskInfo, useRobotsEarningFromTaskId);

                            #-- if scouted, and solitary or local broadcasters, start processing immediatelly
                            if (eventType == 'TASK_SCOUTED' and ( ('solitary' in filePath_) or ('localBroadc' in filePath_))):
                                #-- task reached
                                pyCreeper.crData.addUniqueElementToList(robotId, sitesReachedRobots[t][taskId]);
                                pyCreeper.crData.addUniqueElementToList(robotId, sitesLadenRobots[t][taskId]);
                                #-- task started
                                if (not taskId in sitesCompleted[t]): # sometimes robot abandons a completed task and then restarts and ends it in the same time frame - make sure it's not counted as starting a task.
                                    pyCreeper.crData.addUniqueElementToList(robotId, sitesReachedRobots[t][taskId]);
                                    pyCreeper.crData.addUniqueElementToList(robotId, sitesLadenRobots[t][taskId]);

                                if (printEvents):
                                    print("t=" + str(t) + " robot " + str(robotId) + " started task " + str(taskId) + "  ");

                                clearAllRobotToAbandonRecords(filePath_, taskId, robotId, numOfSites[t], sitesRobotsToAbandon[t], sitesSubscribedRobots[t], sitesSubscribedRobotsByRecruitment[t], sitesReachedRobots[t], sitesLadenRobots[t], sitesEarningRobots[t], robotsTaskSubscriptions, printEvents, useRobotsEarningFromTaskId);

                                #-- reward started
                                if ("PERFORM" in filePath_):
                                    outputStr = "t=" + str(t) + " rew started for robot " + str(robotId) + " task " + str(taskId);
                                    pyCreeper.crData.addUniqueElementToList(robotId, sitesEarningRobots[t][taskId], outputStr, printEvents)



                        #-- keep track of robots that reached the task
                        if (eventType == 'TASK_REACHED'):
                            pyCreeper.crData.addUniqueElementToList(robotId, sitesReachedRobots[t][taskId]);
                            pyCreeper.crData.addUniqueElementToList(robotId, sitesLadenRobots[t][taskId]);


                        if (eventType == 'TASK_PAUSED'):
                            pass

                        #-- keep track of robots that started doing a task
                        if (eventType == 'TASK_STARTED'):
                            if (not taskId in sitesCompleted[t]): # sometimes robot abandons a completed task and then restarts and ends it in the same time frame - make sure it's not counted as starting a task.

                                pyCreeper.crData.addUniqueElementToList(robotId, sitesReachedRobots[t][taskId]);
                                pyCreeper.crData.addUniqueElementToList(robotId, sitesLadenRobots[t][taskId]);

                                if (printEvents):
                                    print("t=" + str(t) + " robot " + str(robotId) + " started task " + str(taskId) + "  ");

                                #-- make sure counted as subscribed - e.g. robot could have started a task that meanwhile got a new ID, but did not note down a SCOUTED event
                                pyCreeper.crData.addUniqueElementToList(robotId, sitesSubscribedRobots[t][taskId])
                                # check if robot is subscribed to other worksites
                                cleanupRecordsAfterSubscription(taskId, robotId, sitesSubscribedRobots[t], sitesSubscribedRobotsByRecruitment[t], sitesReachedRobots[t], sitesLadenRobots[t], sitesEarningRobots[t], robotsTaskSubscriptions, printTaskInfo, useRobotsEarningFromTaskId);

                                #-- make sure that if it's solitary or local broadcasters, it is counted as earning (sometimes robots fail to report a scouting event in PERFORM)
                                if ("PERFORM" in filePath_) and ('solitary' in filePath_ or 'localBroadc' in filePath_):
                                    outputStr = "t=" + str(t) + " rew automatically started for robot " + str(robotId) + " task " + str(taskId);
                                    pyCreeper.crData.addUniqueElementToList(robotId, sitesEarningRobots[t][taskId], outputStr, printEvents);

                            clearAllRobotToAbandonRecords(filePath_, taskId, robotId, numOfSites[t], sitesRobotsToAbandon[t], sitesSubscribedRobots[t], sitesSubscribedRobotsByRecruitment[t], sitesReachedRobots[t], sitesLadenRobots[t], sitesEarningRobots[t], robotsTaskSubscriptions, printEvents, useRobotsEarningFromTaskId);

                        #-- robots that are earning reward
                        if (eventType == 'REWARD_STARTED'):
                            if (not taskId in sitesCompleted[t] or useRobotsEarningFromTaskId ): # sometimes robot abandons a completed task and then restarts and ends it in the same time frame - make sure it's not counted as starting a task.
                                outputStr = "t=" + str(t) + " rew started for robot " + str(robotId) + " task " + str(taskId);
                                pyCreeper.crData.addUniqueElementToList(robotId, sitesLadenRobots[t][taskId]);
                                pyCreeper.crData.addUniqueElementToList(robotId, sitesEarningRobots[t][taskId], outputStr, printEvents)

                                #-- make sure counted as subscribed - e.g. robot could have started a task that meanwhile got a new ID, but did not note down a SCOUTED event
                                pyCreeper.crData.addUniqueElementToList(robotId, sitesSubscribedRobots[t][taskId])
                                # check if robot is subscribed to other worksites
                                cleanupRecordsAfterSubscription(taskId, robotId, sitesSubscribedRobots[t], sitesSubscribedRobotsByRecruitment[t], sitesReachedRobots[t], sitesLadenRobots[t], sitesEarningRobots[t], robotsTaskSubscriptions, printTaskInfo, useRobotsEarningFromTaskId);



                        if (eventType == 'REWARD_FINISHED'):
                            debugMessage = "t=" + str(t) + " robot " + str(robotId) + " reward finished from task " + str(taskId) + "   " + str(sitesEarningRobots[t][taskId]);
                            pyCreeper.crData.removeElementFromList(robotId, sitesEarningRobots[t][taskId], debugMessage, printEvents )
                            pyCreeper.crData.removeElementFromList(robotId, sitesLadenRobots[t][taskId]);

                            #-- unsubscribe the robot if it was set to abandon the worksite after it unloaded cart
                            if (robotId in sitesRobotsToAbandon[t][taskId]):
                                if (printEvents):
                                    print("t=" + str(t) + " robot " + str(robotId) + " unsubscribed from task " + str(taskId) + " AFTER EMPTYING CART ");

                            clearRobotToAbandonRecordsForTask(robotId, taskId, sitesRobotsToAbandon[t], sitesSubscribedRobots[t], sitesSubscribedRobotsByRecruitment[t], sitesReachedRobots[t], robotsTaskSubscriptions)

                        #-- abandonment
                        if (eventType == 'TASK_ABANDONED' or eventType == 'TASK_MISSING'):
                            if (robotId in sitesSubscribedRobots[t][taskId]):
                                if not (robotId in sitesLadenRobots[t][taskId]):
                                    #-- robot has nothing in the card, i.e. it didn't reach the worksite yet. Abandon straight away.
                                    debugMessage = "t=" + str(t) + " robot " + str(robotId) + " unsubscribed from task " + str(taskId) + " BEFORE EMPTYING CART ";
                                    pyCreeper.crData.removeElementFromList(robotId, sitesSubscribedRobots[t][taskId], debugMessage, printEvents);
                                    pyCreeper.crData.removeElementFromList(robotId, sitesSubscribedRobotsByRecruitment[t][taskId]);
                                    pyCreeper.crData.removeElementFromList(robotId, sitesReachedRobots[t][taskId]);

                                    pyCreeper.crData.removeElementFromList(taskId, robotsTaskSubscriptions[robotId])

                                else:
                                    #-- robot still needs to return the load, remember it should be counted as abandoned after it's unloaded
                                    debugMessage = "t=" + str(t) + " robot " + str(robotId) + " WILL unsubscribe from task " + str(taskId);
                                    pyCreeper.crData.addUniqueElementToList(robotId, sitesRobotsToAbandon[t][taskId], debugMessage, printEvents )


                        if (eventType == 'TASK_SWITCHED'):
                            raise ValueError(" ERROR: TASK SWITCHED EVENT FOUND");




        else:
            #-- there was no info even for this time
            pass

        #-- calculate reward
        rewardGainRate = dataFormatHelpers.getRewardGainRateForExperiment(filePath_);
        for taskId in range(numOfSites[t]):
            calculatedRewardGain = 0;
            robotsThatCantGetReward = [];
            for r in range(len(sitesEarningRobots[t][taskId])):
                if (tasksVolumesForCalcGains[taskId] > 0):
                    calculatedRewardGain += rewardGainRate*taskValue;
                    tasksVolumesForCalcGains[taskId] -= rewardGainRate;
                else:
                    robotsThatCantGetReward.append(sitesEarningRobots[t][taskId][r])

            #if (t > 6475 and t < 6479):
            #    print("$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$")
            #    print(tasksVolumesForCalcGains[taskId]);

            if (len(robotsThatCantGetReward) > 0):
                if (printRewards):
                    print("!!!! reward stopped mid second for robots {}".format(robotsThatCantGetReward))

                if (not sitesProduceReward_):
                    for r in range(len(robotsThatCantGetReward)):

                        robotId = robotsThatCantGetReward[r]

                        if (printTaskInfo):
                            print(" cleaning states for robotId {}".format(robotId));

                        pyCreeper.crData.removeElementFromList(robotId, sitesEarningRobots[t][taskId])
                        pyCreeper.crData.removeElementFromList(robotId, sitesLadenRobots[t][taskId])

                        #-- unsubscribe the robot if it was set to abandon the worksite after it unloaded cart
                        if (robotId in sitesRobotsToAbandon[t][taskId]):
                            if (printEvents):
                                print("t=" + str(t) + " robot " + str(robotId) + " unsubscribed from task " + str(taskId) + " WITHOUT EMPTYING CART - nothing in cart due to insufficient site volume ");

                        clearRobotToAbandonRecordsForTask(robotId, taskId, sitesRobotsToAbandon[t], sitesSubscribedRobots[t], sitesSubscribedRobotsByRecruitment[t], sitesReachedRobots[t], robotsTaskSubscriptions)


            actualReward[t] += calculatedRewardGain;
        #if (calculatedRewardGain > 0):
        #    print(" reward received for task {} taskVal {} rho={} dR={}".format(taskId, taskValue, rewardGainRate_, calculatedRewardGain))
        if (printTaskInfo and t>0 and t in infoEventTimes):
            if (True):
                #if (len(sitesCompleted[t][taskId]) != len(sitesCompleted[t-1][taskId]) or len(sitesSubscribedRobots[t][taskId]) != len(sitesSubscribedRobots[t-1][taskId]) or len(sitesReachedRobots[t][taskId]) != len(sitesReachedRobots[t-1][taskId]) or len(sitesEarningRobots[t][taskId]) != len(sitesEarningRobots[t-1][taskId]) or len(sitesLadenRobots[t][taskId]) != len(sitesLadenRobots[t-1][taskId]) or actualReward[t] != actualReward[t-1]):
                print("   COMPLETED TASKS: " + str(sorted(sitesCompleted[t])));
                print("   SUB:             " + str(icrfHelper.getTasksInfoForPrinting(sitesSubscribedRobots[t])));
                print("   REACHED:         " + str(icrfHelper.getTasksInfoForPrinting(sitesReachedRobots[t])));
                print("   EARN :           " + str(icrfHelper.getTasksInfoForPrinting(sitesEarningRobots[t])));
                print("   IN CART :        " + str(icrfHelper.getTasksInfoForPrinting(sitesLadenRobots[t])));
                print("   reward :        " + str(actualReward[t]));

    return([
        sitesInitialVolumes, sitesInitialValues,                        #0-1
        numOfSites, totalEnvReward, actualReward,                       #2-4
        sitesCompleted,                                                 #5
        sitesSubscribedRobots, sitesSubscribedRobotsByRecruitment,      #6-7
        sitesReachedRobots, sitesLadenRobots, sitesEarningRobots,        #8-10
        brokenRobotIds,robotIds                                          #11-12

    ])



def cleanInfoEventsData(infoEventsData_, isForaging_, debug_=False):
    """
    Get rid of rows of data that do not make sense, e.g. repeated scouting / abandonment etc.
    :param infoEventsData_:
    :return: cleaned array. in the same format as infoEventsData_
    """

    if (debug_):
        print(">>> cleaning info events data");

    returnData = [];
    shouldRemoveNextRow = False;
    lastRewardStartedEventRow = -1;
    lastSiteReachedEventRow = -1;
    lastSiteAbandonedEventRow = -1;
    for row in range(len(infoEventsData_)):
        isRowValid = True;

        time, eventType, siteId, robotId = icrfHelper.getInfoEventDataFromRow(infoEventsData_[row]);
        nextTime = -1;
        nextEventType = -1;
        nextSiteId = -1;
        nextRobotId = -1;
        prevTime = -1;
        prevEventType = -1;
        prevSiteId = -1;
        prevRobotId = -1;
        if (row < (len(infoEventsData_)-1)):
            nextTime, nextEventType, nextSiteId, nextRobotId = icrfHelper.getInfoEventDataFromRow(infoEventsData_[row+1]);
        if (row > 0):
            prevTime, prevEventType, prevSiteId, prevRobotId = icrfHelper.getInfoEventDataFromRow(infoEventsData_[row-1]);

        #-- first check if it is already known that the row should be removed
        if (shouldRemoveNextRow):
            isRowValid = False;
            shouldRemoveNextRow = False;

        rowToAdd = [];

        #-- keep track of last encountered events
        #if (eventType == dataFormatHelpers.getEventDescriptionForType(constants.E_REWARD_STARTED)):
        #    lastRewardStartedEventRow = row;
        #elif (eventType == dataFormatHelpers.getEventDescriptionForType(constants.E_SITE_REACHED)):
        #    lastSiteReachedEventRow = row;
        #elif (eventType == dataFormatHelpers.getEventDescriptionForType(constants.E_SITE_ABANDONED)):
        #    lastSiteAbandonedEventRow = row;


        #-- check that during foraging, reward started and reward finished are 1 second apart
        if (isForaging_):
            if (eventType == dataFormatHelpers.getEventDescriptionForType(constants.E_REWARD_STARTED)):
                if ( not (nextEventType == dataFormatHelpers.getEventDescriptionForType(constants.E_REWARD_FINISHED) and nextRobotId == robotId)):
                    rowToAdd = infoEventsData_[row].copy();
                    rowToAdd[icrfAnalyser.EVENTS_RECORD_EVENT_TYPE_COLUMN] = dataFormatHelpers.getEventDescriptionForType(constants.E_REWARD_FINISHED);
                    rowToAdd[icrfAnalyser.EVENTS_RECORD_TIME_COLUMN] = time + 1;

        #-- check that reward started and finished are at least 1 second apart
        if (eventType == dataFormatHelpers.getEventDescriptionForType(constants.E_REWARD_FINISHED)):
            if (siteId == prevSiteId and time == prevTime and robotId == prevRobotId):
                infoEventsData_[row][icrfAnalyser.EVENTS_RECORD_TIME_COLUMN] += 1;
                if (debug_):
                    print("   adjusting reward finished time from {} to {} for row {}".format(infoEventsData_[row-1][icrfAnalyser.EVENTS_RECORD_TIME_COLUMN],infoEventsData_[row][icrfAnalyser.EVENTS_RECORD_TIME_COLUMN],infoEventsData_[row]))


        #-- include this row if valid
        if (isRowValid):
            returnData.append(infoEventsData_[row]);
        else:
            if (debug_):
                print ("   removing row {}: {} ".format(row, infoEventsData_[row]))

        if (len(rowToAdd) > 0):
            returnData.append(rowToAdd)
            if (debug_):
                print("   appending reward finished event {}".format(rowToAdd));

    return returnData;



#======================================================================================================
#=============================== INFO EVENTS ANALYSIS DATA SPECIFIC ===================================
#======================================================================================================

def clearAllRobotToAbandonRecords(scenario_, currentTaskId, robotId, numOfTasks, tasksRobotsToAbandon, tasksSubscribedRobots, tasksSubscribedRobotsByRecruitment, tasksReachedRobots, tasksLadenRobots, tasksEarningRobots, robotsTaskSubscriptions, printEvents, allowOpportunism):
    for checkingTaskId in range(numOfTasks):

        if (robotId in tasksRobotsToAbandon[checkingTaskId]):
            if (printEvents):
                print("   robot {} still has reward from task {} , removing the 'to abandon' records ".format(robotId, checkingTaskId))
            tasksRobotsToAbandon[checkingTaskId].remove(robotId);

            if (currentTaskId != checkingTaskId):
                if (robotId in tasksSubscribedRobots[checkingTaskId]):
                    tasksSubscribedRobots[checkingTaskId].remove(robotId);
                if (robotId in tasksSubscribedRobotsByRecruitment[checkingTaskId]):
                    tasksSubscribedRobotsByRecruitment[checkingTaskId].remove(robotId);
                if (robotId in tasksReachedRobots[checkingTaskId]):
                    tasksReachedRobots[checkingTaskId].remove(robotId);
                if (checkingTaskId in robotsTaskSubscriptions[robotId]):
                    robotsTaskSubscriptions[robotId].remove(checkingTaskId);


                if (robotId in tasksLadenRobots[checkingTaskId]):
                    if (printEvents):
                        print("      also removing from laden robots")
                    tasksLadenRobots[checkingTaskId].remove(robotId);

                if ("PERFORM" in scenario_):
                    pyCreeper.crData.removeElementFromList(robotId, tasksEarningRobots[checkingTaskId], "    also removing from earning robots", printEvents )



def clearRobotToAbandonRecordsForTask(robotId, taskId, tasksRobotsToAbandon, tasksSubscribedRobots, tasksSubscribedRobotsByRecruitment, tasksReachedRobots, robotsTaskSubscriptions):
    if (robotId in tasksRobotsToAbandon[taskId]):
        tasksRobotsToAbandon[taskId].remove(robotId);
        if (robotId in tasksSubscribedRobots[taskId]):
            tasksSubscribedRobots[taskId].remove(robotId);
        if (robotId in tasksSubscribedRobotsByRecruitment[taskId]):
            tasksSubscribedRobotsByRecruitment[taskId].remove(robotId);
        if (robotId in tasksReachedRobots[taskId]):
            tasksReachedRobots[taskId].remove(robotId);
        if (taskId in robotsTaskSubscriptions[robotId]):
            robotsTaskSubscriptions[robotId].remove(taskId);


def handleTaskValueNotRecorded(t, robotId, numOfTasks, taskId, eventType, tasksSubscribedRobots, tasksReachedRobots, tasksLadenRobots, tasksSubscribedRobotsOldRecords):
    """
    Attempts to find a task id for an info event, if a task has not been recorded yet. Specific event types allow this based on previous records.
    :return: task Id, if the logic allows it. Otherwise throws an error.
    """
    if (eventType == "TASK_ABANDONED" or eventType == "TASK_MISSING"):
        print("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! {} t {} robotId {} ".format(eventType, t, robotId))
        print(tasksSubscribedRobots)
        print(tasksReachedRobots)
        print(tasksLadenRobots)
        for existingTaskId in range(numOfTasks):
            if (robotId in tasksSubscribedRobots[existingTaskId]):
                print("      change task id {} to {} ".format(taskId, existingTaskId))
                return existingTaskId;
            if (robotId in tasksSubscribedRobotsOldRecords[existingTaskId]):
                print("      change task id {} to {} ".format(taskId, existingTaskId))
                return existingTaskId;

    elif (eventType == "REWARD_STARTED" or eventType == "REWARD_FINISHED"):
        print("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + eventType)
        for existingTaskId in range(numOfTasks):
            if (robotId in tasksLadenRobots[existingTaskId]):
                print("      change task id {} to {} ".format(taskId, existingTaskId))
                return existingTaskId;
            if (robotId in tasksSubscribedRobotsOldRecords[existingTaskId]):
                print("      change task id {} to {} ".format(taskId, existingTaskId))
                return existingTaskId;
    else:
        raise ValueError(" Task value not recorded yet for task id " + str(taskId) + " t=" + str(t) + " event=" + eventType)


def cleanupRecordsAfterSubscription(taskId, robotId,  tasksSubscribedRobots, tasksSubscribedRobotsByRecruitment, tasksReachedRobots, tasksLadenRobots, tasksEarningRobots, robotsTaskSubscriptions, printTaskInfo, allowOpportunism):
    for checkingTaskId in robotsTaskSubscriptions[robotId]:
        #print("checking {} robot {} ".format(checkingTaskId, robotId))
        if (checkingTaskId != taskId):
            debugMessage = "  automaticaly remove previous subscribed robot id {} from task {}".format(robotId, checkingTaskId)
            pyCreeper.crData.removeElementFromList(robotId, tasksSubscribedRobots[checkingTaskId], debugMessage, printTaskInfo);
            pyCreeper.crData.removeElementFromList(robotId, tasksSubscribedRobotsByRecruitment[checkingTaskId], debugMessage, printTaskInfo);
            debugMessage = "  ! automaticaly remove previous reached robot id {} from task {}".format(robotId, checkingTaskId)
            pyCreeper.crData.removeElementFromList(robotId, tasksReachedRobots[checkingTaskId], debugMessage, printTaskInfo);

            #if (not allowOpportunism):
            debugMessage = "  !! automaticaly remove previous laden robot id {} from task {}".format(robotId, checkingTaskId)
            pyCreeper.crData.removeElementFromList(robotId, tasksLadenRobots[checkingTaskId], debugMessage, printTaskInfo);
            debugMessage = "  !!!! automaticaly remove previous earning robot id {} from task {}".format(robotId, checkingTaskId)
            pyCreeper.crData.removeElementFromList(robotId, tasksEarningRobots[checkingTaskId], debugMessage, printTaskInfo);

    robotsTaskSubscriptions[robotId] = [];
    pyCreeper.crData.addUniqueElementToList(taskId, robotsTaskSubscriptions[robotId]);
