'''
Created on 18-Jul-2024

@author: b thakur
'''
import inspect
import traceback
import random

from sympy import (latex, Symbol, UnevaluatedExpr as ueval,
                   )
import sympy as sp

from ._base_helper import BaseHelper

from xv.util import list_to_str
from xv.km import SympyUnitSystem

# _fluid_mechanics_helper
# FluidMechanicsHelper


class FluidMechanicsHelper(BaseHelper):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        pass
    
    def get_problem_incompressible_fluid(self, *args, **kwargs):
        sInfo = f"""Processing {self.__class__.__name__}.{inspect.stack()[0][3]}: """
        self._log(sInfo.strip())

        index = kwargs.get('index', None)
        
        try:
            
            if index is None:
                random_choices = list(range(1, 3))
        
                identifier = f"{self.__class__.__name__}{id(self)}.{inspect.stack()[0][3]}.random_choices"
                random_choice = self.get_no_repeat_random_choice(identifier, random_choices)
            else:
                random_choice = index
            
            if random_choice == 1:
                return self._get_incompressible_fluid_speed()
    
            if random_choice == 2:
                return self._get_bernoullis_theorem_computation()
    
            return self._get_incompressible_fluid_speed()
        
        except Exception as e:
            sError = f"""Error {self.__class__.__name__}.{inspect.stack()[0][3]}: {e}
            """
            self._log(sError)
            self._log(traceback.format_exc())
            pass
        
        return None
        pass


    def _get_incompressible_fluid_speed(self, **kwargs):
        sInfo = f"""Processing {self.__class__.__name__}.{inspect.stack()[0][3]}: """
        self._log(sInfo.strip())

        
        su_system = SympyUnitSystem(verbose = self.verbose)


        A = Symbol("A")
        A1 = Symbol("A_1")
        A2 = Symbol("A_2")
        
        A_label = Symbol(f"\\text{{cross-sectional area}}")

        A_unit = su_system.to_unit("meter") ** 2
        
        A1_val = random.randint(2, 9) * A_unit
        A2_val = random.randint(2, 9) * A_unit
        

        v = Symbol("v")
        v1 = Symbol("v_1")
        v2 = Symbol("v_2")
        
        v_label = Symbol(f"\\text{{velocity}}")
        
        v_unit = su_system.to_unit('meter') / su_system.to_unit("second")
        
        v1_val = random.randint(1, 9) * v_unit
        v2_val = A1_val * v1_val / A2_val
        
        
        V = Symbol("V")
        V1 = Symbol("V_1")
        V2 = Symbol("V_2")
        
        V_unit = su_system.to_unit("meter") ** 3
        
        V1_val = A1_val * v1_val
        V2_val = A2_val * v2_val
        
        V_label = Symbol(f"\\text{{volume}}")


        prob_s = f"""
        A pipe of varying cross-section has a cross-sectional 
        area of $\\displaystyle {latex(A1_val)}$ at one end and 
        $\\displaystyle {latex(A2_val)}$ at the other end. 
        If water flows through the pipe at a velocity of 
        $\\displaystyle {latex(v1_val)}$ at the narrower end, 
        find the velocity of water at the wider end.
        """
        prob_s = prob_s.replace("\n", " ").strip()

        soln_stmts = []

        soln_stmt = f"""
        For incompressible fluid, the volume does not change.
        Therefore, the volume of water coming in from one end 
        will be equal to the water coming out from another end.
        """
        soln_stmts.append(soln_stmt.replace("\n", " ").strip())
        
        eq_V_symb = sp.Eq(V1, V2)
        eq_icomp_symb = sp.Eq(A1 * v1, A2 * v2)
        
        eq_icomp_ueval = sp.Eq(Symbol(f"{latex(A1_val)} \\cdot {latex(v1_val)}"), 
                               Symbol(f"{latex(A2_val)} \\cdot {latex(v2)}"))
        
        eq_v2_ueval = sp.Eq(v2, 
                           Symbol(f"\\frac {{ {latex(eq_icomp_ueval.lhs)} }} {{ {latex(A2_val)} }}"))
        
        eq_v2 = sp.Eq(v2, v2_val)

        soln_stmt = f"""
        Therefore,
        
        $\\displaystyle 
        \\begin {{aligned}}
        
        & {latex (eq_V_symb)}
        \\\\ \\\\
        
        \\implies
        & {latex (eq_icomp_symb)}
        \\\\ \\\\
        
        \\implies
        & {latex (eq_icomp_ueval)}
        \\\\ \\\\
        
        \\implies
        & {latex (eq_v2_ueval)}
        \\\\ \\\\
        
        \\implies
        & {latex (eq_v2)}
        \\\\ \\\\
        
        \\end {{aligned}}
        $
        """
        soln_stmts.append(soln_stmt.strip())
        
        
        answer_s = f"""
        """
        answer_s = answer_s.replace("\n", " ").strip()

        soln_s = "\n\n".join(soln_stmts)
        
        result = {
            'prob_s': prob_s.strip(), 
            'soln_s': soln_s.strip(), 
            'answer_s': answer_s.strip(), 
            }
        
        return result
        pass


    def _get_bernoullis_theorem_computation(self, **kwargs):
        sInfo = f"""Processing {self.__class__.__name__}.{inspect.stack()[0][3]}: """
        self._log(sInfo.strip())

        
        su_system = SympyUnitSystem(verbose = self.verbose)

        A = Symbol("A")
        A1 = Symbol("A_1")
        A2 = Symbol("A_2")
        
        A_label = Symbol(f"\\text{{cross-sectional area}}")

        A_unit = su_system.to_unit("meter") ** 2
        
        A1_val = random.randint(2, 9) * A_unit
        A2_val = random.randint(2, 9) * A_unit
        

        v = Symbol("v")
        v1 = Symbol("v_1")
        v2 = Symbol("v_2")
        
        v_label = Symbol(f"\\text{{velocity}}")
        
        v_unit = su_system.to_unit('meter') / su_system.to_unit("second")
        
        v1_val = random.randint(1, 9) / 10 * v_unit
        v2_val = A1_val * v1_val / A2_val
        
        
        P = Symbol("P")
        P1 = Symbol("P_1")
        P2 = Symbol("P_2")
        
        P_unit = su_system.to_unit("pascal")
        
        P1_val = random.randint(2, 9) * P_unit
        P2_val = random.randint(2, 9) * P_unit
        
        P_label = Symbol(f"\\text{{pressure}}")


        V = Symbol("V")
        V_unit = su_system.to_unit("meter") ** 3
        V_val = A1_val * v1_val
        V_label = Symbol(f"\\text{{volume}}")

        rho = Symbol("rho")
        rho_unit = su_system.to_unit("kg") / su_system.to_unit("meter") ** 3
        rho_val = random.randint(2, 100) / 10 * rho_unit
        rho_label = Symbol(f"\\text{{density}}")


        m = Symbol("m")
        m_unit = su_system.to_unit("kg")
        m_val = V_val * rho_val
        m_label = Symbol(f"\\text{{mass}}")
        


        KE = Symbol("KE")
        KE1 = Symbol("KE_1")
        KE2 = Symbol("KE_2")
        
        KE_unit = su_system.to_unit("joule")
        
        KE1_val = sp.Rational(1, 2) * m_val * v1_val**2
        KE2_val = sp.Rational(1, 2) * m_val * v2_val**2
        
        KE_label = Symbol(f"\\text{{kinetic energy}}")


        h = Symbol("h")
        h1 = Symbol("h_1")
        h2 = Symbol("h_2")
        
        h_unit = su_system.to_unit("meter")
        
        h1_val = A1_val * v1_val
        h2_val = A2_val * v2_val
        
        h_label = Symbol(f"\\text{{height}}")

        g = Symbol("g")
        g_unit = su_system.to_unit("meter") / su_system.to_unit("second") ** 2
        g_val = 9.8 * g_unit


        PE = Symbol("PE")
        PE1 = Symbol("PE_1")
        PE2 = Symbol("PE_2")
        
        PE_unit = su_system.to_unit("joule")
        
        PE1_val = m_val * g_val * h1_val
        PE2_val = m_val * g_val * h2_val
        
        PE_label = Symbol(f"\\text{{potential energy}}")

        
        W = Symbol("W")
        W1 = Symbol("W_1")
        W2 = Symbol("W_2")
        
        W_unit = su_system.to_unit("joule")
        
        W1_val = A1_val * v1_val
        W2_val = A2_val * v2_val
        
        W_label = Symbol(f"\\text{{work}}")

        

        prob_s = f"""
        Water is flowing through a pipe of varying diameter and 
        at different heights from the ground. 
        At a point where the velocity of water 
        is $\\displaystyle {latex(v1_val)}$, 
        the pressure is $\\displaystyle {latex(P1_val)}$ and 
        the height from the ground is $\\displaystyle {latex(h1_val)}$. 
        What will be the pressure at a point 
        where the velocity of water i$\\displaystyle {latex(v2_val)}$ and 
        the height from the ground is $\\displaystyle {latex(h2_val)}$?        
        """
        prob_s = prob_s.replace("\n", " ").strip()

        soln_stmts = []

        soln_stmt = f"""
        The law of conservation of energy will apply to the flowing fluid.
        Also, due to incompressibility of fluids, the volume of water coming in from one end 
        will be equal to the water coming out from another end.
        """
        soln_stmts.append(soln_stmt.replace("\n", " ").strip())

        soln_stmt = f"""
        Therefore,
        
        $\\displaystyle 
        \\begin {{aligned}}
        
        """
        soln_stmts.append(soln_stmt.strip())
        

        
        
        eq_energy_symb = sp.Eq(Symbol(f"{latex(W1)} + {latex(PE1)} + {latex(KE1)}"),
                               Symbol(f"{latex(W2)} + {latex(PE2)} + {latex(KE2)}"))
        
        soln_stmt = f"""
        & {latex (eq_energy_symb)}
        \\\\ \\\\
        """
        soln_stmts.append(soln_stmt.strip())

        
        W1_val_symb = Symbol(f"{latex(P1)} {latex(V)}")
        PE1_val_symb = Symbol(f"{latex(m)} {latex(g)} {latex(h1)}")
        KE1_val_symb = Symbol(f"{latex(sp.Rational(1, 2))} {latex(m)} {latex(v1 ** 2)}")
        
        W2_val_symb = Symbol(f"{latex(P2)} {latex(V)}")
        PE2_val_symb = Symbol(f"{latex(m)} {latex(g)} {latex(h2)}")
        KE2_val_symb = Symbol(f"{latex(sp.Rational(1, 2))} {latex(m)} {latex(v2 ** 2)}")
        
        eq_bern_symb = sp.Eq(
            Symbol(f"{latex(W1_val_symb)} + {latex(PE1_val_symb)} + {latex(KE1_val_symb)}"),
            Symbol(f"{latex(W2_val_symb)} + {latex(PE2_val_symb)} + {latex(KE2_val_symb)}")
            )

        soln_stmt = f"""
        \\implies
        & {latex (eq_bern_symb)}
        \\\\ \\\\
        """
        soln_stmts.append(soln_stmt.strip())
        
        
        
        W1_val_bern_symb = Symbol(f"{latex(P1)}")
        PE1_val_bern_symb = Symbol(f"{latex(rho)} {latex(g)} {latex(h1)}")
        KE1_val_bern_symb = Symbol(f"{latex(sp.Rational(1, 2))} {latex(rho)} {latex(v1 ** 2)}")
        
        W2_val_bern_symb = Symbol(f"{latex(P2)}")
        PE2_val_bern_symb = Symbol(f"{latex(rho)} {latex(g)} {latex(h2)}")
        KE2_val_bern_symb = Symbol(f"{latex(sp.Rational(1, 2))} {latex(rho)} {latex(v2 ** 2)}")
        
        eq_bern_rho_symb = sp.Eq(
            Symbol(f"{latex(W1_val_bern_symb)} + {latex(PE1_val_bern_symb)} + {latex(KE1_val_bern_symb)}"),
            Symbol(f"{latex(W2_val_bern_symb)} + {latex(PE2_val_bern_symb)} + {latex(KE2_val_bern_symb)}")
            )
        
        soln_stmt = f"""
        \\implies
        & {latex (eq_bern_rho_symb)}
        \\qquad \\text{{ divided both sides by }} {latex(V)}
        \\\\ \\\\
        """
        soln_stmts.append(soln_stmt.strip())
        
        
        W1_val_bern_symb = Symbol(f"{latex(P1)}")
        PE1_val_bern_symb = Symbol(f"{latex(rho)} {latex(g)} {latex(h1)}")
        KE1_val_bern_symb = Symbol(f"{latex(sp.Rational(1, 2))} {latex(rho)} {latex(v1 ** 2)}")
        
        W2_val_bern_symb = Symbol(f"{latex(P2)}")
        PE2_val_bern_symb = Symbol(f"{latex(rho)} {latex(g)} {latex(h2)}")
        KE2_val_bern_symb = Symbol(f"{latex(sp.Rational(1, 2))} {latex(rho)} {latex(v2 ** 2)}")
        
        eq_bern_rho_symb = sp.Eq(
            Symbol(f"{latex(W1_val_bern_symb)} + {latex(PE1_val_bern_symb)} + {latex(KE1_val_bern_symb)}"),
            Symbol(f"{latex(W2_val_bern_symb)} + {latex(PE2_val_bern_symb)} + {latex(KE2_val_bern_symb)}")
            )

        soln_stmt = f"""
        \\end {{aligned}}
        $ 
        """
        soln_stmts.append(soln_stmt.strip())
        
        
        
        answer_s = f"""
        """
        answer_s = answer_s.replace("\n", " ").strip()

        soln_s = "\n\n".join(soln_stmts)
        
        result = {
            'prob_s': prob_s.strip(), 
            'soln_s': soln_s.strip(), 
            'answer_s': answer_s.strip(), 
            }
        
        return result
        pass

        

    pass
#class FluidMechanicsHelper:

    