'''
Created on 20-Jan-2022

@author: bthakur
'''

import inspect
import traceback
import random
import numpy as np
from sympy import (latex, Symbol, symbols,
                    Rational,
                    sin, cos, tan,
                    asin, acos, atan,
                    sinh, cosh, tanh,
                    log, exp, sqrt,
                   diff, factor, 
                   Function, Eq,
                   Add, 
                   simplify, solve, factorial,
                   Sum, binomial, pi
                   )


from xv.km.util import latex_fraction
from sympy.core.expr import UnevaluatedExpr

from xv.km.ui import SvgHelper
from xv.util.data import CacheUtil

import matplotlib.pyplot as plt
from xv.plotter.core import Plotter2D, Plotter3D

from xv.util import UtilClass, toIntOrFloat

# _area_helper
# AreaHelper

class AreaHelper(UtilClass):
    _MAIN_OPACITY = 0.6
    _SUB_OPACITY = 0.1
    
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        pass

    
    def get_reactangle_area_rep(self, b, h):
        area_rep = []
        area_rep.append("""<table style = "border: 2px solid black;" >""")
        iCount = 0
        for i in range(h):
            area_rep.append("<tr>")
            for j in range(b):
                iCount = iCount + 1
                area_rep.append("<td>")
                area_rep.append(f"{iCount}")
                area_rep.append("</td>")
                pass
            area_rep.append("</tr>")
            pass
        area_rep.append("</table>")
        
        area_rep = "\n\n".join(area_rep).replace("\n", "").strip()
        
        return area_rep
        pass
    


    def plot_parallelogram(self, b, h, theta, left_bottom = (0, 0),
                           figsize = (10,6),
                           title = None,
                           use_cache = True
                           ):
        
        verbose = self.verbose
        self.cacheUtil = CacheUtil(use_cache = use_cache, verbose = verbose)
        
        cache_file_path, exists_cached_file = self.cacheUtil.get_cached_path(
                self.__class__, inspect.stack()[0][3],
                b,
                h,
                theta,
                left_bottom,
                figsize,
                title,
                file_prefix = f"{self.__class__.__name__}_{inspect.stack()[0][3]}_"
                )
        
        if use_cache and exists_cached_file:
            return self.cacheUtil.load_file(cache_file_path)

        plt.ioff()
        
        plotter = Plotter2D()
        n3 = toIntOrFloat(h / np.sin(float(theta)), roundoff =  2)
        if title is None:      
            title = f"Parallelogram (base: {b}, height: {h}, angle: ${latex(theta)}$)"
        
        fig, ax = plotter.plot_rectangle(b, h, 
                                             left_bottom = left_bottom, 
                                             aspect = 'equal',
                                             color =  'green',
                                             opacity = AreaHelper._SUB_OPACITY
                                            )
        plotter.plot_parallelogram(b, n3, float(theta), 
                                 left_bottom = left_bottom, 
                                 aspect = 'equal',
                                 color =  'red',
                                 opacity = AreaHelper._MAIN_OPACITY,
                                 fill = True,
                                 ax = ax)
        
        ax.set_title(title, color =  (1, 0, 0, 1))
            
        svgHelper = SvgHelper()
        svg_str = svgHelper.plt_to_svg()
        
        if not svg_str:
            if self.verbose:
                print(f"Error: svg_str could not be initialized")
            return None
            
        self.cacheUtil.save_file(svg_str, cache_file_path)
        plt.clf()
        plt.close()

        return svg_str
        pass
    
    def plot_triangle(self, b, h, theta, left_bottom = (0, 0),
                           figsize = (10,6),
                           title = None,
                           use_cache = True
                           ):

        self.cacheUtil = CacheUtil(use_cache = use_cache, verbose = self.verbose)
        
        cache_file_path, exists_cached_file = self.cacheUtil.get_cached_path(
                self.__class__, inspect.stack()[0][3],
                b,
                h,
                theta,
                left_bottom,
                figsize,
                title,
                file_prefix = f"{self.__class__.__name__}_{inspect.stack()[0][3]}_"
                )
        
        if use_cache and exists_cached_file:
            return self.cacheUtil.load_file(cache_file_path)

        plt.ioff()
        
        plotter = Plotter2D()
        n3 = toIntOrFloat(h / np.sin(float(theta)), roundoff =  2)
        if title is None:      
            title = f"Triangle (base: {b}, height: {h}, angle: ${latex(theta)}$)"
        
        fig, ax = plotter.plot_parallelogram(b, n3, float(theta), 
                                 left_bottom = left_bottom, 
                                 aspect = 'equal',
                                 color =  'green',
                                 opacity = AreaHelper._SUB_OPACITY
                                 )
        
        p1 = (0, 0)
        p2 = (b, 0)
        p3 = (n3 * cos(float(theta)), h)
        
        plotter.plot_triangle(p1, p2, p3, 
                             aspect = 'equal',
                             color =  'red',
                             opacity = AreaHelper._MAIN_OPACITY,
                             fill = True,
                             ax = ax)
        
        ax.set_title(title, color =  (1, 0, 0, 1))
            
        svgHelper = SvgHelper()
        svg_str = svgHelper.plt_to_svg()
    
        if not svg_str:
            if self.verbose:
                print(f"Error: svg_str could not be initialized")
            return None
            
        self.cacheUtil.save_file(svg_str, cache_file_path)
        
        plt.clf()
        plt.close()
        
        return svg_str
        pass

    def plot_rhombus(self, d1, d2, left_bottom = (0, 0),
                           figsize = (10,6),
                           title = None,
                           use_cache = True
                           ):

        self.cacheUtil = CacheUtil(use_cache = use_cache, verbose = self.verbose)
        
        cache_file_path, exists_cached_file = self.cacheUtil.get_cached_path(
                self.__class__, inspect.stack()[0][3],
                d1,
                d2,
                left_bottom,
                figsize,
                title,
                file_prefix = f"{self.__class__.__name__}_{inspect.stack()[0][3]}_"
                )
        
        if use_cache and exists_cached_file:
            return self.cacheUtil.load_file(cache_file_path)

        side_length = ((d1/2) ** 2 + (d2/2) ** 2) * 0.5
        
        theta = 2 * np.arctan2(d2, d1)
        
        if title is None:      
            title = f"Rhombus (diagonals: {d1}, {d2})"

        
        plt.ioff()
        plotter = Plotter2D()
        fig, ax = plotter.plot_rhombus(side_length, float(theta), 
                                 left_bottom = left_bottom, 
                                 draw_diagonals = True,
                                 aspect = 'equal',
                                 color =  'red',
                                 opacity = AreaHelper._MAIN_OPACITY,
                                 diagonals_color = 'green',
                                 diagonals_opacity = AreaHelper._SUB_OPACITY,
                                 ) 
        
        ax.set_title(title, color =  (1, 0, 0, 1))
            
        svgHelper = SvgHelper()
        svg_str = svgHelper.plt_to_svg()
    
        if not svg_str:
            if self.verbose:
                print(f"Error: svg_str could not be initialized")
            return None
            
        self.cacheUtil.save_file(svg_str, cache_file_path)
        
        plt.clf()
        plt.close()
        
        return svg_str
        pass

    def plot_trapezoid(self, b1, b2, h, theta, 
                           figsize = (10,6),
                           title = None,
                           use_cache = True
                           ):

        self.cacheUtil = CacheUtil(use_cache = use_cache, verbose = self.verbose)
        
        cache_file_path, exists_cached_file = self.cacheUtil.get_cached_path(
                self.__class__, inspect.stack()[0][3],
                b1,
                b2,
                h,
                theta,
                figsize,
                title,
                file_prefix = f"{self.__class__.__name__}_{inspect.stack()[0][3]}_"
                )
        
        if use_cache and exists_cached_file:
            return self.cacheUtil.load_file(cache_file_path)

        
        if title is None:      
            title = f"Trapezoid (bases: {b1}, {b2}, height: {h}, angle: ${latex(theta)}$)"
        
        plt.ioff()
        plotter = Plotter2D()
        n3 = toIntOrFloat(h / np.sin(float(theta)), roundoff =  2)
        
        p1 = (0, 0)
        p2 = (b1, 0)
        p4 = (n3 * cos(float(theta)), h)
        p3 = (p4[0] + b2, h)
        
        left_bottom = (0, 0)
        fig, ax = plotter.plot_parallelogram(b2, n3, float(theta), 
                                 left_bottom = left_bottom, 
                                 aspect = 'equal',
                                 color =  'blue',
                                 opacity = AreaHelper._SUB_OPACITY,
                                 )
        
        plotter.plot_parallelogram(b1, n3, float(theta), 
                                 left_bottom = left_bottom, 
                                 aspect = 'equal',
                                 color =  'green',
                                 opacity = AreaHelper._SUB_OPACITY,
                                 ax = ax)
        
        plotter.plot_quadrilateral(p1, p2, p3, p4,
                             aspect = 'equal',
                             color =  'red',
                             opacity = AreaHelper._MAIN_OPACITY,
                             ax = ax
                             )
        
        ax.set_title(title, color =  (1, 0, 0, 1))
            
        svgHelper = SvgHelper()
        svg_str = svgHelper.plt_to_svg()
    
        if not svg_str:
            if self.verbose:
                print(f"Error: svg_str could not be initialized")
            return None
            
        self.cacheUtil.save_file(svg_str, cache_file_path)
        
        plt.clf()
        plt.close()
        
        return svg_str
        pass

    def plot_circle(self, radius, center, start_angle = 0, end_angle = 2*np.pi, 
                           figsize = (10,6),
                           title = None,
                           use_cache = True
                           ):

        self.cacheUtil = CacheUtil(use_cache = use_cache, verbose = self.verbose)
        
        cache_file_path, exists_cached_file = self.cacheUtil.get_cached_path(
                self.__class__, inspect.stack()[0][3],
                radius,
                center,
                start_angle,
                end_angle,
                figsize,
                title,
                file_prefix = f"{self.__class__.__name__}_{inspect.stack()[0][3]}_"
                )
        
        if use_cache and exists_cached_file:
            return self.cacheUtil.load_file(cache_file_path)

        
        if title is None:      
            title = f"Circle (radius: {radius}, center: {center}, angle: (${latex(end_angle)}$))"
        
        plt.ioff()
        plotter = Plotter2D()
        
        fig, ax = plotter.plot_circle(radius, center, 
                                      start_angle = start_angle, end_angle = float(2*np.pi), 
                                     aspect = 'equal',
                                     color =  'blue',
                                     opacity = AreaHelper._SUB_OPACITY,
                                     )
        #end_angle = float(end_angle)
        
        plotter.plot_circle(radius, center, 
                            start_angle = start_angle, 
                            end_angle = float(end_angle), 
                             aspect = 'equal',
                             color =  'red',
                             opacity = AreaHelper._MAIN_OPACITY,
                             draw_radii = True,
                             radii_color = 'blue',
                             radii_opacity = AreaHelper._SUB_OPACITY,
                             fill = True,
                             ax = ax
                             )
        
        ax.set_title(title, color =  (1, 0, 0, 1))
            
        svgHelper = SvgHelper()
        svg_str = svgHelper.plt_to_svg()
    
        if not svg_str:
            if self.verbose:
                print(f"Error: svg_str could not be initialized")
            return None
            
        self.cacheUtil.save_file(svg_str, cache_file_path)
        
        plt.clf()
        plt.close()
        
        return svg_str
        pass

    def plot_ellipse(self, a, b, center, start_angle = 0, end_angle = 2*np.pi, 
                           figsize = (10,6),
                           title = None,
                           use_cache = True
                           ):

        self.cacheUtil = CacheUtil(use_cache = use_cache, verbose = self.verbose)
        
        cache_file_path, exists_cached_file = self.cacheUtil.get_cached_path(
                self.__class__, inspect.stack()[0][3],
                a, b,
                center,
                start_angle,
                end_angle,
                figsize,
                title,
                file_prefix = f"{self.__class__.__name__}_{inspect.stack()[0][3]}_"
                )
        
        if use_cache and exists_cached_file:
            return self.cacheUtil.load_file(cache_file_path)

        
        if title is None:      
            title = f"Ellipse (radii: {a}, {b}, center: {center}, angle: (${latex(end_angle)}$))"
        
        plt.ioff()
        plotter = Plotter2D(verbose = self.verbose)
        
        fig, ax = plotter.plot_ellipse(a, b, center, 
                                      start_angle = start_angle, end_angle = float(2*np.pi), 
                                     aspect = 'equal',
                                     color =  'blue',
                                     opacity = AreaHelper._SUB_OPACITY,
                                     )
        #end_angle = float(end_angle)
        
        plotter.plot_ellipse(a, b, center, 
                            start_angle = start_angle, 
                            end_angle = float(np.pi / 2), 
                             aspect = 'equal',
                             color =  'blue',
                             opacity = AreaHelper._SUB_OPACITY,
                             draw_radii = True,
                             radii_color = 'blue',
                             radii_opacity = AreaHelper._SUB_OPACITY,
                             ax = ax
                             )
        
        plotter.plot_ellipse(a, b, center, 
                            start_angle = start_angle, 
                            end_angle = float(end_angle), 
                             aspect = 'equal',
                             color =  'red',
                             fill = True,
                             ax = ax
                             )
        
        ax.set_title(title, color =  (1, 0, 0, 1))
            
        svgHelper = SvgHelper()
        svg_str = svgHelper.plt_to_svg()
    
        if not svg_str:
            if self.verbose:
                print(f"Error: svg_str could not be initialized")
            return None
            
        self.cacheUtil.save_file(svg_str, cache_file_path)
        
        plt.clf()
        plt.close()
        
        return svg_str
        pass

    def plot_sphere(self, radius, center,
                    start_theta = 0, end_theta = 2*pi,
                    start_alpha = 0, end_alpha = pi,
                    figsize = (10,6),
                    title = None,
                    use_cache = True
                    ):

        self.cacheUtil = CacheUtil(use_cache = use_cache, verbose = self.verbose)
        
        cache_file_path, exists_cached_file = self.cacheUtil.get_cached_path(
                self.__class__, inspect.stack()[0][3],
                radius,
                center,
                start_theta, end_theta,
                start_alpha, end_alpha,
                figsize,
                title,
                file_prefix = f"{self.__class__.__name__}_{inspect.stack()[0][3]}_"
                )
        
        if use_cache and exists_cached_file:
            return self.cacheUtil.load_file(cache_file_path)

        
        if title is None:      
            title = f"Sphere (radius: {radius}, center: {center}, theta: ${latex(end_theta)}$, alpha: ${latex(end_alpha)}$)"
        
        plt.ioff()
        plotter = Plotter3D()
        
        fig, ax = plotter.plot_sphere(radius, center, 
                                        start_theta = 0, 
                                        end_theta = float(2*pi),
                                        start_alpha = 0, 
                                        end_alpha = float(pi),
                                        color =  'blue',
                                        opacity = AreaHelper._SUB_OPACITY,
                                        )

        plotter.plot_sphere(radius, center, 
                            start_theta = float(start_theta), 
                            end_theta = float(end_theta),
                            start_alpha = 0, 
                            end_alpha = float(end_alpha),
                            color =  'red',
                            opacity = AreaHelper._SUB_OPACITY,
                            draw_radii_props = {
                                'draw_radii': True,
                                'radii_color': 'blue',
                                'radii_opacity': AreaHelper._MAIN_OPACITY
                                },
                            ax = ax
                            )
        
        ax.set_title(title, color =  (1, 0, 0, 1))
            
        svgHelper = SvgHelper()
        svg_str = svgHelper.plt_to_svg()
    
        if not svg_str:
            if self.verbose:
                print(f"Error: svg_str could not be initialized")
            return None
            
        self.cacheUtil.save_file(svg_str, cache_file_path)
        
        plt.clf()
        plt.close()
        
        return svg_str
        pass

    def plot_cylinder(self, radius, length, center,
                    start_theta = 0, end_theta = 2*pi,
                    figsize = (10,6),
                    title = None,
                    use_cache = True
                    ):

        self.cacheUtil = CacheUtil(use_cache = use_cache, verbose = self.verbose)
        
        cache_file_path, exists_cached_file = self.cacheUtil.get_cached_path(
                self.__class__, inspect.stack()[0][3],
                radius, length, center,
                start_theta, end_theta,
                figsize,
                title,
                file_prefix = f"{self.__class__.__name__}_{inspect.stack()[0][3]}_"
                )
        
        if use_cache and exists_cached_file:
            return self.cacheUtil.load_file(cache_file_path)

        
        if title is None:      
            title = f"Cylinder (radius: {radius}, length: {length},  center: {center}, theta: ${latex(end_theta)}$)"
        
        plt.ioff()
        plotter = Plotter3D()
        
        fig, ax = plotter.plot_cylinder(radius, length, center, 
                                        start_theta = 0, 
                                        end_theta = float(2*pi),
                                        surface_color = 'blue', 
                                        disk_color = 'blue',
                                        opacity = AreaHelper._SUB_OPACITY,
                                        )

        plotter.plot_cylinder(radius, length, center, 
                            start_theta = float(start_theta), 
                            end_theta = float(end_theta),
                            surface_color = 'red', 
                            disk_color = 'red',
                            opacity = AreaHelper._SUB_OPACITY,
                            draw_radii_props = {
                                'draw_radii': True,
                                'radii_color': 'blue',
                                'radii_opacity': AreaHelper._MAIN_OPACITY
                                },
                            ax = ax
                            )
        
        ax.set_title(title, color =  (1, 0, 0, 1))
            
        svgHelper = SvgHelper()
        svg_str = svgHelper.plt_to_svg()
    
        if not svg_str:
            if self.verbose:
                print(f"Error: svg_str could not be initialized")
            return None
            
        self.cacheUtil.save_file(svg_str, cache_file_path)
        
        plt.clf()
        plt.close()
        
        return svg_str
        pass

    def plot_cone(self, radius, length, center,
                    start_theta = 0, end_theta = 2*pi,
                    figsize = (10,6),
                    title = None,
                    use_cache = True
                    ):

        self.cacheUtil = CacheUtil(use_cache = use_cache, verbose = self.verbose)
        
        cache_file_path, exists_cached_file = self.cacheUtil.get_cached_path(
                self.__class__, inspect.stack()[0][3],
                radius, length, center,
                start_theta, end_theta,
                figsize,
                title,
                file_prefix = f"{self.__class__.__name__}_{inspect.stack()[0][3]}_"
                )
        
        if use_cache and exists_cached_file:
            return self.cacheUtil.load_file(cache_file_path)

        
        if title is None:      
            title = f"Cone (radius: {radius}, length: {length},  center: {center}, theta: ${latex(end_theta)}$)"
        plt.ioff()
        plotter = Plotter3D()
        
        fig, ax = plotter.plot_cone(radius, length, center, 
                                        start_theta = 0, 
                                        end_theta = float(2*pi),
                                        surface_color = 'blue', 
                                        disk_color = 'blue',
                                        opacity = AreaHelper._SUB_OPACITY,
                                        )

        plotter.plot_cone(radius, length, center, 
                            start_theta = float(start_theta), 
                            end_theta = float(end_theta),
                            surface_color = 'red', 
                            disk_color = 'red',
                            opacity = AreaHelper._SUB_OPACITY,
                            draw_radii_props = {
                                'draw_radii': True,
                                'radii_color': 'blue',
                                'radii_opacity': AreaHelper._MAIN_OPACITY
                                },
                            ax = ax
                            )
        
        ax.set_title(title, color =  (1, 0, 0, 1))
            
        svgHelper = SvgHelper()
        svg_str = svgHelper.plt_to_svg()
    
        if not svg_str:
            if self.verbose:
                print(f"Error: svg_str could not be initialized")
            return None
            
        self.cacheUtil.save_file(svg_str, cache_file_path)
        
        plt.clf()
        plt.close()
        
        return svg_str
        pass

    def plot_prism(self, tri_length, length, center,
                    start_theta = 0, end_theta = 2*pi,
                    figsize = (10,6),
                    title = None,
                    use_cache = True
                    ):

        self.cacheUtil = CacheUtil(use_cache = use_cache, verbose = self.verbose)
        
        cache_file_path, exists_cached_file = self.cacheUtil.get_cached_path(
                self.__class__, inspect.stack()[0][3],
                tri_length, length, center,
                figsize,
                title,
                file_prefix = f"{self.__class__.__name__}_{inspect.stack()[0][3]}_"
                )
        
        if use_cache and exists_cached_file:
            return self.cacheUtil.load_file(cache_file_path)

        
        if title is None:      
            title = f"Prism (Triangle length: {tri_length}, length: {length}"
        plt.ioff()
        plotter = Plotter3D()
        
        fig, ax = plotter.plot_prism(tri_length, length, center, 
                                    surface_color = 'blue', 
                                    opacity = AreaHelper._SUB_OPACITY,
                                    )

        
        ax.set_title(title, color =  (1, 0, 0, 1))
            
        svgHelper = SvgHelper()
        svg_str = svgHelper.plt_to_svg()
    
        if not svg_str:
            if self.verbose:
                print(f"Error: svg_str could not be initialized")
            return None
            
        self.cacheUtil.save_file(svg_str, cache_file_path)
        
        plt.clf()
        plt.close()
        
        return svg_str
        pass
    
    def plot_cubiod(self, length, width, height, center,
                    figsize = (10,6),
                    title = None,
                    use_cache = True
                    ):

        self.cacheUtil = CacheUtil(use_cache = use_cache, verbose = self.verbose)
        
        cache_file_path, exists_cached_file = self.cacheUtil.get_cached_path(
                self.__class__, inspect.stack()[0][3],
                length, width, height, center,
                figsize,
                title,
                file_prefix = f"{self.__class__.__name__}_{inspect.stack()[0][3]}_"
                )
        
        if use_cache and exists_cached_file:
            return self.cacheUtil.load_file(cache_file_path)

        
        if title is None:      
            title = f"(length: {length}, width: {width}, height: {height},  center: {center})"
        
        plt.ioff()
        plotter = Plotter3D()
        
        fig, ax = plotter.plot_cuboid(length, width, height, center,
                                      color = 'blue', 
                                      opacity = AreaHelper._SUB_OPACITY,
                                      )

        
        ax.set_title(title, color =  (1, 0, 0, 1))
            
        svgHelper = SvgHelper()
        svg_str = svgHelper.plt_to_svg()
    
        if not svg_str:
            if self.verbose:
                print(f"Error: svg_str could not be initialized")
            return None
            
        self.cacheUtil.save_file(svg_str, cache_file_path)
        
        plt.clf()
        plt.close()
        
        return svg_str
        pass

    
    pass
#class AreaHelper:
