#!/usr/bin/python
#
# Display magicpoint presentations in a terminal window.
# Named in honour of http://www.calastrology.com/
#
# Peter Hardy <peter@hardy.dropbear.id.au>

import curses, re, select, sys, xreadlines
from curses import panel
from threading import Event

class voodoo:
	# root window, and its dimensions
	global stdscr, scr_x, scr_y
	# Page array, and the index of the current page
	global pages, curpage
	# alignment doobeys
	global LEFT, CENTRE, RIGHT
	# The index panel
	global index

	def __init__ (self, win, fh):
		self.LEFT, self.CENTRE, self.RIGHT = 1, 2, 3
		self.stdscr = win
		# Get the size of the window
		self.scr_y, self.scr_x = win.getmaxyx()
		self.readfile(fh)
		self.curpage = 1

		# Set up the colours
		# I have grand plans of parsing %fore and possibly %back tags
		# Maybe one day.
		curses.init_pair (1, curses.COLOR_WHITE, curses.COLOR_BLACK)
		curses.init_pair (2, curses.COLOR_BLUE, curses.COLOR_BLACK)
		curses.init_pair (3, curses.COLOR_CYAN, curses.COLOR_BLACK)
		curses.init_pair (4, curses.COLOR_GREEN, curses.COLOR_BLACK)
		curses.init_pair (5, curses.COLOR_MAGENTA, curses.COLOR_BLACK)
		curses.init_pair (6, curses.COLOR_RED, curses.COLOR_BLACK)
		curses.init_pair (7, curses.COLOR_YELLOW, curses.COLOR_BLACK)
		# And finally a nice one for the index
		curses.init_pair (8, curses.COLOR_WHITE, curses.COLOR_BLUE)
		
		# Set up the index panel
		indexwin = curses.newwin (5, 40, (self.scr_y/2)-2, (self.scr_x/2)-20)
		self.index = panel.new_panel(indexwin)
		indexwin.bkgdset (ord(' '), curses.color_pair(8))
		self.index.hide()
	
	def drawcurpage(self):
		self.drawpage(self.pages[self.curpage])
		
	def drawpage(self, page):
		# clear the screen
		self.stdscr.erase()
		alignment = self.LEFT
		attributes=curses.A_NORMAL

		# Process any commands at the start of the page
		first = 0
		while (page[first][0] in ['%', '\n']):
			self.parse_cmd(page[first])
			first = first + 1
		# Draw the first line
		self.stdscr.addstr(0, 0, page[first], curses.A_STANDOUT)
		
		for line_no in range(first+1,min(self.scr_y-1, len(page))):
			if page[line_no][0] == '%':
				alignment = self.parse_cmd(page[line_no], alignment)
			else:
				if (alignment == self.CENTRE):
					x = (self.scr_x - len(page[line_no]))/2
				elif (alignment == self.RIGHT):
					x = self.scr_x - len(page[line_no])
				else:
					x = 0
				self.stdscr.addstr(line_no, x, page[line_no], attributes)
		self.stdscr.refresh()
	
	def useindex (self):
		win = self.index.window()
		newpage = self.curpage
		self.index.show()
		
		exit_evt = Event()
		while (exit_evt.isSet() == 0):
			win.erase()
			win.box()
			win.addstr(2, 2, repr(newpage))
			win.addstr(2, 5, self.pages[newpage][0])
			win.refresh()

			nextchar = 'x'

			inputs, outputs, errors = select.select([sys.stdin], [], [], 0.1)
			if sys.stdin in inputs:
				nextchar=self.stdscr.getch()
			if sys.stdin in errors:
				nextchar = ' '
			
			if nextchar == curses.KEY_LEFT:
				newpage = max(newpage - 1, 1)
			elif nextchar == curses.KEY_RIGHT:
				newpage = min(newpage + 1, len(self.pages)-1)
			elif nextchar == ord(' '):
				exit_evt.set()
			else:
				pass
			
		self.index.hide()
		self.gotopage(newpage)

	# Read a file in, and split it up into pages
	# At the end of all this, self.pages will
	# contain a list of pages, each one represented by a
	# list of lines.
	def readfile(self, fh):
		pagematch = re.compile("^%page", re.I)
		self.pages = []
		page = []

		for line in xreadlines.xreadlines(fh):
			if pagematch.match(line):
				self.pages.append(page)
				page = []
			else:
				page.append(line)
		self.pages.append(page)
	
	def nextpage(self):
		self.gotopage(self.curpage + 1)
	
	def lastpage(self):
		self.gotopage(self.curpage - 1)
	
	def gotopage(self, newpage):
		if (0 < newpage < len(self.pages)):
			self.curpage = newpage
			self.drawpage(self.pages[self.curpage])
	
	def parse_cmd (self, line, alignment):
		if re.match("%left", line, re.I):
			alignment = self.LEFT
		elif re.match("%cent(re|er)", line, re.I):
			alignment = self.CENTRE
		elif re.match("%right", line, re.I):
			alignment = self.RIGHT
		else:
			pass

		return alignment
	
	def runpres (self):
		self.drawcurpage()
		# input loop goes here
		nextchar = ' '
		exit_evt = Event()
		
		while exit_evt.isSet() == 0:
			inputs, outputs, errors = select.select([sys.stdin], [], [], 0.1)
			if sys.stdin in inputs:
				nextchar = self.stdscr.getch()
				self.handle_key (nextchar, exit_evt)
			if sys.stdin in errors:
				exit_evt.set()

	def handle_key (self, nextchar, exit_evt):
		if (nextchar == ord('q')):
			exit_evt.set()
		elif (nextchar in [ord(' '), ord('n'), ord('j'), curses.KEY_DOWN]):
			self.nextpage()
		elif (nextchar in [curses.KEY_DL, ord('p'), ord('k'), curses.KEY_UP]):
			self.lastpage()
		elif (nextchar == ord('g')):
			self.useindex()
		else:
			pass

def main(stdscr):
	foobie = sys.argv[len(sys.argv)-1]
	try:
		fh = open(foobie)
	except:
		print("Problem opening input file.  Bailing.")
		sys.exit(1)

	mypres = voodoo (stdscr, fh)
	mypres.runpres()

if __name__ == "__main__":
	curses.wrapper(main)
