#!/home/crew/manus/bin/python

#	====================================
#	Requires Python version 2.0 or later
#	====================================

import sys

class PostScriptMap:
	#	----------------------------------------------------------------------
	def __init__(self, mapFile, inFile = None, outFile = None):
		try: lines = (inFile and open(inFile) or sys.stdin).readlines()
		except: raise CannotOpenResultsFile
		try: self.outFile = outFile and open(outFile, 'w') or sys.stdout
		except: raise CannotOpenOutputFile
		self.owner, self.adj, self.units = {}, {}, {}
		self.map, self.retreats, self.pages, self.sc = [], [], 0, ''
		self.started = section = power = None
		self.loadInfo('%s.info' % mapFile)
		try: file = open('%s.ps' % mapFile)
		except: raise CannotOpenPostScriptFile
		self.outFile.write(file.read())
		file.close()
		for line in lines:
			word = line.upper().split()
			if not word or (line[0] in ' \t' and section != 'O'): continue
			copy = ' '.join(word)

			#	-------------------------------------------
			#	Check if this is the beginning of a section
			#	-------------------------------------------
			if ' '.join(word[:2]) in [	'STARTING POSITION', 'STATUS OF',
										'ADJUSTMENT ORDERS', 'RETREAT ORDERS',
										'MOVEMENT RESULTS'	]:
				where, section = word[1] == 'OF' and 5 or 2, word[0][0]
			elif section: where = 0
			else: continue
			if where:
				#	--------------------------------------------------------
				#	Determine the page title (game name, season, year, etc.)
				#	--------------------------------------------------------
				orders, graphics, scList, season = [], [], '', ''
				lowWord, title = line.split(), '(game start)'
				if lowWord[where] == 'for':
					season, year = lowWord[where + 1:where + 3]
					if year == 'of': year = lowWord[where + 3]
					year = int(float(year))
					if lowWord[-1][0] == '(' and lowWord[-1][-1] == ')':
						name = lowWord[-1][1:-1].split('.')[0]
						title = '%s, %s %d' % (name, season, year)
				#	------------
				#	Prepare page
				#	------------
				if section in 'MS':
					if self.started: self.endPage()
					self.units = {}
					self.startPage(title)
				#	-----------------------------
				#	Page begun.  Go to next line.
				#	-----------------------------
				continue

			#	---------------------------------------------
			#	See if we're reading orders for a order power
			#	---------------------------------------------
			if word[0][-1] == ':' and power != word[0][:-1]:
				power = word[0][:-1]
				if section == 'O': self.sc = '%s%sCENTER\n' % (self.sc, power)
				else:
					self.outFile.write('%s\n' % power)
					if section == 'M': orders.append(power)

			#	-------------------
			#	Lines to be ignored
			#	-------------------
			if (copy.find('THE FOLLOWING') == 0
			or	copy.find('PENDING') > -1
			or	copy.find('INVALID') > -1
			or	copy.find('REORDER') > -1
			or	copy.find('NO VALID') > -1
			or	copy.find('DESTROYED') > -1
			or	copy.find('INCOME HAS') > -1): continue

			#	--------------------------------------------
			#	Lines signaling the end of the phase results
			#	--------------------------------------------
			if (copy.find('THE ') == 0
			or	copy.find('DEADLINE ') == 0
			or	copy.find('ORDERS ') == 0
			or	copy.find('END') == 0): section = None

			#	------------------------
			#	Ownership report trigger
			#	------------------------
			elif copy.find('OWNERSHIP') == 0:
				section, self.sc, self.owner = 'OWN', '', {}

			#	------------------------------
			#	Check if the section has ended
			#	------------------------------
			if section in ['OWN', None]:
				#	---------------------
				#	Write the information
				#	---------------------
				if orders:
					self.outFile.write('OrderReport\n')
					for order in orders:
						self.outFile.write('(%s) WriteOrder\n' % order)
				for graphic in graphics: self.outFile.write('%s\n' % graphic)
				power = None
				if section: section = 'O'
				continue

			#	----------------
			#	Center ownership
			#	----------------
			if section == 'O':
				if 'SUPPLY' in word:
					power = section = None
					continue
				scList += ' %s' % ' '.join(word[line[0] != ' ':])
				if scList[-1] == '.':
					self.owner[power], scList = [], scList[:-1]
					for loc in scList.split(','):
						where = self.lookup(loc.strip())
						self.owner[power].append(where['nick'])
						self.sc += '%s supply\n' % where['nick']
					scList = ''
				continue

			#	-------------------------------------------------------
			#	Adjustment orders (modify the units list for final map)
			#	-------------------------------------------------------
			if word[1] in ['DEFAULTS,', 'REMOVES', 'BUILDS', 'BUILD']:
				self.adj.setdefault(power,
					[word[1][0] == 'B' and 'BUILDS ' or 'REMOVES'])
				if word[2].startswith('WAIVE'):
					self.adj[power].append('WAIVED')
					continue
				which = 3 + (word[1][0] == 'D')
				unit = word[which][0]
				which += 2
				where = ' '.join(word[which + (word[which] == 'THE'):])
				where = self.lookup(where.split('.')[0])
				if word[1][0] != 'B':
					for piece in self.units[power]:
						if piece['loc']['nick'] == where['nick']:
							self.units[power].remove(piece)
							break
				else: self.addUnit(power, unit, where)
				self.adj[power].append('%s %s' % (unit, where['nick']))
				continue
				
			#	------------------------------------------------------
			#	Determine if the order failed (presence of annotation)
			#	------------------------------------------------------
			message, msg = copy.split('(*'), ''
			if len(message) > 1:
				copy, msg = message[0], message[1].split('*)')[0]
				word = copy.split()
			copy, word[-1] = copy.strip()[:-1], word[-1][:-1]

			#	--------------------------------------------------------
			#	Find the order type (and where in the order it is given)
			#	Detect the word 'NO' from "NO ORDER PROCESSED" ==> HOLD.
			#	--------------------------------------------------------
			for order in ['SUPPORT', 'CONVOY', '->', 'HOLD', 'DISBAND', 'NO']:
				try: orderWord = word.index(order)
				except: continue
				if order == 'NO': word[orderWord - 1] = word[orderWord - 1][:-1]
				break
			else: order, orderWord = '.', len(word)

			#	-----------------------
			#	Determine unit location
			#	-----------------------
			unit, si = word[1][0], self.lookup(' '.join(word[2:orderWord]))

			#	------------------------------------------------------
			#	Dislodged units will not be listed in the unit list.
			#	------------------------------------------------------
			if msg.find('DISLODGED') >= 0: msg = 'DISLODGED'
			elif order[0] != 'D': self.addUnit(power, unit, si)

			#	-------------
			#	Draw the unit
			#	-------------
			if section != 'R': self.outFile.write("\t%d %d %s\n" %
				(si['x'], si['y'], unit == 'A' and "DrawArmy" or "DrawFleet"))

			#	--------------------------------------------
			#	Determine order text and graphical depiction
			#	--------------------------------------------

			#	----
			#	HOLD
			#	----
			if order[0] in 'NH': order, graph = 'H', ''
			#	------
			#	CONVOY
			#	------
			elif order[0] == 'C':
				mover, which = word.index('->'), ['ARMY', 'FLEET'][unit == 'A']
				embark = ' '.join(word[word.index(which) + 1:mover])
				di, land = self.lookup(embark), ' '.join(word[mover + 1:])
				di2 = self.lookup(land)
				order = "C %.3s - %-6.6s" % (di['nick'], di2['nick'])
				graph = ("%d %d %d %d ArrowConvoy" %
					(di['x'], di['y'], di2['x'], di2['y']))
			#	-------
			#	SUPPORT
			#	-------
			elif order[0] == 'S':
				if 'ARMY' in word[orderWord:] or 'FLEET' in word[orderWord:]:
					try: where = word[orderWord:].index('ARMY')
					except: where = word[orderWord:].index('FLEET')
				else: continue
				where += orderWord + 1
				mover = None
				try:
					move = word.index('->')
					mover, where = ' '.join(word[where:move]), move + 1
				except: pass
				dest = ' '.join(word[where:])
				di = self.lookup(dest)
				if not mover:
					order = "S %.3s" % di['nick']
					graph = '%d %d ArrowHold' % (di['x'], di['y'])
				else:
					di2 = self.lookup(mover)
					order = "S %.3s - %-6.6s" % (di2['nick'], di['nick'])
					graph = ("%d %d %d %d ArrowSupport" %
						(di2['x'], di2['y'], di['x'], di['y']))
			#	----
			#	MOVE
			#	----
			elif order[0] == '-':
				where = 0
				while 1:
					try: where += word[where:].index('->') + 1
					except: break
				where = ' '.join(word[where:])
				di = self.lookup(where)
				order = "- %-6.6s" % di['nick']
				graph = "%d %d ArrowMove" % (di['x'], di['y'])
				if not msg:
					self.units[power][-1]['loc'] = di
					if section == 'R': self.retreats.append('%-10s%s %s - %s' %
						(power, unit, si['nick'], di['nick']))
				elif section == 'R':
					order = graph = ''
					del self.units[power][-1]
			#	-------
			#	Disband
			#	-------
			elif order[0] == 'D': self.retreats.append('%-10s%s %s DISBAND' %
				(power, unit, si['nick']))
			#	--------------------
			#	Simple unit position
			#	--------------------
			else: order = graph = ''

			#	----------------------------------------------------
			#	Add information (order and graphic arrow) to the map
			#	----------------------------------------------------
			if section in 'MS': orders.append(' %c %.3s %-15s %s' %
				(unit, si['nick'], order, msg))
			if graph and section == 'M': graphics.append('%s%d %d %s%s' %
				(msg and 'FailedOrder ' or '', si['x'], si['y'], graph,
				msg and ' OkOrder' or ''))

		#	------------------------------------------------------
		#	Finished reading the map.  Display any unfinished page
		#	------------------------------------------------------
		if self.pages:
			#	---------------------------
			#	Display any unfinished page
			#	---------------------------
			if self.started: self.endPage()
			#	---------------------------------------------------------------
			#	If the last page was movement, add final page showing positions
			#	---------------------------------------------------------------
			if self.units:
				self.startPage('%s, After %s %d' % (name, season, year))
				seq = self.units.keys()
				seq.sort()
				for time in range(2):
					if time: self.outFile.write('OrderReport\n')
					for power in seq:
						self.outFile.write(
							(time and '(%s) WriteOrder\n' or '%s\n') % power)
						for unit in self.units[power]:
							if time: self.outFile.write(
								'( %c %s) WriteOrder\n' %
								(unit['type'], unit['loc']['nick']))
							else: self.outFile.write('\t%d %d %s\n' %
								(unit['loc']['x'], unit['loc']['y'],
								unit['type'] == 'A' and
								'DrawArmy' or 'DrawFleet'))
				self.endPage()
			#	-------------------
			#	Finish the document
			#	-------------------
			self.outFile.write(
				"%%%%Trailer\n"
				"%%%%Pages: %d 1\n"
				"%%%%BoundingBox:0 0 612 792\n" % self.pages)
	#	----------------------------------------------------------------------
	def addUnit(self, power, unit, where):
		self.units.setdefault(power, []).append({'type': unit, 'loc': where})
	#	----------------------------------------------------------------------
	def startPage(self, title = None):
		self.pages += 1
		self.started = 1
		self.outFile.write(
			"%%%%Page: %d %d\n"
			"%%%%PageBoundingBox: 0 0 792 612\n"      # 11 x 8.5
			"[{ThisPage} /Rotate 90 /PUT pdfmark\n"
			"DrawMap\n" % (self.pages, self.pages))
		if title: self.outFile.write('(%s) DrawTitle\n' % title)
		for data in self.map:
			if '/' not in data['nick']:
				self.outFile.write('%(x)d %(y)d (%(nick)s) DrawName\n' % data)
	#	----------------------------------------------------------------------
	def endPage(self):
		#	--------------
		#	Retreat report
		#	--------------
		if self.retreats:
			self.retreats.sort()
			self.outFile.write('RetreatReport\n')
			for order in self.retreats:
				self.outFile.write('(%s) WriteRetreat\n' % order)
			self.retreats = []
		#	-----------------
		#	Adjustment report
		#	-----------------
		if self.adj:
			self.outFile.write('AdjustReport\n')
			seq = self.adj.keys()
			seq.sort()
			for power in seq: self.outFile.write(
				'(%-10s %s %s) WriteAdjust\n' % (power, self.adj[power][0],
				', '.join(self.adj[power][1:])))
			self.adj = {}
		#	-------------------
		#	SC ownership report
		#	-------------------
		if self.owner:
			self.outFile.write('OwnerReport\n')
			seq = self.owner.keys()
			seq.sort()
			if 'UNOWNED' in seq:
				seq.remove('UNOWNED')
				seq.append('UNOWNED')
			for power in seq:
				if not self.units.get(power) and not self.owner.get(power):
					continue
				status = power == 'UNOWNED' and ' ' or ('(%d/%d)' %
					(len(self.units.get(power, [])),
					len(self.owner.get(power, []))))
				self.outFile.write('(%-10s%-7s %s) WriteOwner\n' %
					(power, status, ' '.join(self.owner[power])))
		#	---------------------------------
		#	SC coloration and page terminator
		#	---------------------------------
		self.outFile.write(
			"closepath newpath\n%s\nBlack\n"
			"ShowPage\n"
			"%%%%PageTrailer\n" % self.sc)
		self.started = 0
	#	----------------------------------------------------------------------
	def loadInfo(self, infoFile):
		try: file, mode = open(infoFile), 0
		except: raise CannotReadInfoFile
		for line in file.readlines():
			if line[0] in ' #': continue
			copy = ' '.join(line.upper().split())
			if mode:
				word = copy.split()
				try:
					x, y = map(int, word[:2])
					nick, junk, name = copy.split('|')[1:4]
				except: raise BadLocationDescriptionLine
				self.map.append({'x': x, 'y': y, 'name': name, 'nick': nick})
			elif line[0] == '-': mode = 1
		file.close()
	#	----------------------------------------------------------------------
	def lookup(self, name):
		for loc in self.map:
			if name in loc.values(): return loc
		sys.stderr.write('CANNOT LOOKUP %s\n' % name)
		raise OhCrap
	#	----------------------------------------------------------------------

if __name__ == '__main__':
	import os
	if len(sys.argv) == 1:
		sys.argv.append('%s/dpmap/standard' % os.path.dirname(sys.argv[0]))
	if len(sys.argv) != 2: sys.stderr.write(
		'Usage: %s [../mapDir/mapName] < resultsFile > outputPSfile\n' %
		os.path.basename(sys.argv[0]))
	else: PostScriptMap(sys.argv[1])

