# -*- coding: iso-8859-1 -*-

import tokenize
import StringIO
import string
import os
import re
from traceback import format_exception
import codecs

from MoinMoin.util.ParserBase import ParserBase
from MoinMoin.util import MoinMoinNoFooter, filesys
from MoinMoin import config

Dependencies = ['time']

class StopScriptError(StandardError):
    pass

def redirect(target, message=None):
    raise StopScriptError('redirect ' + target, message)

def return_(message=None):
    raise StopScriptError('return', message)

class SudData(object):
    zone = None
    status = None
    _datafile = None
    inventory = None
    otherzones = None

    lzone_re = re.compile(r'lastzone\s*=\s*(.*)')
    zone_re = re.compile(r'zonestatus\((.*)\)\s*=\s*(.*)')
    dict_re = re.compile(r'(.*):(.*)')
    def __init__(self, cfg, user, zone):
	self.zone = zone
	self.status = 0
	self.otherzones = ''
	self.inventory = {}
	self._datafile = os.path.join(cfg.user_dir, user.id + '.suddata')
	if os.path.exists(self._datafile):
	    self.load()
    def get_status(self):
	return self.status
    def set_status(self, status):
	if isinstance(status, int) or \
	   isinstance(status, basestring) or \
	   isinstance(status, list):
	    self.status = status
	else:
	    raise ValueError('Status must be int, str or list')
    def get_inventory(self):
	class Empty(object):
	    pass
	result = Empty()
	result.set_item = self.inventory.__setitem__
	result.get_item = self.inventory.__getitem__
	result.del_item = self.inventory.__delitem__
	result.has_key = self.inventory.has_key
	return result
    def load(self):
	data = codecs.open(self._datafile, "r", config.charset)
	for line in map(string.strip, data.readlines()):
	    if not line or line.startswith('#'):
		continue
	    match = self.lzone_re.search(line)
	    if match:
		lastzone = match.group(1)
		continue
	    match = self.zone_re.search(line)
	    if match:
		if match.group(1) == self.zone:
		    self.status = match.group(2)
		    if self.status.startswith('['):
			self.status = eval(self.status)
		else:
		    self.otherzones += line + '\n'
		continue
	    if self.zone != lastzone:
		continue
	    match = self.dict_re.search(line)
	    if match:
		self.inventory[match.group(1)] = match.group(2)
	data.close()
    def save(self):
	filesys.makeDirs(os.path.dirname(self._datafile))
	data = codecs.open(self._datafile, "w", config.charset)
	data.write('lastzone = %s\n' % self.zone)
	data.write('zonestatus(%s) = %s\n' % (self.zone, str(self.status)))
	data.write(self.otherzones)
	for ii in self.inventory.keys():
	    data.write('%s:%s' % (ii, self.inventory[ii]) + '\n')
	data.close()

class Parser(ParserBase):

    parsername = "SudParser"
    extensions = ['']

    _restricted_tokens = ('import', 'open', 'eval', 'exec', \
			  'print', 'execfile', 'file')
    emsg = None
    messages = None

    def __init__(self, raw, request, **kw):
	self.request = request
	self.messages = []
	_ = request.getText

	pagename = request.page.page_name
	adampage = pagename.split('/')[0]
	zone = request.pragma.get('zone', adampage)
	if zone.split('/')[0] != adampage:
	    self.emsg = _('Invalid %s') % 'zone'
	    return

	if request.form.has_key('message'):
	    self.messages = request.form['message'][0].split(',,')
	try:
	    rf_count = int(request.form.get('refreshed', (0,))[0])
	except ValueError:
	    rf_count = 0

	# Check raw text input
	tokenstream = tokenize.generate_tokens(StringIO.StringIO(raw).readline)
	try:
	    while 1:	# tokenstream ends by raising an exception
		token = tokenstream.next()
		if token[0] == 1 and \
			( token[1] in self._restricted_tokens or \
			  token[1].find('__') != -1 ):
		    self.emsg = _('Disabled statement %s used in line %s.') % \
			    (token[1], token[2][0])
		    return
	except StopIteration:
	    pass

	requested_redirect = ''
	# Create environment
	suddata = SudData(request.cfg, request.user, zone)
	referer = request.http_referer.replace(request.getBaseURL(), '')
	referer = referer.strip('/')
	if referer.find('?') > 0:
	    referer = referer[:referer.index('?')]
	sb_locals = {'msg':'',
		     'referer':referer,
		     'username':request.user.name,
		     'status':suddata.get_status(),
		     'inventory':suddata.get_inventory(),
		     'return_':return_,
		     'redirect':redirect,
		     'refresh':0,
		     'refresh_count':rf_count,
		    }

	try:
	    exec raw in sb_locals
	except StopScriptError, sse:
	    if len(sse.args) > 1:
		sb_locals['msg'] = sse.args[1]
	    if sse.args[0].startswith('redirect '):
		requested_redirect = sse.args[0].split(' ', 1)[1]
		if requested_redirect.find('://') > -1:
		    self.emsg = 'Illegal redirect to external page'
		    return
	except StandardError, se:
	    self.emsg = ''.join(format_exception(se.__class__, se, None))
	    return

	sbmsg = sb_locals.get('msg', '')
	if isinstance(sbmsg, list) or isinstance(sbmsg, tuple):
	    self.messages.extend(sbmsg)
	elif sbmsg:
	    self.messages.append(unicode(sbmsg))

	try:
	    suddata.set_status(sb_locals.get('status', 0))
	except ValueError:
	    self.emsg = 'Invalid %s' % 'status'
	    return
	suddata.save()

	if requested_redirect:
	    if not request.form.has_key('debug'):
		url = requested_redirect
		if self.messages:
		    url += ('?', '&')[url.find('?') != -1] + 'message='
		    url += ',,'.join( \
			['%s:+%s' % (pagename, x.replace(' ', '+')) \
			 for x in self.messages])
		url = url.encode("utf-8")
		print '<script language="javascript">\n' + \
			'<!--\n' + \
			'document.location.replace("%s");\n' % url + \
			'//-->\n' + \
			'</script>\n'
		print 'This page redirects to %s using Javascript!' \
							% requested_redirect
		raise MoinMoinNoFooter
	    else:
		self.messages.append('Redirection to %s on hold (debug mode)' \
				% requested_redirect)
		del sb_locals['__builtins__']
		self.messages.append(str(sb_locals))

	try:
	    secs = int(sb_locals.get('refresh', 0))
	    if secs > 0:
		cmd = pagename + '?refreshed=%d' % (rf_count + 1)
		cmd = 'document.location.replace("%s")' % cmd
		print '<script language="javascript">\n' + \
			'<!--\n' + \
			"setTimeout('%s', %d);\n" % (cmd, secs*1000) + \
			'//-->\n' + \
			'</script>\n'
	except ValueError:
	    self.emsg = _('Invalid value for %s') % 'refresh'
	
    def format(self, formatter):
	if self.emsg:
	    self.request.write('<div class="error">%s</div>\n' % self.emsg)
	if self.messages:
	    self.request.write('<div id="message">\n')
	    for ii in self.messages:
		self.request.write('  %s<br />\n' % ii.encode("utf-8"))
	    self.request.write('</div>\n')

