'''
Created on 20-Jan-2022

@author: bthakur
'''

import random

import inspect
import traceback

from sympy import (latex,
                   nsimplify,
                   Symbol, symbols,
                   Add, Mul,
                   Point2D, Segment2D, Line2D,
                   UnevaluatedExpr as ueval,
                   )
import sympy as sp
import numpy as np

import matplotlib.pyplot as plt
from numpy import sin, cos, pi, linspace

from xv.util.data import CacheUtil
from xv.plotter.core import Plotter2D
from xv.km.ui import SvgHelper  

from xv.km import Geometry2DFactory

from xv.util import UtilClass, list_to_str, toIntOrFloat

from ._triangle_basic_properties import TriangleBasicProperties

# _triangle_helper
# TriangleHelper
        
class TriangleHelper(UtilClass):
    
    def __init__(self, **kwargs):
        super().__init__(**kwargs)

        self.plotter_2d = Plotter2D(**kwargs)
        self.svg_helper = SvgHelper(**kwargs)
        pass

    
    def _stmts_to_sn_strs(self, prob_stmts, soln_stmts, answer_stmts):
        
        prob_stmts = [f"{prob_stmt_idx+ 1}. {prob_stmt.strip()}" 
                      for prob_stmt_idx, prob_stmt in enumerate(prob_stmts)
                      if prob_stmt.strip()]

        soln_stmts = [f"<b>{soln_stmt_idx+ 1}.</b> {soln_stmt.strip()}" 
                      for soln_stmt_idx, soln_stmt in enumerate(soln_stmts)
                      if soln_stmt.strip()]

        answer_stmts = [f"{answer_stmt_idx+ 1}. {answer_stmt.strip()}" 
                      for answer_stmt_idx, answer_stmt in enumerate(answer_stmts)
                      if answer_stmt.strip()]

        prob_s = "\n\n".join(prob_stmts)
        soln_s = "\n\n\n".join(soln_stmts)
        answer_s = "\n".join(answer_stmts)


        return (prob_s, soln_s, answer_s)
        pass

    def _update_stmts(self, result, prob_stmts, soln_stmts, answer_stmts):
        prob_stmts.append(result[0].strip())
        soln_stmts.append(result[1].strip())
        answer_stmts.append(result[2].strip())
        pass
    
    
    def get_triangle_basic_properties(self):
        
        tb_props = TriangleBasicProperties(verbose = self.verbose)
        
        prob_stmts = []
        soln_stmts = []
        answer_stmts = []
        
        prop_result = tb_props.is_a_polygon()
        self._update_stmts(prop_result, prob_stmts, soln_stmts, answer_stmts)

        prop_result = tb_props.has_real_sides()
        self._update_stmts(prop_result, prob_stmts, soln_stmts, answer_stmts)
        
        prop_result = tb_props.has_real_angles()
        self._update_stmts(prop_result, prob_stmts, soln_stmts, answer_stmts)
        
        prop_result = tb_props.classify_by_angle()
        self._update_stmts(prop_result, prob_stmts, soln_stmts, answer_stmts)
        
        prop_result = tb_props.classify_by_sides()
        self._update_stmts(prop_result, prob_stmts, soln_stmts, answer_stmts)
        

        prob_stmt = f"""
        """
        prob_stmts.append(prob_stmt.strip())

                


        soln_stmt = f"""
        """
        soln_stmts.append(soln_stmt.strip())


        answer_stmt = f"""
        """
        answer_stmts.append(answer_stmt.strip())

        prob_s, soln_s, answer_s = self._stmts_to_sn_strs(prob_stmts, soln_stmts, answer_stmts)

        prob_stmts = []
        soln_stmts = []
        answer_stmts = []
        
        result = {
            'prob_s': prob_s.strip(),
            'soln_s': soln_s.strip(),
            'answer_s': answer_s.strip(),
            }
        return result
        pass
    
    
    def get_computation_given_three_sides(self):

        tb_props = TriangleBasicProperties(verbose = self.verbose)
        
        triangle = Geometry2DFactory.create('triangle')
        side_lengths = [toIntOrFloat(float(side.length), 2) for side in triangle.sides]
        
        
        prob_stmts = []
        soln_stmts = []
        answer_stmts = []
        
        prop_result = tb_props.get_type_of_triangle_by_sides(triangle)
        self._update_stmts(prop_result, prob_stmts, soln_stmts, answer_stmts)
        
        prop_result = tb_props.get_perimeter_calc(triangle)
        self._update_stmts(prop_result, prob_stmts, soln_stmts, answer_stmts)
        
        prop_result = tb_props.get_area_calc_by_sides(triangle)
        self._update_stmts(prop_result, prob_stmts, soln_stmts, answer_stmts)

        prop_result = tb_props.get_circumradius_calc_by_sides(triangle, show_sub_cal = False)
        self._update_stmts(prop_result, prob_stmts, soln_stmts, answer_stmts)
        
        prop_result = tb_props.get_inradius_calc_by_sides(triangle, show_sub_cal = False)
        self._update_stmts(prop_result, prob_stmts, soln_stmts, answer_stmts)
        
        prop_result = tb_props.get_radii_excircles_calc_by_sides(triangle, show_sub_cal = False)
        self._update_stmts(prop_result, prob_stmts, soln_stmts, answer_stmts)
        
        prop_result = tb_props.get_altitudes_calc_by_sides(triangle, show_sub_cal = False)
        self._update_stmts(prop_result, prob_stmts, soln_stmts, answer_stmts)
        
        prop_result = tb_props.get_angle_calc_by_sides(triangle)
        self._update_stmts(prop_result, prob_stmts, soln_stmts, answer_stmts)
        
        prop_result = tb_props.get_median_calc_by_sides(triangle)
        self._update_stmts(prop_result, prob_stmts, soln_stmts, answer_stmts)
        
        prop_result = tb_props.get_angle_bisector_calc_by_sides(triangle)
        self._update_stmts(prop_result, prob_stmts, soln_stmts, answer_stmts)
        
        
        
        prob_s, soln_s, answer_s = self._stmts_to_sn_strs(prob_stmts, soln_stmts, answer_stmts)

        prob_s = f"""
        Find the following for a triangle whose sides are {list_to_str(side_lengths)}.
        
        {prob_s.strip()}
        """
        
        result = {
            'prob_s': prob_s.strip(),
            'soln_s': soln_s.strip(),
            'answer_s': answer_s.strip(),
            }
        return result
        pass


    def get_computation_sides_to_coordinates(self):

        tb_props = TriangleBasicProperties(verbose = self.verbose)
        
        side_lengths = tb_props.get_random_sides()
        side_lengths_str = list_to_str(side_lengths, last_sep = ' and ')
        
        R = Symbol("R")
        
        side_symbs = symbols("a, b, c")
        a, b, c = side_symbs
        a_val, b_val, c_val = side_lengths
        
        angle_symbs = symbols("A, B, C")
        A, B, C = angle_symbs
        
        C_val = sp.acos( ( a_val ** 2 + b_val ** 2 - c_val ** 2 ) / (2 * a_val * b_val) )
        C_val_float = toIntOrFloat(float(C_val), 2)
        C_val = nsimplify(C_val_float)
        C_val_degrees = toIntOrFloat(np.degrees(C_val_float), 2)
        
        A_val = sp.acos( ( b_val ** 2 + c_val ** 2 - a_val ** 2 ) / (2 * b_val * c_val) )
        A_val_float = toIntOrFloat(float(A_val), 2)
        A_val = nsimplify(A_val_float)
        A_val_degrees = toIntOrFloat(np.degrees(A_val_float), 2)

         
        R_val_float = toIntOrFloat(float(a_val / (2 * sp.sin(A_val))) , 2)
        R_val = nsimplify(R_val_float)
        
        pA_symb = Point2D(R, 0)
        pB_symb = Point2D(R * sp.cos(2 * C), R * sp.sin(2 * C))
        pC_symb = Point2D(R * sp.cos(2 * C + 2 * A), R * sp.sin(2 * C + 2 * A))
        
        pA_ueval = Point2D(ueval(R_val), 0)
        pB_ueval = Point2D(ueval(R_val) * ueval(sp.cos(2 * ueval(C_val) )), 
                          ueval(R_val) * ueval(sp.sin(2 * ueval(C_val) )))

        pC_ueval = Point2D(ueval(R_val) * ueval(sp.cos(2 * ueval(C_val) + 2 * ueval(A_val) )), 
                          ueval(R_val) * ueval(sp.sin(2 * ueval(C_val) + 2 * ueval(A_val) )))
        
        pA_val = Point2D(R_val, 0)
        pB_val = Point2D(toIntOrFloat(float(R_val * sp.cos(2 * C_val)), 2), 
                         toIntOrFloat(float(R_val * sp.sin(2 * C_val)), 2))
        pC_val = Point2D(toIntOrFloat(float(R_val * sp.cos(2 * C_val + 2 * A_val)), 2), 
                         toIntOrFloat(float(R_val * sp.sin(2 * C_val + 2 * A_val)), 2))
        
        
        
        abc_val_str =  f"""We have $\\displaystyle 
        {latex(a)} = {latex(a_val)} \\\\
        {latex(b)} = {latex(b_val)} \\\\ 
        {latex(c)} = {latex(c_val)} 
        $."""

        line_BC = Segment2D(pB_val, pC_val)
        line_CA = Segment2D(pC_val, pA_val)
        line_AB = Segment2D(pA_val, pB_val)

        plotter_2d = self.plotter_2d
        svg_helper = self.svg_helper
        
        #create sympy shapes
        circumcenter = Point2D(0, 0)
        circumcircle = sp.Circle(circumcenter, R_val)
        
        triangle = Geometry2DFactory.create('triangle', side_lengths = side_lengths)
        
        sOut = f"""
        triangle.vertices = {triangle.vertices}
        triangle.get_sides_dict().keys() = {list(triangle.get_sides_dict().keys())}
        
        triangle.sides = {triangle.sides}
        
        triangle.get_sides_dict() = {triangle.get_sides_dict()}
        
        A = {pA_val}
        B = {pB_val}
        C = {pC_val}
        """
        #print(sOut)
        
        
        #initial plot without all values
        #draw triangle 
        origin = Point2D(0, 0)
        initial_triangle = Geometry2DFactory.create('triangle', origin = origin)
        fig, ax = plotter_2d.plot(initial_triangle, color = 'red')
        
        
        #lables
        temp_labels = ["A(R, 0)",
                       "B(R cos(2C), R sin(2C))",
                       "C(R cos(2C+2A), R sin(2C+2A))"
                       ]
        plotter_2d.label(initial_triangle, ax =  ax, labels = temp_labels)
        
        #draw circumcircle
        plotter_2d.plot(initial_triangle.circumcircle, ax = ax, color = 'grey')
        
        #draw circumcenter
        
        plotter_2d.plot(origin, ax = ax, color = 'grey')
        plotter_2d.label(origin, ax =  ax, label = "O", show_values = True)
        
        plotter_2d.plot('circumradii', initial_triangle, ax =  ax, color = 'lightgreen')
        plotter_2d.plot('circumangles', initial_triangle, ax =  ax, color = 'red', 
                        labels = ["2A", "2B", "2C"])
        
        svg_str_initial = svg_helper.plt_to_svg(close_plot = True)
        
        
        #final plot with all values
        #draw triangle 
        fig, ax = plotter_2d.plot(triangle, color = 'red')
        plotter_2d.label(triangle, ax =  ax, show_values = True, color = 'black')
        
        #draw circumcircle
        plotter_2d.plot(circumcircle, ax = ax, color = 'grey')
        
        #draw circumcenter
        plotter_2d.plot(circumcenter, ax = ax, color = 'grey')
        plotter_2d.label(circumcenter, ax =  ax, label = "O", show_values = True)
        
        svg_str_computed = svg_helper.plt_to_svg(close_plot = True)
        
        
        prob_stmts = []
        soln_stmts = []
        answer_stmts = []
        
        prob_stmt = f"""
        Convert side lengths {side_lengths_str} 
        of a triangle into coordinates of its vertices.
        """
        prob_stmts.append(prob_stmt.replace("\n", " ").strip())


        soln_stmt = f"""
        {abc_val_str}
        
        Let ${latex(R)}$ be circumradius of the triangle.
        """
        soln_stmts.append(soln_stmt.strip())

        soln_stmt = f"""
        $\\displaystyle 
        {latex(C)} 
        =  cos^{{-1}} \\left(
            \\frac{{ 
                {latex(a)}^2 + {latex(b)}^2 
                - {latex(c)}^2 }} {{ 2 {latex(a)} {latex(b)} 
                }}
        \\right)
        
        =  cos^{{-1}} \\left(
            \\frac{{ 
                {latex(a_val)}^2 + {latex(b_val)}^2 
                - {latex(c_val)}^2 }} {{ 2 \\cdot {latex(a_val)} \\cdot {latex(b_val)} 
                }}
        \\right)
        
        = {latex(C_val_float)}
        = {latex(C_val)}
        = {latex(C_val_degrees)}^{{ \\circ }} 
        $
        """
        soln_stmts.append(soln_stmt.strip())


        soln_stmt = f"""
        $\\displaystyle 
        {latex(A)} 
        =  cos^{{-1}} \\left(
            \\frac{{ 
                {latex(b)}^2 + {latex(c)}^2 
                - {latex(a)}^2 }} {{ 2 {latex(b)} {latex(c)} 
                }}
        \\right)
        
        =  cos^{{-1}} \\left(
            \\frac{{ 
                {latex(b_val)}^2 + {latex(c_val)}^2 
                - {latex(a_val)}^2 }} {{ 2 \\cdot {latex(b_val)} \\cdot {latex(c_val)} 
                }}
        \\right)
        
        = {latex(A_val_float)}
        = {latex(A_val)}
        = {latex(A_val_degrees)}^{{ \\circ }} 
        $
        """
        soln_stmts.append(soln_stmt.strip())

        soln_stmt = f"""
        $\\displaystyle 
        {latex(a)} = 2 {latex(R)} {latex(sp.sin(A))}
        $ 
        $\\displaystyle \\implies
        {latex(R)} 
        = \\frac {{ {latex(a)} }} {{ 2 {latex(sp.sin(A))} }}
        = \\frac {{ {latex(a_val)} }} {{ 2 \\cdot {latex(toIntOrFloat(sp.sin(A_val), 2))} }}
        = {latex(R_val_float)} 
        = {latex(R_val)} 
        $ 
        """
        soln_stmts.append(soln_stmt.strip())



        soln_stmt = f"""
        The vertices are as follows:
        
        {svg_str_initial}
        
        $\\displaystyle 
        {latex(A)} 
        = \\left({latex(pA_symb.x)}, {latex(pA_symb.y)} \\right)
        = \\left({latex(pA_val.x)}, {latex(pA_val.y)} \\right)
        $ 
        """
        soln_stmts.append(soln_stmt.strip())


        soln_stmt = f"""
        $\\displaystyle
        \\begin{{align}}
        
        {latex(B)} 
        & = \\left({latex(pB_symb.x)}, {latex(pB_symb.y)} \\right)
        \\\\ \\\\
        
        & = \\left({latex(pB_ueval.x)}, {latex(pB_ueval.y)} \\right)
        \\\\ \\\\
        
        & = \\left({latex(pB_val.x)}, {latex(pB_val.y)} \\right)
        
        \\end{{align}} 
        $
        """
        soln_stmts.append(soln_stmt.strip())


        soln_stmt = f"""
        $\\displaystyle
        \\begin{{align}}
        
        {latex(C)} 
        & = \\left({latex(pC_symb.x)}, {latex(pC_symb.y)} \\right)
        \\\\ \\\\
        
        & = \\left({latex(pC_ueval.x)}, {latex(pC_ueval.y)} \\right)
        \\\\ \\\\
        
        & = \\left({latex(pC_val.x)}, {latex(pC_val.y)} \\right)
        
        \\end{{align}} 
        $
        """
        soln_stmts.append(soln_stmt.strip())


        soln_stmt = f"""
        Let us check:
        
        $\\displaystyle
        \\begin{{align}}
        
        {latex(a)}  
        & = \\text{{ length of }} {latex(B)}{latex(C)}
        \\\\ \\\\
        
        & = \\text{{ length of }} {latex(line_BC)}
        \\\\ \\\\
        
        & = \\sqrt{{
            \\left( {latex(pC_val.x)} - {latex(pB_val.x)} \\right) ^2 
          + \\left( {latex(pC_val.y)} - {latex(pB_val.y)} \\right) ^2 
        }}
        \\\\ \\\\
        
        & = {latex(toIntOrFloat(line_BC.length, 2))}
        \\qquad \\text {{ we had }} {latex(a)} = {latex(a_val)} 
        
        
        \\end{{align}} 
        $
        """
        soln_stmts.append(soln_stmt.strip())


        soln_stmt = f"""
        $\\displaystyle
        \\begin{{align}}
        
        {latex(b)}  
        & = \\text{{ length of }} {latex(C)}{latex(A)}
        \\\\ \\\\
        
        & = \\text{{ length of }} {latex(line_CA)}
        \\\\ \\\\
        
        & = \\sqrt{{
            \\left( {latex(pA_val.x)} - {latex(pC_val.x)} \\right) ^2 
          + \\left( {latex(pA_val.y)} - {latex(pC_val.y)} \\right) ^2 
        }}
        \\\\ \\\\
        
        & = {latex(toIntOrFloat(line_CA.length, 2))}
        \\qquad \\text {{ we had }} {latex(b)} = {latex(b_val)} 
        
        
        \\end{{align}} 
        $
        """
        soln_stmts.append(soln_stmt.strip())


        soln_stmt = f"""
        $\\displaystyle
        \\begin{{align}}
        
        {latex(c)}  
        & = \\text{{ length of }} {latex(A)}{latex(B)}
        \\\\ \\\\
        
        & = \\text{{ length of }} {latex(line_AB)}
        \\\\ \\\\
        
        & = \\sqrt{{
            \\left( {latex(pB_val.x)} - {latex(pA_val.x)} \\right) ^2 
          + \\left( {latex(pB_val.y)} - {latex(pA_val.y)} \\right) ^2 
        }}
        \\\\ \\\\
        
        & = {latex(toIntOrFloat(line_AB.length, 2))}
        \\qquad \\text {{ we had }} {latex(c)} = {latex(c_val)}
        
        \\end{{align}} 
        $
        {svg_str_computed}
        """
        soln_stmts.append(soln_stmt.strip())



        answer_stmt = f"""
        {svg_str_computed}
        """
        answer_stmts.append(answer_stmt.strip())

        
        prob_s = "\n\n".join(prob_stmts)
        soln_s = "\n\n\n".join(soln_stmts)
        answer_s = "\n".join(answer_stmts)

        
        result = {
            'prob_s': prob_s.strip(),
            'soln_s': soln_s.strip(),
            'answer_s': answer_s.strip(),
            }
        return result
        pass

    pass
#class TriangleHelper:
