'''
Created on 12-Jul-2021

@author: bthakur

'''
import inspect
import traceback

import random
from xv.km.util import latex_fraction
from xv.util import toIntOrFloat


from xv.util import UtilClass

class DifferentiationBasicsHelper(UtilClass):
    
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        pass

    def get_random_item_name(self):
        item_name, item_names = random.choice([
                ('apple', 'apples'),
                ('orange', 'oranges'),
                ('peach', 'peaches'),
                ('mango', 'mangoes'),
            ])
        return item_name, item_names
        
    def random_rate_qty_cost(self):
        item_name, item_names = self.get_random_item_name()
        qty = random.randint(2, 10)
        qty2 = random.randint(2, 10)
        while qty2 == qty:
            qty2 = random.randint(2, 10)
            
        cost = qty * random.randint(2, 10)
        
        rate = round(cost/qty, 2)
        if rate == int(rate):
            rate = int(rate)

        cost2 = qty2 * rate
        
        rate_computation_latex = latex_fraction(f"\\$\\;{cost}", f"{qty}\\;{item_name}")
        rate_latex = latex_fraction(f"\\$\\;{rate}", f"{item_name}")
        
        data = {
            'item_name': item_name,
            'item_names': item_names,
            'rate': rate,
            'qty': qty,
            'qty2': qty2,
            'cost': cost,
            'cost2': cost2,
            'rate_computation_latex': rate_computation_latex,
            'rate_latex': rate_latex,
            }  
        return data
        pass

    def get_cost_rate_tuple_sets(self):
        import pandas as pd
        item_name, item_names = self.get_random_item_name()
        cost_field = 'cost'
        quantity_field = 'quantity'

        size = random.randint(4, 10)
        rate_arr = sorted(random.sample(range(1, 3 * size), size), reverse = True)
        cost_arr = []
        qty_arr = []
        q_min = 1
        q_step = 10
        prev_q = 0
        prev_c = 0
        while len(qty_arr) < size:
            q = random.randint(q_min, q_min + q_step)
            if not q in qty_arr:
                idx = len(qty_arr) 
                c = prev_c + (q - prev_q) * rate_arr[idx]
                qty_arr.append(q)
                cost_arr.append(c)
                
                prev_q = q
                prev_c = c
                q_min = q + 1

        data = [qty_arr, cost_arr]
        df = pd.DataFrame(data).T
        df.columns = [quantity_field, cost_field]
        df_html = df.to_html(index = False).replace("\n", "").strip()
        
        qty_min = min(df[quantity_field])
        qty_max = max(df[quantity_field])
        
        qty = random.randint(qty_min + 1, qty_max - 1)
        
        while qty in qty_arr:
            qty = random.randint(qty_min + 1, qty_max - 1)

        for idx, qty_curr in enumerate(qty_arr):
            if qty_curr > qty:
                cost_curr = cost_arr[idx]
                qty_prev = qty_arr[idx - 1]
                cost_prev = cost_arr[idx - 1]
                break

        rate_computation_1_latex = latex_fraction(f'{cost_curr} - {cost_prev}', f'{qty_curr} - {qty_prev}')   
        rate_computation_2_latex = latex_fraction(f'{cost_curr - cost_prev}', f'{qty_curr - qty_prev}')   
        
        rate = round((cost_curr - cost_prev)/(qty_curr - qty_prev), 2)
        cost = cost_prev + rate * (qty - qty_prev)
        rate_latex = latex_fraction(f"\\$\\;{rate}", f"{item_name}")
            
        df[['incr_qty', 'incr_cost']] = df.apply(lambda x: x- x.shift(1))
        df.loc[:, 'incr_qty'] = df.apply(lambda x: x[['incr_qty']].fillna(x[quantity_field]), axis=1)
        df.loc[:, 'incr_cost'] = df.apply(lambda x: x[['incr_cost']].fillna(x[cost_field]), axis=1)
        df['incr_rate'] = df['incr_cost'] / df['incr_qty']
        
        df_html_with_rates = df.to_html(index = False).replace("\n", "").strip()
            
        data = {
            'item_name': item_name,
            'item_names': item_names,
            'df': df,
            'df_html': df_html,
            'df_html_with_rates': df_html_with_rates,
            
            'qty_prev': qty_prev,
            'qty_curr': qty_curr,
            'qty': qty,
            
            'cost_prev': cost_prev,
            'cost_curr': cost_curr,
            'cost': cost,
            
            'rate': rate,
            'rate_latex': rate_latex,
            'rate_computation_1_latex': rate_computation_1_latex,
            'rate_computation_2_latex': rate_computation_2_latex,
            }  
        return data
        pass


    def problem_calculate_rate(self, data = None):
        if data is None:
            data = self.random_rate_qty_cost()

        prob_s = f"""
        Find rate of an {data['item_name']} 
        if {data['qty']} {data['item_names']} cost \${data['cost']}.
        """.replace("\n", "")
        
        soln_s = f"""
        Rate of {data['item_name']} 
        
        = $\\displaystyle {data['rate_computation_latex']}$ 
        
        = $\\displaystyle {data['rate_latex']} $
        """

        answer_s = f"""$\\displaystyle {data['rate_latex']} $"""
        
        data['prob_s'] = prob_s
        data['soln_s'] = soln_s
        data['answer_s'] = answer_s
        return data

    def problem_calculate_cost(self, data = None):
        if data is None:
            data = self.random_rate_qty_cost()

        prob_s = f"""
        Find the cost of {data['qty']} {data['item_names']} if 
        one {data['item_name']} costs \${data['rate']}
        """.replace("\n", "")


        soln_s = f"""
        Cost of {data['qty']} {data['item_name']}
        
        = $\\displaystyle {data['rate_latex']} *  {data['qty']} \\; {data['item_name']}$ 
        
        = \${data['cost']}
        """

        answer_s = f"""\${data['cost']}"""
        
        data['prob_s'] = prob_s
        data['soln_s'] = soln_s
        data['answer_s'] = answer_s
        return data

    def problem_calculate_cost_from_cost(self, data = None):
        if data is None:
            data = self.random_rate_qty_cost()

        prob_s = f"""
        Find the cost of {data['qty2']} {data['item_names']} if 
        {data['qty']} {data['item_names']} cost \${data['cost']}
        """.replace("\n", "")


        soln_s = f"""
        Rate of {data['item_name']} 
        
        = $\\displaystyle {data['rate_computation_latex']}$ 
        
        = $\\displaystyle {data['rate_latex']} $

        Therefore, cost of {data['qty2']} {data['item_names']}
        
        = $\\displaystyle {data['rate_latex']} *  {data['qty2']} \\; {data['item_name']}$ 
        
        = \${data['cost2']}
        """

        answer_s = f"""\${data['cost2']}"""
        
        data['prob_s'] = prob_s
        data['soln_s'] = soln_s
        data['answer_s'] = answer_s
        return data

    def problem_calculate_cost_from_rate_slope(self, 
                                               data = None, 
                                               df_with_rates = False,
                                               rules = False, 
                                               plot_rules = False):
        if data is None:
            data = self.get_cost_rate_tuple_sets()

        
        if df_with_rates:
            soln_item = f"""
            The revised table is 
            {data['df_html_with_rates']} 
            """
        else:
            soln_item = f"""
            The quantity {data['qty']} is between {data['qty_prev']} and {data['qty_curr']}.
            The respective costs are {data['cost_prev']} and {data['cost_curr']}.
    
            The rate of additional a {data['item_name']} 
            between {data['qty_prev']} and {data['qty_curr']}        
            
            ${data['rate_computation_1_latex']}$ = ${data['rate_computation_2_latex']}$ = ${data['rate']}$
    
            """
            pass
        
        rate_rules_latex = self.rate_rules_latex(data['df'])
        if rules:
            soln_item = f"""
            {soln_item.strip()}
            
            The cost function is:
            
            {rate_rules_latex}
            """
        
        soln_s = f'''
        {soln_item.strip()}
        
        Hence, the cost  of {data['qty']} {data['item_names']} 
        
        = cost of {data['qty_prev']} {data['item_names']} + cost of additional {data['qty'] - data['qty_prev']} {data['item_names']}
        
        = {data['cost_prev']} + {data['rate']} * {data['qty'] - data['qty_prev']}
        
        = {data['cost_prev']} + {data['rate'] * (data['qty'] - data['qty_prev'])}
        
        = {data['cost']}
        '''
        
        answer_s = f"""
        {data['cost']}
        """
        
        data['rate_rules_latex'] = rate_rules_latex
        data['soln_s'] = soln_s
        data['answer_s'] = answer_s

        if plot_rules:
            svg_str = self.plot_rate_slope(data['df'],
                                           data['qty'],
                                           data['cost'])
            data['svg_str'] = svg_str
            pass
        
        return data

    def rate_rules_latex(self, df):
        from sympy import Symbol, Function, latex
        Q = Symbol("Q")
        C = Function('C')
        C = C(Q)
        
        costs = df['cost'].to_list()
        incr_costs = df['incr_cost'].to_list()
        qtys = df['quantity'].to_list()
        incr_qtys = df['incr_qty'].to_list()
        incr_rates = df['incr_rate'].to_list()
        
        
        step_rhss = []
        for i in range(len(incr_qtys)):
            if i == 0:
                prev_qty = 0
            else:
                prev_qty = qtys[i-1]
                
            step_expr_cost = f""" 
            {toIntOrFloat(costs[i] - incr_costs[i])} + {toIntOrFloat(incr_rates[i])} * ({latex(Q)} - {prev_qty})
            """
            step_expr_qty = f""" 
            {toIntOrFloat(qtys[i] - incr_qtys[i])} < {latex(Q)} <= {toIntOrFloat(qtys[i])}
             """
            
            step_expr = f"""
            {step_expr_cost} & \\text{{for}}\\: {step_expr_qty} 
            """
            
            step_rhss.append(step_expr)
            pass
        
        step_rhss_str = "\\\\ ".join(step_rhss)
        
        step_rhss_str = rf"""$
        {latex(C)} = \begin{{cases}} 
        
        {step_rhss_str}
        
        \end{{cases}}
        $
        """
        return step_rhss_str
        pass
    
    def plot_rate_slope(self, df, qty, total_cost):
        costs = df['cost'].to_list()
        qtys = df['quantity'].to_list()
        
        import matplotlib.pyplot as plt
        from xv.km.ui import SvgHelper

        figsize = (15, 10)
        plt.ioff()
        plt.figure(figsize = figsize)
        
        ax = plt.gca()
        
        qtys.insert(0, 0)
        costs.insert(0, 0)
        
        ax.scatter(qtys, costs)
        ax.plot(qtys, costs)
        ax.plot(qty, total_cost, marker = 'o', color = 'red', markersize = 20)
        ax.annotate(f"({qty}, {total_cost})", 
                    xy = (qty, total_cost * 1.03),
                    xycoords='data')
        
        svgHelper = SvgHelper()
        svg_str = svgHelper.plt_to_svg()
        
        #plt.figure().clear()
        plt.clf()
        plt.close()

        return svg_str
        pass
                

    def _log_DELETED(self, sOut, to_print = None):
        if to_print is None:
            to_print = self.verbose
            
        if to_print:
            print(sOut)
            
    pass
#class DifferentiationBasicsHelper:
