#!/usr/bin/env python

# Commands

# fourstar
#	connect <file name>
#	status
#	create
#
#
# 	chart
#		list
#		create
#		find
#		details <ledger id>
#		balance
#	ledger
#		balance <ledger id>
#		print
#	journal
#		find
#		details <journal id>
#		balance <journal id>
#		post
#### create????
#
#	system
#		init
#		export
#		import

from System import System
from Store import Store

#

print "Hello Cruel World!"


# real code below here

#!/usr/bin/python
# vim:ts=4

# USE_DB = 0

import ConfigParser
import sys, os, string, re, pwd, signal, math, syslog
import logging, logging.handlers
from traceback import format_tb
from time import time, sleep, mktime, localtime

#DOOR = 1
#KEY = 3
TICK = 4

(
STATE_IDLE,
STATE_DOOR_OPENING,
STATE_DOOR_CLOSING,
STATE_GETTING_UID,
STATE_GETTING_PIN,
STATE_GET_SELECTION,
STATE_GRANDFATHER_CLOCK,
) = range(1,8)

class FS_State:
	def __init__(self,v):
		self.state_table = {}
		self.state = STATE_IDLE
		self.counter = 1

		#self.mk = MessageKeeper(v)
		self.cur_user = ''
		self.cur_pin = ''
		self.username = ''
		self.cur_selection = ''
		self.time_to_autologout = None

		self.last_timeout_refresh = None

	def change_state(self,newstate,newcounter=None):
		if self.state != newstate:
			#print "Changing state from: ", 
			#print self.state,
			#print " to ", 
			#print newstate 
			self.state = newstate

		if newcounter is not None and self.counter != newcounter:
			#print "Changing counter from: ", 
			#print self.counter,
			#print " to ", 
			#print newcounter 
			self.counter = newcounter

def do_nothing(state, event, params, v, vstatus):
	print "doing nothing (s,e,p)", state, " ", event, " ", params
	pass

def create_state_table(vstatus):
	vstatus.state_table[(STATE_IDLE,TICK,1)] = do_nothing
#	vstatus.state_table[(STATE_IDLE,KEY,1)] = do_nothing
#	vstatus.state_table[(STATE_IDLE,DOOR,1)] = do_nothing
#
#	vstatus.state_table[(STATE_DOOR_OPENING,TICK,1)] = handle_door_idle
#	vstatus.state_table[(STATE_DOOR_OPENING,DOOR,1)] = handle_door_event
#	vstatus.state_table[(STATE_DOOR_OPENING,KEY,1)] = do_nothing
#
#	vstatus.state_table[(STATE_DOOR_CLOSING,TICK,1)] = return_to_idle
#	vstatus.state_table[(STATE_DOOR_CLOSING,DOOR,1)] = handle_door_event
#	vstatus.state_table[(STATE_DOOR_CLOSING,KEY,1)] = do_nothing
#
#	vstatus.state_table[(STATE_GETTING_UID,TICK,1)] = handle_getting_uid_idle
#	vstatus.state_table[(STATE_GETTING_UID,DOOR,1)] = do_nothing
#	vstatus.state_table[(STATE_GETTING_UID,KEY,1)] = handle_getting_uid_key
#
#	vstatus.state_table[(STATE_GETTING_PIN,TICK,1)] = handle_getting_pin_idle
#	vstatus.state_table[(STATE_GETTING_PIN,DOOR,1)] = do_nothing
#	vstatus.state_table[(STATE_GETTING_PIN,KEY,1)] = handle_getting_pin_key
#
#	vstatus.state_table[(STATE_GET_SELECTION,TICK,1)] = handle_get_selection_idle
#	vstatus.state_table[(STATE_GET_SELECTION,DOOR,1)] = do_nothing
#	vstatus.state_table[(STATE_GET_SELECTION,KEY,1)] = handle_get_selection_key
#
#	vstatus.state_table[(STATE_GRANDFATHER_CLOCK,TICK,1)] = handle_idle_grandfather_tick
#	vstatus.state_table[(STATE_GRANDFATHER_CLOCK,TICK,2)] = handle_grandfather_tick
#	vstatus.state_table[(STATE_GRANDFATHER_CLOCK,DOOR,1)] = do_nothing
#	vstatus.state_table[(STATE_GRANDFATHER_CLOCK,DOOR,2)] = do_nothing
#	vstatus.state_table[(STATE_GRANDFATHER_CLOCK,KEY,1)] = do_nothing
#	vstatus.state_table[(STATE_GRANDFATHER_CLOCK,KEY,2)] = do_nothing

def get_state_table_handler(vstatus, state, event, counter):
	return vstatus.state_table[(state,event,counter)]

def run_forever(rfh, wfh, options, cf):
	# get system, status object
	v = System(rfh, wfh)
	vstatus = FS_State(v)

	create_state_table(vstatus)

	# This main loop was hideous and the work of the devil.
	# This has now been fixed (mostly) - mtearle
	#
	#
	# notes for later surgery
	#   (event, counter, ' ')
	#        V
	#   d[      ] = (method)
	#
	# ( return state - not currently implemented )
	while True:
		timeout = 0
		e = v.next_event(timeout)
		(event, params) = e
		run_handler(event, params, v, vstatus)

def run_handler(event, params, v, vstatus):
	handler = get_state_table_handler(vstatus,vstatus.state,event,vstatus.counter)
	if handler:
		handler(vstatus.state, event, params, v, vstatus)

def parse_args():
	from optparse import OptionParser

	op = OptionParser(usage="%prog [OPTION]...")
	op.add_option('-f', '--config-file', default='fourstar.conf', metavar='FILE', dest='config_file', help='use the specified config file instead of fourstar.conf')
#	op.add_option('--serial', action='store_true', default=True, dest='use_serial', help='use the serial port')
#	op.add_option('--lat', action='store_true', default=False, dest='use_lat', help='use LAT')
#	op.add_option('--virtualvend', action='store_false', default=True, dest='use_serial', help='use the virtual vending server instead of LAT')
#	op.add_option('-n', '--hostname', dest='host', default='localhost', help='the hostname to connect to for virtual vending machine mode (default: localhost)')
#	op.add_option('-p', '--port', dest='port', default=5150, type='int', help='the port number to connect to (default: 5150)')
	op.add_option('-l', '--log-file', metavar='FILE', dest='log_file', default='', help='log output to the specified file')
	op.add_option('-s', '--syslog', dest='syslog', metavar='FACILITY', default=None, help='log output to given syslog facility')
#	op.add_option('-d', '--daemon', dest='daemon', action='store_true', default=False, help='run as a daemon')
	op.add_option('-v', '--verbose', dest='verbose', action='store_true', default=False, help='spit out lots of debug output')
	op.add_option('-q', '--quiet', dest='quiet', action='store_true', default=False, help='only report errors')
	op.add_option('--pid-file', dest='pid_file', metavar='FILE', default='', help='store daemon\'s pid in the given file')
	options, args = op.parse_args()

	if len(args) != 0:
		op.error('extra command line arguments: ' + ' '.join(args))

	return options



config_options = {
}


class FS_ConfigFile:
	def __init__(self, config_file, options):
		try:
			cp = ConfigParser.ConfigParser()
			cp.read(config_file)

			for option in options:
				section, name = options[option]
				value = cp.get(section, name)
				self.__dict__[option] = value
		
		except ConfigParser.Error, e:
			raise SystemExit("Error reading config file "+config_file+": " + str(e))




def set_stuff_up():
	def do_nothing(signum, stack):
		signal.signal(signum, do_nothing)
	def stop_server(signum, stack): raise KeyboardInterrupt
	signal.signal(signal.SIGHUP, do_nothing)
	signal.signal(signal.SIGTERM, stop_server)
	signal.signal(signal.SIGINT, stop_server)

	options = parse_args()
	config_opts = FS_ConfigFile(options.config_file, config_options)
#	if options.daemon: become_daemon()
	set_up_logging(options)
	if options.pid_file != '': create_pid_file(options.pid_file)

	return options, config_opts


def clean_up_nicely(options, config_opts):
	if options.pid_file != '':
		try:
			os.unlink(options.pid_file)
			logging.debug('Removed pid file '+options.pid_file)
		except OSError: pass  # if we can't delete it, meh


def set_up_logging(options):
	logger = logging.getLogger()

#	if not options.daemon:
#		stderr_logger = logging.StreamHandler(sys.stderr)
#		stderr_logger.setFormatter(logging.Formatter('%(levelname)s: %(message)s'))
#		logger.addHandler(stderr_logger)
	
	if options.log_file != '':
		try:
			file_logger = logging.FileHandler(options.log_file)
			file_logger.setFormatter(logging.Formatter('%(asctime)s %(levelname)s: %(message)s'))
			logger.addHandler(file_logger)
		except IOError, e:
			logger.warning('unable to write to log file '+options.log_file+': '+str(e))

	if options.syslog != None:
		sys_logger = logging.handlers.SysLogHandler('/dev/log', options.syslog)
		sys_logger.setFormatter(logging.Formatter('fourstar[%d]'%(os.getpid()) + ' %(levelname)s: %(message)s'))
		logger.addHandler(sys_logger)

	if options.quiet:
		logger.setLevel(logging.WARNING)
	elif options.verbose:
		logger.setLevel(logging.DEBUG)
	else:
		logger.setLevel(logging.INFO)


#def become_daemon():
#	dev_null = file('/dev/null')
#	fd = dev_null.fileno()
#	os.dup2(fd, 0)
#	os.dup2(fd, 1)
#	os.dup2(fd, 2)
#	try:
#		if os.fork() != 0:
#			sys.exit(0)
#		os.setsid()
#	except OSError, e:
#		raise SystemExit('failed to fork: '+str(e))


def do_FS_server(options, config_opts):
	while True:

		wfh = sys.stdout
		rfh = sys.stdin

#		try:
		run_forever(rfh, wfh, options, config_opts)
#		except VendingException:
#			logging.error("Connection died, trying again...")
#			logging.info("Trying again in 5 seconds.")
#			sleep(5)


if __name__ == '__main__':
	options, config_opts = set_stuff_up()
	while True:
		try:
			logging.warning('Starting FS Server')
			do_FS_server(options, config_opts)
			logging.error('FS Server finished unexpectedly, restarting')
		except KeyboardInterrupt:
			logging.info("Killed by signal, cleaning up")
			clean_up_nicely(options, config_opts)
			logging.warning("FS Server stopped")
			break
		except SystemExit:
			break
		except:
			(exc_type, exc_value, exc_traceback) = sys.exc_info()
			tb = format_tb(exc_traceback, 20)
			del exc_traceback
			
			logging.critical("Uh-oh, unhandled " + str(exc_type) + " exception")
			logging.critical("Message: " + str(exc_value))
			logging.critical("Traceback:")
			for event in tb:
				for line in event.split('\n'):
					logging.critical('    '+line)
			logging.critical("This message should be considered a bug in the FS Server.")
			logging.critical("Please report this to someone who can fix it.")
			sleep(10)
			logging.warning("Trying again anyway (might not help, but hey...)")

