# @package      hubzero-submit-common
# @file         JobStatistic.py
# @copyright    Copyright (c) 2012-2020 The Regents of the University of California.
# @license      http://opensource.org/licenses/MIT MIT
#
# Copyright (c) 2012-2020 The Regents of the University of California.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
# HUBzero is a registered trademark of The Regents of the University of California.
#
import os
import os.path
import re
import logging
try:
   from UserDict import UserDict
except ImportError:
   from collections import UserDict

from hubzero.submit.LogMessage import getLogJobIdMessage as getLogMessage

class JobStatistic(UserDict):
   def __init__(self,
                nCores):
      self.logger = logging.getLogger(__name__)

      UserDict.__init__(self)

      self['jobSubmissionMechanism'] = 'Unknown'
      self['remoteJobIdNumber']      = ''
      self['event']                  = ''
      self['generated']              = ''
      self['venue']                  = ''
      self['realTime']               = 0.
      self['userTime']               = 0.
      self['sysTime']                = 0.
      self['exitCode']               = 2
      self['transferCompleteTime']   = 0.
      self['jobStartedTime']         = 0.
      self['jobFinshedTime']         = 0.
      self['waitingTime']            = 0.
      self['elapsedRunTime']         = 0.
      self['nCores']                 = nCores


   @staticmethod
   def decodeJsonToJobStatistic(obj):
      if obj.pop('__type__',None) == "JobStatistic":
         decoded = JobStatistic(1)
         for key,value in obj.items():
            decoded[key] = value
         return decoded
      else:
         return obj


   @staticmethod
   def encodeJobStatisticToJson(obj):
      if isinstance(obj,JobStatistic):
         encoded = {"__type__": "JobStatistic"}
         for key,value in obj.items():
            encoded[key] = value
      else:
         raise TypeError("Can't serialize {}".format(obj))

      return encoded


   def recordTime(self,
                  statistic,
                  timePath):
      if os.path.exists(timePath):
         if(os.path.getsize(timePath)):
            try:
               fpTime = open(timePath,'r')
               try:
                  self[statistic] = float(fpTime.readline())
#                 self.logger.log(logging.DEBUG,getLogMessage("recordTime: %s %s %f" % (timePath,statistic,self[statistic])))
               except (IOError,OSError):
                  self.logger.log(logging.ERROR,getLogMessage("%s could not be read" % (timePath)))
               finally:
                  fpTime.close()
            except (IOError,OSError):
               self.logger.log(logging.ERROR,getLogMessage("%s could not be opened" % (timePath)))
         os.remove(timePath)


   def recordTimer(self,
                   timerPath):
#     self.logger.log(logging.DEBUG,getLogMessage("recordTimer: %s" % (timerPath)))
      if os.path.exists(timerPath):
         venue       = None
         jobId       = None
         event       = None
         generated   = None
         realTime    = 0.
         userTime    = 0.
         sysTime     = 0.
         elapsedTime = 0.
         waitTime    = 0.
         exitStatus  = 0
         exitSignal  = 0

         try:
            fpTimer = open(timerPath,'r')
            try:
               while True:
                  line = fpTimer.readline()
                  if not line:
                     break
                  line = line.strip()

                  if line != "":
                     parts = line.split()
                     if len(parts) == 2:
                        timeType = parts[0]
                        try:
                           timeUsed = float(parts[1])

                           if   timeType == 'real':
                              realTime    = max(realTime,timeUsed)
                           elif timeType == 'user':
                              userTime    = userTime + timeUsed
                           elif timeType == 'sys':
                              sysTime     = sysTime + timeUsed
                           elif timeType == 'elapsed':
                              elapsedTime = elapsedTime + timeUsed
                           elif timeType == 'wait':
                              waitTime    = waitTime + timeUsed
                           elif timeType == 'jobId':
                              jobId       = parts[1]
                        except:
                           if   timeType == 'site':
                              venue = parts[1]
                           elif timeType == 'jobId':
                              jobId = parts[1]
                           elif timeType == 'event':
                              event = parts[1]
                           elif timeType == 'generated':
                              generated = parts[1]
                     else:
                        if len(parts) > 2:
                           if parts[-2] == "status":
                              try:
                                 exitStatus = int(float(parts[-1]))
                              except ValueError:
                                 message = "recordTimer:exitStatus = %s" % (parts[-1])
                                 self.logger.log(logging.DEBUG,getLogMessage(message))
                                 exitStatus = 0
                           if parts[-2] == "signal":
# Killed by signal 2.
                              exitStatus = 1
                              try:
                                 exitSignal = int(float(parts[-1]))
                              except ValueError:
                                 message = "recordTimer:exitSignal = %s" % (parts[-1])
                                 self.logger.log(logging.DEBUG,getLogMessage(message))
                                 exitStatus = 0
                        if re.search("Killed",line):
                           exitStatus = 1
                           exitSignal = 15

               if self['exitCode'] == 2:
                  if exitSignal > 0:
                     self['exitCode'] = exitStatus << 7 | exitSignal
                  else:
                     self['exitCode'] = exitStatus
               self['realTime']       = realTime
               self['userTime']       = userTime
               self['sysTime']        = sysTime
               self['elapsedRunTime'] = elapsedTime
               self['waitingTime']    = waitTime
               if venue and not self['venue']:
                  self['venue']             = venue
               if jobId:
                  self['remoteJobIdNumber'] = jobId
               if event:
                  self['event']             = event
               if generated:
                  self['generated']         = generated
            except (IOError,OSError):
               self.logger.log(logging.ERROR,getLogMessage("%s could not be read" % (timerPath)))
            finally:
               fpTimer.close()
            os.remove(timerPath)
         except (IOError,OSError):
            self.logger.log(logging.ERROR,getLogMessage("%s could not be opened" % (timerPath)))


   def setWaitingTime(self):
      waitingTime = self['jobStartedTime'] - self['transferCompleteTime']
      self['waitingTime'] += max(waitingTime,0.)


   def setElapsedRunTime(self):
      elapsedRunTime = self['jobFinshedTime'] - self['jobStartedTime']
      self['elapsedRunTime'] = max(self['elapsedRunTime'],elapsedRunTime)


