#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Version 1.0
# Complain to Simos Xenitellis for this craft.

from re			import findall, match, split
from string		import atoi
from unicodedata	import normalize
from urllib 		import urlopen
from os.path		import isfile
from copy 		import copy

""" i.e. without the combining_ stuff """
FILENAME_COMPOSE = "Compose.opti"  

URL_KEYSYMSTXT = "http://www.cl.cam.ac.uk/~mgk25/ucs/keysyms.txt"
FILENAME_KEYSYMSTXT = "keysyms.txt"

def keysymvalue(keysym, keysymdatabase, file = "n/a", linenum = 0):
	""" Extracts a value from the keysym """
	""" Find the value of keysym, using the data from keysyms """
	""" Use file and linenum to when reporting errors """
       	if keysymdatabase.has_key(keysym):
               	return keysymdatabase[keysym]
       	elif keysym[0] == 'U' and match('[0-9a-fA-F]+$', keysym[1:]):
               	return atoi(keysym[1:], 16)
       	else:
        	print 'UNKNOWN{%(keysym)s}' % { "keysym": keysym }
               	exit(-1)

def all_permutations(seq):
	""" Borrowed from http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/252178 """
    	if len(seq) <=1:
    	    yield seq
    	else:
    	    for perm in all_permutations(seq[1:]):
    	        for i in range(len(perm)+1):
    	            #nb str[0:1] works in both string and list contexts
        	        yield perm[:i] + seq[0:1] + perm[i:]

if not isfile(FILENAME_KEYSYMSTXT):
	keysymstxt = urllib.urlopen(URL_KEYSYMSTXT)
	localFile = open(URL_KEYSYMSTXT.split('/')[-1], 'w')
	localFile.write(keysymstxt.read())
	keysymstxt.close()
	localFile.close()

""" Reads up the keysyms.txt file that Markus Khun maintains """
""" This file keeps a record between keysyms <-> unicode chars """
linenum_keysymstxt = 0
keysymstxt = open(FILENAME_KEYSYMSTXT, 'r')
keysymdatabase = {}
for line in keysymstxt.readlines():
	linenum_keysymstxt += 1
	line = line.strip()
	if line == "" or match('^#', line):
		continue
	components = split('\s+', line)
	if len(components) < 5:
		print "Invalid line %(linenum)d in %(filename)s: %(line)s'"\
		% {'linenum': linenum_keysymstxt, 'filename': FILENAME_KEYSYMSTXT, 'line': line}
		print "Was expecting 5 items in the line"
		exit(-1)
	if components[1][0] == 'U' and match('[0-9a-fA-F]+$', components[1][1:]):
		unival = atoi(components[1][1:], 16)
	if unival == 0:
		continue
	keysymdatabase[components[4]] = unival
keysymstxt.close()

""" Here we add some items that did not make it in the list of Markus """
keysymdatabase['Oslash'] = 0x0d8

""" This is wrong in Xorg, we fix for our testing """
keysymdatabase['dead_tilde'] = 0x342
keysymdatabase['combining_tilde'] = 0x342
keysymdatabase['dead_stroke'] = 0x337
keysymdatabase['dead_psili'] = 0x313
keysymdatabase['dead_dasia'] = 0x314
keysymdatabase['Multi_key'] = 0xff20

composefile = open(FILENAME_COMPOSE, 'r');
composesequences = []
linenum_compose = 0
for line in composefile.readlines():
	linenum_compose += 1
	line = line.strip()
	if line is "" or match("XCOMM", line) or match("#", line):
		continue

	line = line[:-1]
	components = split(':', line)
	if len(components) != 2:
		print "Invalid line %(linenum_compose)d in %(filename)s: No sequence\
		/value pair found" % { "linenum_compose": linenum_compose, "filename": FILENAME_COMPOSE }
		exit(-1)
	(seq, val ) = split(':', line)
	seq = seq.strip()
	val = val.strip()
	sequence = findall('\w+', seq)
	values = split('\s+', val)
	unichar_temp = split('"', values[0])
	unichar = unichar_temp[1]
	codepointstr = values[1]
	if codepointstr[0] == 'U' and match('[0-9a-fA-F]+$', codepointstr[1:]):
		codepoint = atoi(codepointstr[1:], 16)
	elif keysymdatabase.has_key(codepointstr):
		codepoint = keysymdatabase[codepointstr]
	else:
		print
		print "Invalid codepoint at line %(linenum_compose)d in %(filename)s:\
		 %(line)s" % { "linenum_compose": linenum_compose, "filename": FILENAME_COMPOSE, "line": line }
		exit(-1)
	if "Multi_key" not in sequence:
		if codepoint < 0xFFFF:
			original_sequence = copy(sequence)
			base = sequence.pop()
			basechar = keysymvalue(base, keysymdatabase, FILENAME_COMPOSE, linenum_compose)
			
			if basechar < 0xFFFF:
				unisequence = []
				for i in range(0, len(sequence)):
					unisequence.append(unichr(keysymvalue(sequence.pop(), keysymdatabase, FILENAME_COMPOSE, linenum_compose)))
					
				counter = 0
				not_normalised = True
				for perm in all_permutations(unisequence):
					counter += 1
					normalized = normalize('NFC', unichr(basechar) + "".join(perm))
					if len(normalized) == 1:
						"""
						print 'Base: %(base)s [%(basechar)s], produces [%(unichar)s] (0x%(codepoint)04X)' \
						% { "base": base, "basechar": unichr(basechar), "unichar": unichar, "codepoint": codepoint },
						print "Normalized: [%(normalized)s] SUCCESS %(c)d" % { "normalized": normalized, "c": counter }
						"""
						not_normalised = False
						break;
				if not_normalised:
					original_sequence.append(codepoint)
					composesequences.append(original_sequence)
					""" print composesequences[-1] """
					
			else:
				print "Error"
				exit(-2)
	else:
		sequence.append(codepoint)
		composesequences.append(sequence)
		""" print composesequences[-1] """

def sequence_cmp(x, y):
	if keysymvalue(x[0], keysymdatabase) > keysymvalue(y[0], keysymdatabase):
		return 1
	elif keysymvalue(x[0], keysymdatabase) < keysymvalue(y[0], keysymdatabase):
		return -1
	elif len(x) > len(y):
		return 1
	elif len(x) < len(y):
		return -1
	elif keysymvalue(x[1], keysymdatabase) > keysymvalue(y[1], keysymdatabase):
		return 1
	elif keysymvalue(x[1], keysymdatabase) < keysymvalue(y[1], keysymdatabase):
		return -1
	elif len(x) < 4:
		return 0
	elif keysymvalue(x[2], keysymdatabase) > keysymvalue(y[2], keysymdatabase):
		return 1
	elif keysymvalue(x[2], keysymdatabase) < keysymvalue(y[2], keysymdatabase):
		return -1
	elif len(x) < 5:
		return 0
	elif keysymvalue(x[3], keysymdatabase) > keysymvalue(y[3], keysymdatabase):
		return 1
	elif keysymvalue(x[3], keysymdatabase) < keysymvalue(y[3], keysymdatabase):
		return -1
	elif len(x) < 6:
		return 0
	elif keysymvalue(x[4], keysymdatabase) > keysymvalue(y[4], keysymdatabase):
		return 1
	elif keysymvalue(x[4], keysymdatabase) < keysymvalue(y[4], keysymdatabase):
		return -1
	else:
		return 0

composesequences.sort(sequence_cmp)

firstitem = ""
num_firstitem = 0
zeroes = 0
num_entries = 0
for sequence in composesequences:
	if firstitem != sequence[0]: 
		firstitem = sequence[0]
		num_firstitem += 1
	zeroes += 6 - len(sequence) + 1
	num_entries += 1

print "Number of different first items 	: ", num_firstitem
print "Number of zeroes in list        	: ", zeroes
print "Number of max bytes	 	: ", num_entries * 2 * 6
print "Number of savings		: ", zeroes * 2 - num_firstitem * 2 * 5
