#! /usr/bin/env python
# -*- coding: UTF-8 -*-
import datetime
from collections import defaultdict
import operator
from helpers import *
import sys
import db

def getUser():
	while 1:
		string = raw_input('user? ')
		session = Session()
		user = search_user(string, session)
		session.close()
		if not isinstance(user, list):	
			return user.name
		i=0
		if len(user)==0:
			print 'no matching string'
		if len(user)==1:
			print 'antar: ', user[0].name, '\n'
			return user[0].name
		if len(user)>10:
			continue
		for u in user:
			print i, u.name
			i += 1
		try:
			n = int(raw_input ('enter number:'))
		except:
			print 'invalid input, restarting'
			continue
		if (n>-1) and (n<i):
			return user[n].name
def getProduct():	
	while 1:
		string = raw_input('product? ')
		session = Session()
		product = search_product(string, session)
		session.close()
		if not isinstance(product, list):	
			return product.name
		i=0
		if len(product)==0:
			print 'no matching string'
		if len(product)==1:
			print 'antar: ', product[0].name, '\n'
			return product[0].name
		if len(product)>10:
			continue
		for u in product:
			print i, u.name
			i += 1
		try:
			n = int(raw_input ('enter number:'))
		except:
			print 'invalid input, restarting'
			continue
		if (n>-1) and (n<i):
			return product[n].name

class Database:
	#for varer
	varePersonAntall = defaultdict(dict) #varePersonAntall[Oreo][trygvrad] == 3
	vareDatoAntall = defaultdict(list) #dict->array
	vareUkedagAntall = defaultdict(list)
	#for personer
	personVareAntall = defaultdict(dict) #personVareAntall[trygvrad][Oreo] == 3
	personVareVerdi = defaultdict(dict) #personVareVerdi[trygvrad][Oreo] == 30 #[kr]
	personDatoVerdi = defaultdict(list) #dict->array
	personUkedagVerdi = defaultdict(list)
	#for global
	personPosTransactions = {} # personPosTransactions[trygvrad] == 100 #trygvrad har lagt 100kr i boksen
	personNegTransactions = {} # personNegTransactions[trygvrad» == 70 #trygvrad har tatt 70kr fra boksen
	globalVareAntall = {}#globalVareAntall[Oreo] == 3
	globalVareVerdi = {}#globalVareVerdi[Oreo] == 30 #[kr]
	globalPersonAntall = {}#globalPersonAntall[trygvrad] == 3
	globalPersonForbruk = {}#globalPersonVerdi == 30 #[kr]
	globalUkedagForbruk = []
	globalDatoVarer = [] 
	globalDatoForbruk = []
	pengebeholdning = []

class InputLine:
	def __init__(self, u, p, t):
		self.inputUser = u
		self.inputProduct = p
		self.inputType = t

def getDateDb(date, inp):
	try:
		year = inp.partition('-')
		month = year[2].partition('-')
		return datetime.datetime(int(year[0]), int(month[0]), int(month[2]))
	except:
		print 'invalid date, setting date to date found in db'
		print date
		return date

def dateToDateNumDb(date, startDate):
	deltaDays = date-startDate
	return int(deltaDays.days), date.weekday()

def getInputType():
	inp = 0
	while not (inp == '1' or inp == '2' or inp == '3' or inp == '4'):
		print 'type 1 for user-statistics'
		print 'type 2 for product-statistics'
		print 'type 3 for global-statistics'
		print 'type 4 to enter loop-mode'
		inp = raw_input('')
	return int(inp)

def getProducts(products):
	product = []
	products = products.partition('¤')
	product.append(products[0])
	while (products[1]=='¤'):
		products = products[2].partition('¤')
		product.append(products[0])
	return product

def getDateFile(date, inp):
	try:
		year = inp.partition('-')
		month = year[2].partition('-')
		return datetime.date(int(year[0]), int(month[0]), int(month[2]))	
	except:
		print 'invalid date, setting date to date found on file file'
		print date
		return datetime.date(int(date.partition('-')[0]), int(date.partition('-')[2].partition('-')[0]), int(date.partition('-')[2].partition('-')[2]))

def dateToDateNumFile(date, startDate):
	year = date.partition('-')
	month = year[2].partition('-')
	day =  datetime.date(int(year[0]), int(month[0]), int(month[2]))
	deltaDays = day-startDate
	return int(deltaDays.days), day.weekday()
	
def clearDatabase(database):
	database.varePersonAntall.clear()
	database.vareDatoAntall.clear()
	database.vareUkedagAntall.clear()
	database.personVareAntall.clear()
	database.personVareVerdi.clear()
	database.personDatoVerdi.clear()
	database.personUkedagVerdi.clear()
	database.personPosTransactions.clear()
	database.personNegTransactions.clear()
	database.globalVareAntall.clear()
	database.globalVareVerdi.clear()
	database.globalPersonAntall.clear()
	database.globalPersonForbruk.clear()
	return(database)

def addLineToDatabase(database, inputLine):
	if abs(inputLine.price)>90000:
		return database
	#fyller inn for varer
	if (not inputLine.product=='') and ((inputLine.inputProduct=='') or (inputLine.inputProduct==inputLine.product)):
		database.varePersonAntall[inputLine.product][inputLine.user] = database.varePersonAntall[inputLine.product].setdefault(inputLine.user,0) + 1	
		if inputLine.product not in database.vareDatoAntall:
			database.vareDatoAntall[inputLine.product] = [0]*(inputLine.numberOfDays+1)
		database.vareDatoAntall[inputLine.product][inputLine.dateNum] += 1
		if inputLine.product not in database.vareUkedagAntall:
			database.vareUkedagAntall[inputLine.product] = [0]*7
		database.vareUkedagAntall[inputLine.product][inputLine.weekday] += 1
	#fyller inn for personer
	if (inputLine.inputUser=='') or (inputLine.inputUser==inputLine.user):
		if not inputLine.product == '':
			database.personVareAntall[inputLine.user][inputLine.product] = database.personVareAntall[inputLine.user].setdefault(inputLine.product,0) + 1
			database.personVareVerdi[inputLine.user][inputLine.product] = database.personVareVerdi[inputLine.user].setdefault(inputLine.product,0) + inputLine.price
			if inputLine.user not in database.personDatoVerdi:
				database.personDatoVerdi[inputLine.user] = [0]*(inputLine.numberOfDays+1)
			database.personDatoVerdi[inputLine.user][inputLine.dateNum] += inputLine.price
			if inputLine.user not in database.personUkedagVerdi:
				database.personUkedagVerdi[inputLine.user] = [0]*7
			database.personUkedagVerdi[inputLine.user][inputLine.weekday] += inputLine.price
	#fyller inn delt statistikk (genereres uansett)
	if (inputLine.product==''):
		if (inputLine.price>0):
			database.personPosTransactions[inputLine.user] = database.personPosTransactions.setdefault(inputLine.user,0) + inputLine.price
		else:
			database.personNegTransactions[inputLine.user] = database.personNegTransactions.setdefault(inputLine.user,0) + inputLine.price
	elif not (inputLine.inputType==1):
		database.globalVareAntall[inputLine.product] = database.globalVareAntall.setdefault(inputLine.product,0) + 1
		database.globalVareVerdi[inputLine.product] = database.globalVareVerdi.setdefault(inputLine.product,0) + inputLine.price
					
	#fyller inn for global statistikk
	if (inputLine.inputType==3) or (inputLine.inputType==4):
		database.pengebeholdning[inputLine.dateNum] += inputLine.price
		if not (inputLine.product==''):
			database.globalPersonAntall[inputLine.user] = database.globalPersonAntall.setdefault(inputLine.user,0) + 1
			database.globalPersonForbruk[inputLine.user] = database.globalPersonForbruk.setdefault(inputLine.user,0) + inputLine.price
			database.globalDatoVarer[inputLine.dateNum] += 1
			database.globalDatoForbruk[inputLine.dateNum] += inputLine.price
			database.globalUkedagForbruk[inputLine.weekday] += inputLine.price
	return database

def buildDatabaseFromDb(inputType, inputProduct, inputUser):
	sdate = raw_input('enter start date (yyyy-mm-dd)? ')
	edate = raw_input('enter end date (yyyy-mm-dd)? ')
	print 'building database...'	
	session = Session()
	transaction_list = session.query(Transaction).all()
	inputLine = InputLine(inputUser, inputProduct, inputType)
	startDate = getDateDb(transaction_list[0].time, sdate)
	endDate = getDateDb(transaction_list[-1].time, edate)
	inputLine.numberOfDays = (endDate-startDate).days
	database = Database()
	database = clearDatabase(database)

	if (inputType==3) or (inputType==4):
		database.globalDatoVarer = [0]*(inputLine.numberOfDays+1)
		database.globalDatoForbruk = [0]*(inputLine.numberOfDays+1)
		database.globalUkedagForbruk = [0]*7
		database.pengebeholdning = [0]*(inputLine.numberOfDays+1)
	print 'wait for it.... '
	for transaction in transaction_list:
		if transaction.purchase:
			products = map(lambda ent: ent.product.name, transaction.purchase.entries)
		else:
			products = []
			products.append('')
		inputLine.dateNum, inputLine.weekday = dateToDateNumDb(transaction.time, startDate)
		if inputLine.dateNum<0 or inputLine.dateNum>(inputLine.numberOfDays):
			continue
		inputLine.user=transaction.user.name
		inputLine.price=transaction.amount
		for inputLine.product in products:	
			database=addLineToDatabase(database, inputLine )
			inputLine.price = 0;

	print 'saving as default.dibblerlog...',
	f=open('default.dibblerlog','w')	
	line_format = '%s|%s|%s|%s|%s|%s\n'
	transaction_list = session.query(Transaction).all()
	for transaction in transaction_list:
		if transaction.purchase:
			products = u'¤'.join(map(lambda ent: ent.product.name, transaction.purchase.entries))
			description = ''
		else:
			products = ''
			description = transaction.description
		line = line_format % ('purchase', transaction.time, products, transaction.user.name, transaction.amount, transaction.description)
		f.write(line.encode('utf8'))
	session.close()
	f.close
	#bygg database.pengebeholdning
	if (inputType==3) or (inputType==4):
		for i in range(inputLine.numberOfDays+1):
			if i > 0:
				database.pengebeholdning[i] +=database.pengebeholdning[i-1]
	#bygg dateLine
	day=datetime.timedelta(days=1)
	dateLine=[]
	dateLine.append(startDate)
	for n in range(inputLine.numberOfDays):
		dateLine.append(startDate+n*day)
	print 'done'
	return database, dateLine

def buildDatabaseFromFile(inputFile, inputType, inputProduct, inputUser):
	sdate = raw_input('enter start date (yyyy-mm-dd)? ')
	edate = raw_input('enter end date (yyyy-mm-dd)? ')
		
	f=open(inputFile)
	try:
		fileLines=f.readlines()
	finally:
		f.close()
	inputLine = InputLine(inputUser, inputProduct, inputType)
	startDate = getDateFile(fileLines[0].partition('|')[2].partition(' ')[0], sdate)
	endDate = getDateFile(fileLines[-1].partition('|')[2].partition(' ')[0], edate)
	inputLine.numberOfDays = (endDate-startDate).days
	database = Database()
	database = clearDatabase(database)

	if (inputType==3) or (inputType==4):
		database.globalDatoVarer = [0]*(inputLine.numberOfDays+1)
		database.globalDatoForbruk = [0]*(inputLine.numberOfDays+1)
		database.globalUkedagForbruk = [0]*7
		database.pengebeholdning = [0]*(inputLine.numberOfDays+1)
	for linje in fileLines:
		if not (linje[0]=='#') and not (linje=='\n') :
			#henter dateNum, products, user, price
			restDel = linje.partition('|')
			restDel = restDel[2].partition(' ')
			inputLine.dateNum, inputLine.weekday = dateToDateNumFile(restDel[0], startDate)
			if inputLine.dateNum<0 or inputLine.dateNum>(inputLine.numberOfDays):
				continue
			restDel=restDel[2].partition('|')
			restDel=restDel[2].partition('|')
			products = restDel[0]
			restDel=restDel[2].partition('|')
			inputLine.user=restDel[0]
			inputLine.price=int(restDel[2].partition('|')[0])
			for inputLine.product in getProducts(products):	
				database=addLineToDatabase(database, inputLine )
				inputLine.price = 0;
	#bygg database.pengebeholdning
	if (inputType==3) or (inputType==4):
		for i in range(inputLine.numberOfDays+1):
			if i > 0:
				database.pengebeholdning[i] +=database.pengebeholdning[i-1]
	#bygg dateLine
	day=datetime.timedelta(days=1)
	dateLine=[]
	dateLine.append(startDate)
	for n in range(inputLine.numberOfDays):
		dateLine.append(startDate+n*day)
	return database, dateLine

def printTopDict(dictionary, n, k):
	i=0
	for key in sorted(dictionary, key=dictionary.get, reverse=k):
		print key, ': ',dictionary[key] 
		if i<n:
			i += 1
		else:
			break

def printTopDict2(dictionary, dictionary2, n):
	print ''
	print 'product :  price[kr] ( number )'
	i=0
	for key in sorted(dictionary, key=dictionary.get, reverse=True):
		print key, ': ',dictionary[key], ' (', dictionary2[key], ') ' 
		if i<n:
			i += 1
		else:
			break

def printWeekdays(week, days):
	if week==[] or days==0:
		return
	print 'mon: ', '%.2f'%(week[0]*7.0/days), ' tue: ', '%.2f'%(week[1]*7.0/days), ' wen: ', '%.2f'%(week[2]*7.0/days), ' thu: ', '%.2f'%(week[3]*7.0/days), ' fri: ', '%.2f'%(week[4]*7.0/days), ' sat: ','%.2f'%( week[5]*7.0/days), ' sun: ', '%.2f'%(week[6]*7.0/days)
	print 'forbruk per dag (snitt): ', '%.2f'%(sum(week)*1.0/days)
	print ''

def printBalance(database, user):
	forbruk = 0
	if (user in database.personVareVerdi):
		forbruk = sum([i for i in database.personVareVerdi[user].values()])
		print 'totalt kjøpt for: ', forbruk, 
	if  (user in database.personNegTransactions):
		print 'kr, totalt lagt til: ', -database.personNegTransactions[user], 
		forbruk=-database.personNegTransactions[user]-forbruk
	if  (user in database.personPosTransactions):
		print 'kr, totalt tatt fra boks: ', database.personPosTransactions[user], 
		forbruk=forbruk-database.personPosTransactions[user]
	print 'balanse: ', forbruk, 'kr',
	print ''

def printUser(database, dateLine, user, n):
	printTopDict2(database.personVareVerdi[user], database.personVareAntall[user], n)
	print '\nforbruk per ukedag [kr/dag],',
	printWeekdays(database.personUkedagVerdi[user], len(dateLine))
	printBalance(database, user)

def printProduct(database, dateLine, product, n):
	printTopDict(database.varePersonAntall[product], n, 1)
	print '\nforbruk per ukedag [antall/dag],',
	printWeekdays(database.vareUkedagAntall[product], len(dateLine))
	print 'Det er solgt: ', database.globalVareAntall[product], product, 'til en verdi av: ', database.globalVareVerdi[product], 'kr' 

def printGlobal(database, dateLine, n):
	print '\nmest lagt til: '
	printTopDict(database.personNegTransactions, n, 0)
	print '\nmest tatt fra:'
	printTopDict(database.personPosTransactions, n, 1)
	print '\nstørst forbruk:'
	printTopDict(database.globalPersonForbruk, n, 1)
	printTopDict2(database.globalVareVerdi, database.globalVareAntall, n)
	print '\nforbruk per ukedag [kr/dag],',
	printWeekdays(database.globalUkedagForbruk, len(dateLine))
	print 'Det er solgt varer til en verdi av: ', sum(database.globalDatoForbruk), 'kr, det er lagt til', -sum([i for i in database.personNegTransactions.values()]), 'og tatt fra', sum([i for i in database.personPosTransactions.values()]),
	print 'balansen blir:', database.pengebeholdning[len(dateLine)-1], 'der negative verdier representerer at brukere har kreditt tilgjengelig'

def alt4menuTextOnly(database, dateLine):
	n=10
	while 1:		
		print '\n1: user-statistics, 2: product-statistics, 3:global-statistics, n: adjust amount of data shown q:quit'
		inp = raw_input('')
		if inp == 'q':
			break
		elif inp == '1':
			try:
				printUser(database, dateLine, getUser(), n)
			except:
				print '\n\nSomething is not right, (last date prior to first date?)'
		elif inp == '2':
			try:
				printProduct(database, dateLine, getProduct(), n)
			except:
				print '\n\nSomething is not right, (last date prior to first date?)'
		elif inp == '3':
			try:
				printGlobal(database, dateLine, n)
			except:
				print '\n\nSomething is not right, (last date prior to first date?)'
		elif inp == 'n':
			n=int(raw_input('set number to show '));

def statisticsTextOnly():
	inputType = 4
	product = ''
	user = '' 
	print '\n0: from file, 1: from database, q:quit'
	inp = raw_input('')
	if inp == '1':
		database, dateLine = buildDatabaseFromDb(inputType, product, user)
	elif inp=='0' or inp == '':
		database, dateLine = buildDatabaseFromFile('default.dibblerlog', inputType, product, user)
	if not inp == 'q':
		alt4menuTextOnly(database, dateLine)
