#!/usr/bin/env python
# -*- coding: utf-8 -*-

import sys
import time
import math
import gv
from optparse import OptionParser

import keys
import stats as st
import modifier as mod

# [<index>, <distance>, <shift>, <plain>, <algr+shift>, <altgr>]
KEYINFO_MODES = [0,1,2,3]

KEYINFO =  [
		[
			[0, 48, "ˇ", "~", "¬", "¬"],  [1, 38, "1" ,"!", "¹", "¡"],[2, 35, "2", '"', "@", "⅛"], [3, 36, "3", "#", "£", "£"],[4, 36, "4", "¤", "$", "$"],  [5, 36, "5", "%", "½", "⅜"],
			[6, 45, "6", "&", "¬", "⅝"],  [7, 40, "7", "/", "{", "⅞"], [8, 40, "8", "(", "[", "™"], [9, 40, "9",")", "]", "±"], [10, 40, "0", "=", "}", "°"],[11, 40, "+", "?", "\\", "¿"],
			[12, 45, "´","`"],[13, 60, ""]
		],
		[
			[14, 30, "", "\t"], [15, 19, "q", "Q", "@", "Ω"],  [16, 19, "w","W", "ł", "Ł"],[17, 19, "e", "E", "€", "¢"],[18, 19, "r", "R", "¶", "®"], [19, 23, "t", "T", "ŧ", "Ŧ"],
			[20, 30, "y", "Y", "←", "¥"], [21, 19, "u", "U", "↓", "↑"],  [22, 19, "i", "I", "→", "ı"],[23, 19, "o", "O", "ø", "Ø"],[24, 19, "p", "P", "þ", "Þ"],[25, 23, "ü", "Ü", "¨", "°"],
			[26, 38, "õ", "Õ", "§", "¯"]
		],
		[    
			[28, 20, ""], [29, 0, "a","A", "æ", "Æ"],[30, 0, "s", "S", "š", "Š"],[31, 0, "d", "D", "ð", "Ð"],[32, 0, "f", "F", "đ", "ª"], [33, 19, "g", "G", "ŋ", "Ŋ"],
			[34, 19, "h", "H", "ħ", "Ħ"],[35, 0, "j", "J"],[36, 0, "k", "K", "ĸ", "&"],[37, 0, "l", "L", "ł", "Ł"],[38, 0, "ö", "Ö", "´", "˝"],[39, 19, "ä", "Ä", "^", "ˇ"],
			[61, 38, "'", "*", "½", "˘"],[40, 55, "", "\n"]
		],
		[    
			[41, 32, "lshift"],[27, 20, "<", ">", "|", "¦"],[42, 20, "z","Z", "ž", "Ž"],[43, 20, "x",  "X", "»", ">"],[44, 20, "c", "C", "¢", "©"],[45, 20, "v","V", "“", "‘"], [46, 35, "b", "B", "”", "’"],
			[47, 22, "n", "N"],[48, 20, "m", "M", "µ", "º"],[49, 20, ",", ";","<", "×"],[50, 20, ".", ":", ">", "÷"],[51, 20, "-","_", "", ""],[52, 23, "rshift"],
		],
		[    
			[53, 0, ""],[54, 0, ""],[55, 0, ""],[56, 0, " "],[57, 20, "altgr"],[58, 0,  ""],[59, 0, ""],[60, 0, ""]
		]
	]
	#~ \[([^,]*,\s*)[^,]*,\s*([^,]*,\s*)([^,]*,\s*)([^,]*),\s*([^,]*)\]

KEYBOARD =  [ 
			[
				[0, 48, "ˇ", False], [1, 38, "1", False], [2, 35, "2", False], [3, 36, "3", False], [4, 36, "4", False], [5, 36, "5", False], [6, 45, "6", False],
				[7, 40, "7", False], [8, 40, "8", False], [9, 40, "9", False], [10, 40, "0", False], [11, 40, "+", False], [12, 45, "´", False ], [13, 60, "backspace", False]
            ],
            [    
				[14, 28, "\t", False], [15, 19, "q",],  [16, 19, "w"],[17, 19, "e"],[18, 19, "r"], [19, 23, "t"],
				[20, 30, "y"], [21, 19, "u"],  [22, 19, "i"],[23, 19, "o" ],[24, 19, "p" ],[25, 23, "ü"],[26, 38, "õ"]
            ],
            [    
				[28, 20, "capslock", False], [29, 0, "a"],[30, 0, "s"],[31, 0, "d"], [32, 0, "f"], [33, 19, "g"], [34, 19, "h"],
                [35, 0, "j"], [36, 0, "k"], [37, 0, "l"], [38, 0, "ö"], [39, 19, "ä"], [40, 38, "'"],[41, 50, "\n", False]
            ],
            [    
				[42, 34, "lshift", False],[43, 20, "<"],[44, 20, "z"],[45, 20, "x"],[46, 20, "c"],[47, 20, "v"], [48, 35, "b"],
				[49, 22, "n"], [50, 20, "m"], [51, 20,","], [52, 20, "."], [53, 20, "-"], [54, 23, "rshift", False],
            ],
            [    
				[55, 0, "lctrl", False] ,[56, 0, "meta", False], [57, 0, "alt", False], [58, 0, " ", False], [59, 20, "altgr", False],
				[60, 0, "context", False], [61, 0, "rctrl", False],[62,0, "", False]
			]
           ]           

KEYMAPS = {'querty': 
	[[0,1,2,3,4,5,6,7,8,9,10,11,12,13],
	[14,15,16,17,18,19,20,21,22,23,24,25,26],
	[28,29,30,31,32,33,34,35,36,37,38,39,40],
	[41,27,42,43,44,45,46,47,48,49,50,51,52],
	[53,54,55,56,57,58,59]
	]
}

	#~ This array indicates a mapping of fingers to keys
	#~ 0 = thumb
	#~ 1 = left pinky
	#~ 2 = left ring finger
	#~ 3 = left middle finger
	#~ 4 = left index finger
	#~ 5 = right index finger
	#~ 6 = right middle finger 
	#~ 7 = right ring finger
	#~ 8 = right pinky

KEY_TO_FINGER = [[1,1,1,2,3,4,4,5,5,6,7,8,8,8],
				 [1,1,2,3,4,4,5,5,6,7,8,8,8],
				 [1,1,2,3,4,4,5,5,6,7,8,8,8,8], 
				 [1,1,1,2,3,4,4,5,5,6,7,8,8],
				 [4,4,0,0,0,5,5,5] 
                ]

OUTFILE = "speed.txt"

KEYINFO_MODE_NORMAL = 0
KEYINFO_MODE_SHIFT = 1
KEYINFO_MODE_ALTGR = 2
KEYINFO_MODE_ALTGRSHIFT = 3

KEY_TO_PRINT = {
			' ':'space',
			'\n':'enter',
			'.':'period',
			',':'comma',
			'-':'hpen', 
			':':'colon', 
			';':'scolon', 
			'!':'excl', 
			'?':'quest', 
			'/':'slash', 
			'_':'uscore', 
			"'":'ucomma',	
			}

print_to_key = dict()	
for key, val in KEY_TO_PRINT.iteritems():
	print_to_key[val] = key

KEYINFO_INDEX_MODE = 0
KEYINFO_INDEX_ROW = 1
KEYINFO_INDEX_COL = 2
KEYINFO_INDEX_FINGER = 3
KEYINFO_INDEX_DISTANCE = 4

def keyhash():
	info = [0, None, None, None, 0]
	keyinfo = dict()
	for i in range(len(KEYINFO)):
		row = KEYINFO[i]
		for j in range(len(row)):
			button = row[j]
			p1 = info[:]
			p1[KEYINFO_INDEX_ROW] = i
			p1[KEYINFO_INDEX_COL] = j
			p1[KEYINFO_INDEX_FINGER] = KEY_TO_FINGER[i][j]
			p1[KEYINFO_INDEX_DISTANCE] = button[1]
			
			p2 = p1[:]		 
			for k in range(len(button)-2):
				p2[KEYINFO_INDEX_MODE] = KEYINFO_MODES[k]
				keyinfo[button[k+2]] = p2
				p2 = p1[:]
		
	return keyinfo
	


POINTS_FINGER_COVERAGE = [0, 7, 4, 4, 8, 8, 4, 4, 10]
POINTS_FINGER = [0, 25, 23, 20, 27, 27, 20, 23, 25 ]
#~ POINTS_FINGER = [0, 25, 23, 20, 18, 18, 20, 23, 25 ]
POINTS_ROW = [35, 20, 15, 30, 0]

POINTS_WEIGHT_FINGER = 20
POINTS_WEIGHT_DISTANCE = 12
POINTS_WEIGHT_ROW = 12
POINTS_WEIGHT_COVERAGE = 0

BUTTONINFO_INDEX_POINTS = 5

def buttoninfo():
	buttoninfo = list()
	for i in range(len(KEYBOARD)):
		for j in range(len(KEYBOARD[i])):			
			button = KEYBOARD[i][j]
			if button[-1] != False:
				button.append(True)
				finger = KEY_TO_FINGER[i][j] 
				points = int(round(POINTS_FINGER[finger]*POINTS_WEIGHT_FINGER + POINTS_FINGER_COVERAGE[finger]*POINTS_WEIGHT_COVERAGE +
					POINTS_ROW[i]*POINTS_WEIGHT_ROW + POINTS_WEIGHT_DISTANCE*(button[1])))

				button.append(finger)
				button.append(points)
				buttoninfo.append(button)
	return buttoninfo
	
	
def buttoncmp(a,b):
	return a[BUTTONINFO_INDEX_POINTS] - b[BUTTONINFO_INDEX_POINTS]
	
def charcmp(b,a):
	return a['count'] - b['count']
	
def graph():
	G = gv.digraph("G")
	N = gv.protonode(G)
	E = gv.protoedge(G)

	#~ gv.setv(G, 'rankdir', 'LR')
	#~ gv.setv(G, 'nodesep', '0.05')
	#~ gv.setv(N, 'shape', 'box')
	gv.setv(N, 'width', '0')
	gv.setv(N, 'height', '0')
	gv.setv(N, 'margin', '.03')
	gv.setv(N, 'fontsize', '8')
	gv.setv(N, 'fontname', 'helvetica')
	gv.setv(E, 'arrowhead', 'none')
	#~ gv.setv(E, 'arrowsize', '.4')


	THRESHOLD = 40
	nodehash = dict()
	allow = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 's', 't']
	#~ stats = dict()
	for prev, row in stats.iteritems():
		nodehash[prev] = gv.node(G, prev.encode('utf-8'))
	
	visited = dict()
	for prev, row in stats.iteritems():	
		for next, weight in row.iteritems():
			if next in nodehash :
				#~ if 0 <= weight <= 1:
					#~ weight = 2
				#~ weight = 1./100*math.log(weight,2)
				
				if visited.has_key(next+prev):
					weight = 0
				else:
					if weight == 0:
						weight = 1
					weight2 = stats[next][prev]
					if weight2 ==0:
						weight2 = 1
					weight = (math.log(weight,2) + math.log(weight2, 2))*2
					visited[prev+next] = True
					
				#~ print weight
				if weight > THRESHOLD:
					e = gv.edge(nodehash[prev], nodehash[next])
					w = "%s" % weight
					#~ gv.setv(e, 'weight', w.encode('utf-8'))

		
			
	gv.layout(G, 'dot')
	gv.render(G, 'xlib')
			
			
def main(argv=None):

	if argv is None:
		argv = sys.argv[1:]
		
	parser = OptionParser(usage = "usage: %prog [options] FILE1 ...", version="%prog 0.1")
	#parser.add_option('-q', '--quiet',  action="store_false", dest = 'verbose', default=True, help="don't print status messages to stdout")
	parser.add_option('-f', '--file', dest='out_filename', default=OUTFILE, metavar="FILE", help="output file name")

	(options, datafiles) = parser.parse_args(args = argv)
	
	global stats
	stats = dict()
	str_lower = mod.Modifier(True)
	
	str_lower.registerModifier(str.decode, 'utf-8')
	#~ str_lower.registerModifier(unicode.lower)
	
	for filename in datafiles:
		f = file(filename, "r")
		
		lines = []
		for line in f:
			line = line.rstrip()
			lines.append(line.split("\t"))
			
		for i in range(1,len(lines)):
			
			prev = print_to_key.get(lines[i][0],lines[i][0])
			if prev in keys.KEYS:
				
				prev = str_lower(prev)
				if prev not in stats:
					stats[prev] = dict()
					stats[prev]['letter'] = prev
			
				#second = dict()
				for j in range(1,len(lines[i])):
					
					next = print_to_key.get(lines[0][j],lines[0][j])
					if next in keys.KEYS:
						next = str_lower(next)
						stats[prev][next] = stats[prev].get(next,0) + int(lines[i][j])
						#second[next] = int(lines[i][j])

			#stats[char] = second
		f.close()	
		
	#~ for prev, row in stats.iteritems():
		#~ print "'%s': {" % prev,
		#~ for next, val in row.iteritems():
			#~ print "'%s': %s," % (next,val),
		#~ print "},",
		
	keyinfo = keyhash()
	sorted_buttons = sorted(buttoninfo(), buttoncmp)
	sorted_stats = sorted(stats.values(), charcmp)
	
	#~ for i in range(len(sorted_buttons)):
		#~ print "%s -> %s" % (sorted_stats[i]['letter'], sorted_buttons[i][2].decode('utf-8'))
	#~ for i in range(len(sorted_buttons), len(sorted_stats)):
		#~ print "%s ->" % (sorted_stats[i]['letter'])
	
	
	#~ graph()
	#~ E = 1/N*sum(e_i)
	#~ e_i = k_b*b_i + k_p*p+i + k_s*s_i 
	#~ b_i = k_1*b_i1*(1+ k_2*b_i2*(1+k_3*b_i3))
	
	#~ p_i = k_1*p_i1*(1+ k_2*p_i2*(1+k_3*p_i3))
	#~ p_ij = w_0 + w_hand*P_hand_j + w_row*P_row_j + w_finger*P_finger_j
	
	#~ s_i = sum_j=hand,row,finger(f_i*p_j)
	
	M = 'main'
	I = 'inertia'
	P = 'penalty'
	S = 'stroke'
	configuration = dict()
	configuration['balaced'] = {
	#	kb, kp, ks
		M: [1,2,3],
	#	k1, k2, k3
		I: [1,2,3],
	#	w0, whand, wrow, wfinger
		P: [0,1,2,3],
	#	fhand, frow, ffinger	
		S: [1,2,3]
	}
	
	# hand: 
	#0 same
	#1 different
	
	#row:
		
	
	fingerUsage = [0,0,0,0,0,0,0,0,0]
	rowUsage = [0,0,0,0,0]

	keyCount = dict()
	keyCount['rshift'] = 0
	keyCount['lshift'] = 0
	keyCount['altgr'] = 0
	keyCount['sameHand'] = 0
	keyCount['sameFinger'] = 0
	keyCount['neighFinger'] = 0
	keyCount['distance'] = 0
	
	time_start = time.time()
	for prev, row in stats.iteritems():
		if prev in keyinfo:
			
			prev_finger = keyinfo[prev][KEYINFO_INDEX_FINGER]
			
			fingerUsage[prev_finger] += row['count']
			#~ print prev, keyinfo[prev][KEYINFO_INDEX_ROW], row['count']
			rowUsage[keyinfo[prev][KEYINFO_INDEX_ROW]] += row['count']
			
			if keyinfo[prev][KEYINFO_INDEX_MODE] & KEYINFO_MODE_ALTGR:
				keyCount['altgr'] += row['count']
				#~ print "altgr: ", prev, row['count']
				
			
			if (keyinfo[prev][KEYINFO_INDEX_MODE] & KEYINFO_MODE_SHIFT):
				if (0 < prev_finger < 5):
					keyCount['rshift'] += row['count']
					#~ print "rshift: ", prev, row['count']
				if  (4 < prev_finger < 9):
					#~ print "lshift: ", prev, row['count']
					keyCount['lshift'] += row['count']

							
			for next, val in row.iteritems():
				if next in keyinfo:
					next_finger = keyinfo[next][KEYINFO_INDEX_FINGER]
					if prev != next and next != 'count':

						keyCount['distance'] += (keyinfo[prev][KEYINFO_INDEX_DISTANCE]+keyinfo[next][KEYINFO_INDEX_DISTANCE])*val
						#~ if val > 0:
							#~ print "'%s': %s, '%s': %s > %s" % (prev, keyinfo[prev]['distance'], next ,keyinfo[next]['distance'], (keyinfo[prev]['distance'] + keyinfo[next]['distance']))
								
						if (0 < prev_finger <= next_finger < 5) or (4 < prev_finger <= next_finger < 9):
							keyCount['sameHand'] += val
							if prev_finger == next_finger:
								keyCount['sameFinger'] += val
							if abs(prev_finger - next_finger) == 1:
								keyCount['neighFinger'] += val
								
	keyCount['distance'] += 2*(keyCount['rshift']*keyinfo['rshift'][KEYINFO_INDEX_DISTANCE] + keyCount['lshift']*keyinfo['lshift'][KEYINFO_INDEX_DISTANCE] + keyCount['altgr']*keyinfo['altgr'][KEYINFO_INDEX_DISTANCE])

	time_end = time.time()
	print "Took time %.5f minutes" % ((time_end - time_start) /60.)
	
	print fingerUsage
	print rowUsage
	print keyCount
	
	
	
if __name__ == "__main__":
	sys.exit(main())
