# Michael Van Veen
# Market.py

import numpy as np
import pylab as graph
import operator
import random

from numpy.random import normal as gaussian
from Scientific.Statistics.Histogram import Histogram

from agent import *

# A market is a container class for agents

# Requirements:
#	1. Store and manage all agents within a market
#	1.1 Construct n agents from a set of global parameters. Draw individual agent parameters from a guassian distribution.
#	1.2 Iterate alive agents at every iteration
#	2. Coordinate buy and sell offers
#	2.1 Buy offers are selected randomly


# Note: maybe buyer seller queues should be heaps?
buy = tuple()
class Market():
	def __init__(self, num_agents, q1, q1_var, q2, q2_var, t1, t1_var, t2, t2_var, r1, r1_var, r2, r2_var):

		# Buyer Queue
		self._buy = []	

		# Seller Queues
		self._sellQ1 = []
		self._sellQ2 = []

		# List of agents
		self._agents = []
		
		# Construct agents from global parameters

		# Variance Percentages (from mean)
		#q1_var *= q1/num_agents
		#q2_var *= q2/num_agents
	
		#t1_var *= t1/num_agents
                #t2_var *= t2/num_agents

		#r1_var *= r1/num_agents
		#r2_var *= r2/num_agents

		# Generate Gaussians
		q1_val = gaussian(q1/num_agents, q1_var, num_agents).tolist()
		q2_val = gaussian(q2/num_agents, q2_var, num_agents).tolist()

		t1_val = gaussian(t1, t1_var, num_agents).tolist()
		t2_val = gaussian(t2, t1_var, num_agents).tolist()

		r1_val = gaussian(r1, r1_var, num_agents).tolist()
		r2_val = gaussian(r2, r2_var, num_agents).tolist()

		#self.sum = 0. 
                #for i in t1_val: 
		#	#print i
                #        self.sum += i
		#x_axis = []
		#y_axis = []
		#hg = Histogram(r2_val, 20)

		#print hg
		#for i in xrange(20):
		#	x_axis.append(hg[i][0])
		#	y_axis.append(hg[i][1])
		#graph.plot(x_axis, y_axis)
		#graph.axis()
		#graph.show()

		#print "Resource Total:	", str(q1)
		#print "Num Agents:	", str(num_agents)
		#print "Variance:	", str(np.std(q1_val))
		#print "Mean:		", str(np.mean(q1_val))
		#print "Budget Error(%):", str(100*(1.0 - self.sum/q1))
		#print "---"

		#Construct agents list
		for i in zip(q1_val, q2_val, r1_val, r2_val, t1_val, t2_val):
			self._agents.append(Agent(i[0], i[1], i[2], i[3], i[4], i[5], self))

	def makeSell1(self, other, amount, price):
		#print "lolwhat2"
		self._sellQ1.append((other, amount, price)) 
		other._sellq1 = amount

	def makeBuy1(self, other, amount, price):
		#self._buyQ1.append((other, amount, price))
		self._buy.append((other, amount, price, True))
		other._buyq1 = amount

	def makeSell2(self, other, amount, price):
		#print "lolwhat"
		self._sellQ2.append((other, amount, price)) 
		other._sellq2 = amount

	def makeBuy2(self, other, amount, price):
		#self._buyQ2.append((other, amount, price))
		self._buy.append((other, amount, price, False))
		other._buyq2 = amount
	
	def getAgents(self):
		return(self._agents)

	#Grab a random buyer, and remove it from buyer list
	def grabBuy(self):
			int		= random.randint(0, len(self._buy)-1)
			temp 	= self._buy[int]
			self._buy.__delitem__(int)
			return(temp)
	
	def iterate(self, n):
		for i in xrange(n):
			print i
			for agent in self._agents:
				agent.iterate()

			print "Buy length:   ", len(self._buy)
			print "Sell1 length: ", len(self._sellQ1)
			print "Sell2 length: ", len(self._sellQ1)
			print "\n"

			self._sellQ1.sort(key=operator.itemgetter(2))
			self._sellQ2.sort(key=operator.itemgetter(2))
			
			while(len(self._buy)>0):

				buy = self.grabBuy()
				#print "buy: ", buy
				# buy[3] tells us what type of buy this is
				if buy[3] == True:
					# Buyer is ordering quantity 1
					self.transact(buy, self._sellQ1, n, True)
				else:
					# Buyer is ordering quantity 2
					self.transact(buy, self._sellQ2, n, False)

			self._sellQ1 = []
			self._sellQ2 = []

	def transact(self, buyer, sellers, n, is_buy1):
		#print sellers
		try:
			seller = sellers[0]
		except IndexError:
			#print "Length: ", len(sellers)
			return
		trade = seller[1]

		#print "seller: ", seller
		# Buyer can meet price of minimum seller
		#print -1.*buyer[2], " ", 1./seller[2]
		if 	(-1.*buyer[2] >= 1./seller[2]): 
			if (is_buy1==False):
				# We're buying q2
				trade_q2 = min(buyer[1], seller[1])
				try:
					#print buyer[0]._sellq1, " ", seller[0]._buyq1
					trade_q1 = min(buyer[0]._sellq1, seller[0]._buyq1)
				except:
					#print "\n",buyer[0]._sellq1, " ", seller[0]._sellq1
					trade_q1 = buyer[0]._sellq1
			else:
				# We're buying q1
				trade_q1 = min(buyer[1], seller[1])
				try:
					#print buyer[0]._sellq2, " ", seller[0]._buyq2
					trade_q2 = min(buyer[0]._sellq2, seller[0]._buyq2)
				except: 
					#print "\n",buyer[0]._sellq2, " ", seller[0]._sellq2
					trade_q2 = buyer[0]._sellq2
			if (min(trade_q1, trade_q2) == trade_q1):
				q1_amt = trade_q1
				# barter price * trade amount
				#note: seller price should be min price
				q2_amt = seller[2] * trade_q1 
				if (buyer[0]._sellq2 < q2_amt):
					#Kill buyer
					buyer[0].kill(n)

				else:
					# Make the trade
					if (is_buy1==False):
						# We switch buyer/seller if it's a q2 buy
						self.make_trade(seller[0], buyer[0], \
														q1_amt, 		q2_amt)
						trade -= q2_amt
						print seller[0], " ", buyer[0], " ", q1_amt, " ", q2_amt
					else:
						self.make_trade(buyer[0], seller[0],\
														q1_amt, 		q2_amt)
						trade -= q1_amt
						print buyer[0], " ", seller[0], " ", q1_amt, " ", q2_amt
				# If q1 sell quantity is now 0, delete the sell
				if (trade == 0):
						sellers.__delitem__(0)

			else:
				q2_amt = trade_q2
				q1_amt = 1./seller[2] * trade_q1

				if (buyer[1] < q2_amt):
					#Kill buyer
					buyer[0].kill(n)

				else:
					if (is_buy1==False):
						# We switch buyer/seller if it's a q2 buy
						self.make_trade(seller[0], buyer[0], \
															q1_amt, 		q2_amt)
						trade -= q2_amt
						print seller[0], " ", buyer[0], " ", q1_amt, " ", q2_amt
					else:
						# Make the trade
						self.make_trade(buyer[0], seller[0], \
														q1_amt,			q2_amt)
						trade -= q1_amt
						print buyer[0], " ", seller[0], " ", q1_amt, " ", q2_amt

				# If q1 sell quantity is now 0, delete the sell
				if (trade == 0):
					sellers.__delitem__(0)
				else:
					sellers[0] = (seller[0], trade, seller[2])
		else:
			#print "DEAD"
			buyer[0].kill(n)

	def make_trade(self, buyer, seller, q1_amt, q2_amt):	
							#print buyer, " ", seller, " ", q1_amt, " ", q2_amt

							seller.modQ1( seller.getQ1() - q1_amt)
							buyer.modQ1( buyer.getQ1() + q1_amt)

							buyer.modQ2(buyer.getQ2() - q2_amt)
							seller.modQ2(seller.getQ2() + q2_amt)

							
			#self.transact(self._buyQ1, self._sellQ1, True)
			#self.transact(self._buyQ2, self._sellQ2, False)

