'''
Created on Mar-07-2024

@author: bthakur

pip install roman

'''

import random

import inspect
import traceback

from sympy import (latex,
                   Matrix,
                   nsimplify, Rational,
                   Symbol, symbols,
                   Add, Mul, Eq, 
                   UnevaluatedExpr as ueval,
                   cos, sin,
                   )
import sympy as sp
import numpy as np

from xv.util import (UtilClass
                     )

from sympy.vector import (CoordSys3D, BaseVector, BaseScalar,
                          VectorAdd, VectorMul,
                          )

# _geometry_3d_base_helper
# Geometry3dBaseHelper
        
class Geometry3dBaseHelper(UtilClass):
    
    def __init__(self, **kwargs):
        super().__init__(**kwargs)

        self.N = CoordSys3D('N')
        pass
    
    def get_random_vector(self, N = None, symb = False, sub = 0):
        '''
        @N: coord sys. Default is self.N
        @symb: bool. If True, symbolic values are returned 
        @sub: if symb is true, it is used as subscript of symbolic values
        '''
        
        if N is None:
            N = self.N
        
        if symb:
            x = Symbol(f"x_{{{sub}}}")
            y = Symbol(f"y_{{{sub}}}")
            z = Symbol(f"z_{{{sub}}}")
            pass
        else:
            x = random.randint(0, 9) * random.choice([1, -1])
            y = random.randint(0, 9) * random.choice([1, -1])
            z = random.randint(0, 9) * random.choice([1, -1])
            pass
        
        return self.get_vector(x = x, y = y, z = z, N = N)
    
    def get_vector(self, x = 0, y = 0, z = 0, N = None):
        if N is None:
            N = self.N
        v = x * N.i + y * N.j + z * N.k
        return v

    def get_random_matrix(self, size, symb = False):
        '''
        @row: int
        @col: int
        '''
        vectors = []
        for i in range(size):
            v = self.get_random_vector(N = self.N, symb = symb, sub = i)
            vectors.append(v)

        matrix = self.convert_to_matrix(*vectors)
        return matrix
        pass
    

    def _update_stmts(self, stmts, result, prob_s = True, soln_s = True, answer_s = True):
        if prob_s:
            stmts[0].append(result['prob_s'].strip())
        
        if soln_s:
            stmts[1].append(result['soln_s'].strip())
        
        if answer_s:
            stmts[2].append(result['answer_s'].strip())
        pass

    def _get_numbered_divs(self, lst, sep = ""):
        import roman
        h_start = """<h3 style="display: inline !important;" >"""
        h_end = "&emsp;</h3>"
        
        s = []
        for idx, item in enumerate(lst):
            s_left = f"""<div>{h_start}{roman.toRoman(idx + 1).lower()}.{h_end}"""
            s_right = f"</div>{sep}"
            s.append(f"{s_left}{item}{s_right}")
            pass
        s = "".join(s)
        return s
    
        pass

    def _get_numbered_list(self, lst):
        s = [f"<li>{x}</li>" for x in lst]
        s = "".join(s)
        s = f"""<ol style="list-style-type: lower-roman !important;" >{s}</ol>"""
        return s
        pass

        
    
    def _parse_N(self, *vectors, N = None):
        if not N is None:
            return N
        for vector in vectors:
            if vector is None:
                continue
            
            if not hasattr(vector, 'get_coord_sys'):
                continue
            
            coord_sys = vector.get_coord_sys()
            if not coord_sys is None:
                return coord_sys
            pass
        return self.N

    def convert_to_matrix(self, *vectors,  N = None):
        try:
            if N is None:
                N = self._parse_N(*vectors,  N = None)
                
            matrix = []
            for v in vectors:
                if hasattr(v, 'coeff'):
                    row = [v.coeff(N.i), v.coeff(N.j), v.coeff(N.k)]
                else:
                    row = v
                if len(vectors) < len(row):
                    sError = f"""Error {self.__class__.__name__}.{inspect.stack()[0][3]}: 
                    the number of vectors ({len(vectors)}) is less than the number of basis vectors.
                    """
                    self._log(sError)
                    raise Exception(sError)
                
                if len(row) < len(vectors):
                    row.extend([1 for i in range(len(row), len(vectors))])
                    pass
                
                matrix.append(row)
                pass
                
            return Matrix(matrix)
        except Exception as e:
            sError = f"""Error {self.__class__.__name__}.{inspect.stack()[0][3]}: {e}
            vectors = {repr(vectors)}
            N = {N}
            """
            self._log(sError)
            self._log(traceback.format_exc())
            return None
        pass
    
    def get_line_eq_latex(self, line_eq):
        line_eq_latex = []
        for item in line_eq:
            item_latex = f""" \\frac{{ {latex(item[0])} }} {{ {latex(item[1])} }} """
            line_eq_latex.append(item_latex)
            pass
        
        line_eq_latex = " = ".join(line_eq_latex)
        
        return line_eq_latex
        pass
    
    def get_magnitude_calc_latex(self, v):
        '''
        @v: a vector or a tuple of three elements
        '''
        if hasattr(v, 'get_coords'):
            coords = v.get_coords()
        else:
            coords = v
        
        magnitude_latex = [f"{{ \\left( {latex(item)} \\right) }}^2" 
                           for item in coords]
        magnitude_latex = " + ".join(magnitude_latex)
        magnitude_latex = f"\\sqrt{{ {magnitude_latex} }}"
        return magnitude_latex
    
    def get_dot_product_calc_latex(self, v1, v2):
        '''
        @v1: a vector
        @v2: a vector
        '''
        coords1 = v1.get_coords()
        coords2 = v2.get_coords()

        calc_latex = []
        for idx, item1 in enumerate(coords1):
            item_latex = f"{latex(item1)} \\cdot {latex(coords2[idx])}"
            calc_latex.append(item_latex)
            pass
        
        calc_latex = " + ".join(calc_latex)
        return calc_latex
    
    pass
#class Geometry3dBaseHelper:
