# -*- coding: utf-8 -*-
# This file is part of k4ever, a point-of-sale system
#   Contact............  <k4ever@lists.someserver.de>
#   Website............  http://k4ever.someserver.de/
#   Bug tracker........  http://k4ever.someserver.de/report
#
# Licensed under GNU Affero General Public License v3 or later

from django.db import models
from django.contrib.auth.models import User
import datetime
from decimal import Decimal
from django.db.models.signals import pre_delete, post_delete

# Create your models here.
class BuyableType(models.Model):
	""" Type/Category for a buyable object """
	name = models.CharField(max_length=100)
	
	def __unicode__(self):
		return self.name
	
	def itemcount(self):
		return self.buyable_set.values().count()


class Buyable(models.Model):
	""" Represents a buyable item. """
	name = models.CharField(max_length=100)
	price = models.DecimalField(max_digits=8, decimal_places=2)
	image = models.ImageField(upload_to='img/buyable/', help_text="<strong>The Image needs to have a 1:1 aspect ratio.</strong>")
	deposit = models.DecimalField(max_digits=8, decimal_places=2)
	description = models.TextField()
	buyableType = models.ManyToManyField(BuyableType)
	barcode = models.CharField(max_length=100, default='', blank=True)
	

	def hasDeposit(self):
		""" Returns True if the item has deposit. """
		return self.deposit > Decimal(0)
	
	def __unicode__(self):
		item = "%s (%.2f EUR" % (self.name, self.price)
		if self.hasDeposit():
			item += "/%.2f Pfand" % self.deposit
		item += ")"
		return item
	
	def types(self):
		typelist = ""
		for item in self.buyableType.values_list():
			typelist += item[1] + ", "
		return typelist.rstrip(u", ")
	
class Order(models.Model):
	""" Represents an order by the user, consists of several :class:`Purchases <Purchase>`. """
	user = models.ForeignKey(User)
	price = models.DecimalField(max_digits=8, decimal_places=2, default=Decimal(0))
	dateTime = models.DateTimeField(auto_now_add=True)
	
	def addItems(self, items):
		for item in items:
			self.purchase.add(item)
			self.price += item.price
	
	def updatePrice(self, instance, price, saveOrder=True):
		""" Update price of order, save Order if saveOrder is True. """
		self.price += price
		if self.id and saveOrder:
			self.save()
	
	def __unicode__(self):
		return "Price %s, User %s" % (self.price, self.user)
	
	def itemList(self):
		l = ""
		for item in self.purchase_set.all():
			l += item.buyable.name + u", "
		return l.rstrip(u", ")
	
	def save(self, *args, **kwargs):
		profile = self.user.get_profile()
		if self.id == None:
			# new item, get it!
			profile.balance -= self.price
			profile.save()
		else:
			# get old
			oldobj = Order.objects.get(id=self.id)
			if oldobj.user == self.user:
				profile = self.user.get_profile()
				profile.balance -= (self.price - oldobj.price)
				profile.save()
			else:
				oldProfile = oldobj.user.get_profile()
				newProfile = self.user.get_profile()
				oldProfile.balance += oldobj.price
				oldProfile.save()
				newprofile.balance -= self.price
				self.save()
		super(Order, self).save(*args, **kwargs)
	
	@staticmethod
	def pre_delete_signal(sender, instance, **kwargs):
		# Normally we would not need this function as for consistency
		# reasons and the behaviour of Buyables delete at deletion
		# the orders price should be 0. But for the sake of not getting
		# inconsistent with the users balance in the strangest situations
		# we will check that here. This means (at least) one extra query
		# per order deletion, as we don't get the updated object but the one
		# where all the buyables have not updated the price
		updOrder = Order.objects.get(id=instance.id)
		if updOrder.price != Decimal("0"):
			profile = updOrder.user.get_profile()
			profile.balance += updOrder.price
			profile.save()

pre_delete.connect(Order.pre_delete_signal, sender=Order)

class Purchase(models.Model):
	""" Represents a :class:`Buyable` in a :class:`Order`.
	
	Could be viewed as 'one bought item' or 'one item on the voucher'."""
	order = models.ForeignKey(Order)
	price = models.DecimalField(max_digits=8, decimal_places=2, default=None)
	isDeposit = models.BooleanField()
	buyable = models.ForeignKey(Buyable)
	
	def updatePrice(self):
		if self.isDeposit:
			self.price = self.buyable.deposit
		else:
			self.price = self.buyable.price
	
	def __unicode__(self):
		return "%s%s" % (self.buyable.name, (self.isDeposit and " (deposit)" or ""))

	def save(self, saveOrder=True, *args, **kwargs):
		if self.price == None:
			self.updatePrice()
		
		if self.order:
			if self.id == None:
				self.order.updatePrice(self, self.price, saveOrder=saveOrder)
			else:
				# object exists, update.
				oldobj = Purchase.objects.get(id=self.id)
				if oldobj.order == self.order:
					self.order.updatePrice(self, self.price - oldobj.price, saveOrder=saveOrder)
				else:
					oldobj.order.updatePrice(oldobj, -oldobj.price, saveOrder=saveOrder)
					self.order.updatePrice(self, self.price, saveOrder=saveOrder)
		super(Purchase, self).save(*args, **kwargs)
	
	@staticmethod
	def pre_delete_signal(sender, instance, **kwargs):
		# FIXME: For many items this could get very inefficient
		#        Find workarround!
		print "pre delete from purchase"
		instance.order.updatePrice(instance, -instance.price, saveOrder=True)

pre_delete.connect(Purchase.pre_delete_signal, sender=Purchase)
