#!/usr/bin/env python
#-#  Copyright 2010-2015 Karlsruhe Institute of Technology
#-#
#-#  Licensed under the Apache License, Version 2.0 (the "License");
#-#  you may not use this file except in compliance with the License.
#-#  You may obtain a copy of the License at
#-#
#-#      http://www.apache.org/licenses/LICENSE-2.0
#-#
#-#  Unless required by applicable law or agreed to in writing, software
#-#  distributed under the License is distributed on an "AS IS" BASIS,
#-#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#-#  See the License for the specific language governing permissions and
#-#  limitations under the License.

import optparse, gcSupport
from gcSupport import TaskModule, getConfig
from grid_control.datasets import DataProvider, DataSplitter

parser = optparse.OptionParser()

ogBackend = optparse.OptionGroup(parser, 'Backend debugging', '')
ogBackend.add_option('', '--backend',     dest='backend',     default=None,
	help='Specify backend')
ogBackend.add_option('', '--list-nodes',  dest='list_nodes',  default=False, action='store_true',
	help='List backend nodes')
ogBackend.add_option('', '--list-queues', dest='list_queues', default=False, action='store_true',
	help='List backend queues')
parser.add_option_group(ogBackend)

parser.add_option('', '--reset-attempts', dest='resettrys', default=False, action='store_true',
	help='Reset the attempt counter')
parser.add_option('-j', '--jdl', dest='jdl', default=False, action='store_true',
	help='Get JDL file')
parser.add_option('-J', '--jobs', dest='jobs', default='',
	help='Display job ids matching selector')
parser.add_option('-s', '--state', dest='state', default='',
	help='Force new job state')
parser.add_option('-S', '--splitting', dest='splitting', default='',
	help='Show splitting information dataset')
parser.add_option('', '--splitting-infos', dest='splittingInfos', default='',
	help='Select displayed splitting information')
parser.add_option('-i', '--invalid', dest='invalid', default='',
	help='List invalidated dataset splittings')
parser.add_option('-D', '--diffdata', dest='diff', default=False, action='store_true',
	help='Show difference between datasets')
parser.add_option('-R', '--findremoved', dest='findrm', default=False, action='store_true',
	help='Find removed blocks')
parser.add_option('-C', '--checksplitting', dest='checkSplitting', default='',
	help='Check splitting of dataset in specified work directory')
parser.add_option('-d', '--decode', dest='decode', default='',
	help='Decode log files')
(opts, args) = gcSupport.parseOptions(parser)

# we need exactly one positional argument (config file)
if opts.jdl or opts.state:
	if len(args) != 1:
		utils.exitWithUsage('%s <job info file>' % sys.argv[0])
	job = Job.load(args[0])

if opts.jobs:
	config = getConfig(args[0])
	# Initialise task module
	taskName = config.get(['task', 'module'])
	task = TaskModule.open(taskName, config, taskName)
	jobDB = JobDB(config)
	selected = JobSelector.create(opts.jobs, task = task)
	if opts.resettrys:
		for jobNum in jobDB.getJobsIter(selected):
			print 'Resetting attempts', jobNum
			jobinfo = jobDB.get(jobNum)
			jobinfo.attempt = 0
			jobinfo.history = {}
			for key in jobinfo.dict.keys():
				if key.startswith('history'):
					jobinfo.dict.pop(key)
			jobDB.commit(jobNum, jobinfo)
	print str.join(' ', map(str, jobDB.getJobsIter(selected)))


if opts.diff:
	if len(args) != 2:
		utils.exitWithUsage('%s <dataset source 1> <dataset source 2>' % sys.argv[0])
	utils.eprint = lambda *x: {}
	a = DataProvider.getInstance('ListProvider', config, args[0], None)
	b = DataProvider.getInstance('ListProvider', config, args[1], None)
	(blocksAdded, blocksMissing, blocksChanged) = DataProvider.resyncSources(a.getBlocks(), b.getBlocks())
	utils.printTabular([(DataProvider.Dataset, 'Dataset'), (DataProvider.BlockName, 'Block')], blocksMissing)

if opts.findrm:
	removed = []
	utils.eprint = lambda *x: {}
	oldDP = DataProvider.getInstance('ListProvider', config, args[0], None)
	for new in args[1:]:
		newDP = DataProvider.getInstance('ListProvider', config, new, None)
		(blocksAdded, blocksMissing, blocksChanged) = DataProvider.resyncSources(oldDP.getBlocks(), newDP.getBlocks())
		for block in blocksMissing:
			tmp = dict(block)
			tmp[-1] = new
			removed.append(tmp)
		oldDP = newDP
	utils.printTabular([(DataProvider.Dataset, 'Dataset'), (DataProvider.BlockName, 'Block'), (-1, 'Removed in file')], removed)

if opts.invalid:
	splitter = DataSplitter.loadState(opts.invalid)
	def getInvalid():
		for jobNum in range(splitter.getMaxJobs()):
			splitInfo = splitter.getSplitInfo(jobNum)
			if splitInfo.get(DataSplitter.Invalid, False):
				yield str(jobNum)
	print str.join(',', getInvalid())

if opts.jdl:
	print job.get('jdl')

if opts.state:
	try:
		newState = getattr(Job, opts.state)
	except Exception:
		print 'Invalid state: %s', opts.state
	oldState = job.state
	utils.vprint('Job state changed from %s to %s' % (Job.enum2str(oldState), Job.enum2str(newState)), -1, True)
	job.state = newState
	utils.safeWrite(open(args[0], 'w'), utils.DictFormat(escapeString = True).format(job.getAll()))

if opts.splitting:
	splitter = DataSplitter.loadState(opts.splitting)
	if not opts.checkSplitting:
		if opts.splittingInfos:
			keyStrings = opts.splittingInfos.split(',')
			keyList = map(lambda k: getattr(DataSplitter, k), keyStrings)
			def getInfos():
				for jobNum in range(splitter.getMaxJobs()):
					splitInfo = splitter.getSplitInfo(jobNum)
					tmp = map(lambda k: (k, splitInfo.get(k, '')), keyList)
					yield dict([('jobNum', jobNum)] + tmp)
			utils.printTabular([('jobNum', 'Job')] + zip(keyList, keyStrings), getInfos())
		else:
			utils.verbosity(10)
			splitter.printAllJobInfo()
	else:
		print 'Checking %d jobs...' % splitter.getMaxJobs()
		fail = utils.set()
		for jobNum in range(splitter.getMaxJobs()):
			splitInfo = splitter.getSplitInfo(jobNum)
			try:
				(events, skip, files) = (0, 0, [])
				for line in open(os.path.join(opts.checkSplitting, 'jobs', 'job_%d.var' % jobNum)).readlines():
					if 'MAX_EVENTS' in line:
						events = int(line.split('MAX_EVENTS', 1)[1].replace('=', ''))
					if 'SKIP_EVENTS' in line:
						skip = int(line.split('SKIP_EVENTS', 1)[1].replace('=', ''))
					if 'FILE_NAMES' in line:
						files = line.split('FILE_NAMES', 1)[1].replace('=', '').replace('\"', '').replace('\\', '')
						files = map(lambda x: x.strip().strip(','), files.split())
				def printError(curJ, curS, msg):
					if curJ != curS:
						print '%s in job %d (j:%s != s:%s)' % (msg, jobNum, curJ, curS)
						fail.add(jobNum)
				printError(events, splitInfo[DataSplitter.NEntries], 'Inconsistent number of events')
				printError(skip, splitInfo[DataSplitter.Skipped], 'Inconsistent number of skipped events')
				printError(files, splitInfo[DataSplitter.FileList], 'Inconsistent list of files')
			except Exception:
				print 'Job %d was never initialized!' % jobNum
		print str.join('\n', map(str, fail))

if opts.decode:
	import base64, gzip, StringIO
	for line in open(opts.decode, 'r').readlines():
		if line.startswith('(B64) '):
			line = gzip.GzipFile(fileobj = StringIO.StringIO(base64.b64decode(line.replace('(B64) ', '')))).read()
		print line.rstrip()

if opts.list_nodes or opts.list_queues:
	config = Config()
	config.opts = config
	config.opts.init = True
	wms = grid_control.WMS.open(opts.backend, config, opts.backend)
	if opts.list_nodes:
		print wms.getNodes()
	if opts.list_queues:
		print wms.getQueues()
