from collections import defaultdict

import random

from samplers import RouletteWheelSampler

class Transaction(object):
    def __init__(self, customer=None, trans_time=None, purchased_items=None, inventory_before=None, inventory_after=None):
        self.customer = customer
        self.trans_time = trans_time
        self.purchased_items = purchased_items
        self.inventory_before = inventory_before
        self.inventory_after = inventory_after

    def __repr__(self):
        return "(%s, Time: %s, Purchased: %s\n\tInventory Before: %s\n\tInventory After: %s)" % (self.customer,
            self.trans_time, len(self.purchased_items), self.inventory_before,
                self.inventory_after)


class TransactionPurchasesSimulator(object):
    def __init__(self, customer_state=None, trans_time=None):
        self.customer_state = customer_state
        self.trans_time = trans_time
        self.purchases = 0

    def choose_category(self):
        category_weights = self.customer_state.item_category_weights(self.trans_time)

        if self.purchases != 0:
            category_weights.append(("stop", 0.1))

        weight_sum = 0.0
        for category, weight in category_weights:
            print category, weight
            weight_sum += weight

        category_probabilities = []
        for category, weight in category_weights:
            category_probabilities.append((category, weight / weight_sum))

        sampler = RouletteWheelSampler(category_probabilities)
        
        return sampler.sample()

    def choose_item(self, category):
        return self.customer_state.choose_item(category)

    def update_usage_simulations(self, item):
        self.customer_state.update_inventory(self.trans_time, item)

    def simulate(self):
        trans_items = []

        while True:
            category = self.choose_category()
            
            if category == "stop":
                break
            
            item = self.choose_item(category)
            self.update_usage_simulations(item)
            
            self.purchases += 1

            trans_items.append(item)

        return trans_items

class TransactionTimeSampler(object):
    def __init__(self, customer_state=None):
        self.customer_state = customer_state

    def propose_transaction_time(self):
        return self.customer_state.propose_transaction_time()

    def transaction_time_probability(self, proposed_trans_time, last_trans_time):
        if proposed_trans_time >= last_trans_time:
            return 1.0
        else:
            return 0.0

    def sample(self, last_trans_time):
        while True:
            proposed_time = self.propose_transaction_time()
            prob = self.transaction_time_probability(proposed_time, last_trans_time)
            r = random.random()
            if r < prob:
                return proposed_time


class TransactionSimulator(object):
    def __init__(self, customer_state=None):
        self.customer_state = customer_state
        self.trans_time_sampler = TransactionTimeSampler(customer_state=customer_state)
    
    def simulate(self, end_time):
        last_trans_time = 0.0
        while True:
            trans_time = self.trans_time_sampler.sample(last_trans_time)

            if trans_time > end_time:
                break
            
            purchase_sim = TransactionPurchasesSimulator(customer_state=self.customer_state,
                                trans_time=trans_time)

            remaining_before = self.customer_state.get_inventory_amounts(trans_time)
            purchased_items = purchase_sim.simulate()
            remaining_after = self.customer_state.get_inventory_amounts(trans_time)
            trans = Transaction(customer=self.customer_state.customer,
                        purchased_items=purchased_items,
                        trans_time=trans_time,
                        inventory_before=remaining_before,
                        inventory_after=remaining_after)
            last_trans_time = trans_time
            yield trans

    
@@ -794,33 +794,16 @@
 ate=None
-, trans_time=None
 ):%0A     
@@ -846,101 +846,74 @@
 ate%0A
+%0A
     
-    self.trans_time = trans_time%0A        self.purchases = 0%0A%0A    def choose_category(self
+def choose_category(self, trans_time=None, num_purchases=None
 ):%0A 
@@ -980,21 +980,16 @@
 weights(
-self.
 trans_ti
@@ -1004,21 +1004,20 @@
      if 
-self.
+num_
 purchase
@@ -1155,43 +1155,8 @@
 ts:%0A
-            print category, weight%0A
     
@@ -1586,20 +1586,42 @@
 s(self, 
-item
+trans_time=None, item=None
 ):%0A     
@@ -1660,21 +1660,16 @@
 ventory(
-self.
 trans_ti
@@ -1696,24 +1696,41 @@
 imulate(self
+, trans_time=None
 ):%0A        t
@@ -1744,16 +1744,38 @@
 ems = %5B%5D
+%0A        purchases = 0
 %0A%0A      
@@ -1832,16 +1832,106 @@
 ategory(
+trans_time=trans_time,%0A                                            num_purchases=purchases
 )%0A      
@@ -2053,16 +2053,29 @@
 tegory)%0A
+            %0A
         
@@ -2108,16 +2108,86 @@
 lations(
+trans_time=trans_time,%0A                                          item=
 item)%0A  
@@ -2201,37 +2201,32 @@
    %0A            
-self.
 purchases += 1%0A%0A
@@ -3231,16 +3231,111 @@
 _state)%0A
+        self.purchase_sim = TransactionPurchasesSimulator(customer_state=self.customer_state) %0A
     %0A   
@@ -3566,157 +3566,8 @@
    %0A
-            purchase_sim = TransactionPurchasesSimulator(customer_state=self.customer_state,%0A                                trans_time=trans_time)%0A%0A
     
@@ -3669,32 +3669,37 @@
 rchased_items = 
+self.
 purchase_sim.sim
@@ -3704,16 +3704,37 @@
 imulate(
+trans_time=trans_time
 )%0A      
@@ -4168,12 +4168,13 @@
  trans%0A%0A    
+%0A

Refactor TransactionPurchasesSimulator so that it is maintained throughout the transaction