"""
طراحی شده توسط محمد نورائی
تمامی حقوق محفوظ است (C) ۱۴۰۵ - تا پایان زمان

Written by Mohammad Nouraei.
CopyRight (C) 2026 - Until end of time
"""
import math
from js import document, window, THREE, katex, requestAnimationFrame
from pyodide.ffi import create_proxy, to_js

shape_select = document.getElementById('shape-select')
inputs_container = document.getElementById('inputs-container')

input_configs = {
    'sphere': [
        {'label': 'شعاع (r):', 'id': 'radius', 'val': 5}
    ],
    'hemisphere': [
        {'label': 'شعاع (r):', 'id': 'radius', 'val': 5}
    ],
    'quarter-sphere': [
        {'label': 'شعاع (r):', 'id': 'radius', 'val': 5}
    ],
    'cylinder': [
        {'label': 'شعاع (r):', 'id': 'radius', 'val': 3},
        {'label': 'ارتفاع (h):', 'id': 'height', 'val': 7}
    ],
    'cone': [
        {'label': 'شعاع (r):', 'id': 'radius', 'val': 4},
        {'label': 'ارتفاع (h):', 'id': 'height', 'val': 8}
    ],
    'prism': [
        {'label': 'طول (L):', 'id': 'length', 'val': 5},
        {'label': 'عرض (W):', 'id': 'width', 'val': 4},
        {'label': 'ارتفاع (H):', 'id': 'height', 'val': 6}
    ],
    'capsule': [
        {'label': 'شعاع (r):', 'id': 'radius', 'val': 3},
        {'label': 'ارتفاع استوانه (h):', 'id': 'height', 'val': 5}
    ],
    'torus': [
        {'label': 'شعاع لوله (r):', 'id': 'tube_r', 'val': 1.5},
        {'label': 'شعاع اصلی (R):', 'id': 'major_r', 'val': 4.5}
    ],
    'ellipsoid': [
        {'label': 'نیم‌محور a:', 'id': 'axis_a', 'val': 4},
        {'label': 'نیم‌محور b:', 'id': 'axis_b', 'val': 3},
        {'label': 'نیم‌محور c:', 'id': 'axis_c', 'val': 5}
    ]
}


def get_val(elem_id, default=1.0):
    elem = document.getElementById(elem_id)
    if elem:
        try:
            v = float(elem.value)
            return v if v > 0 else default
        except Exception:
            return default
    return default


def update_inputs(event=None):
    shape = shape_select.value
    inputs_container.innerHTML = ''
    configs = input_configs.get(shape, [])
    for cfg in configs:
        div = document.createElement('div')
        div.className = 'input-row'
        div.innerHTML = f"<label>{cfg['label']}</label><input type=\"number\" id=\"{cfg['id']}\" value=\"{cfg['val']}\" min=\"0.1\" step=\"0.1\" style=\"width: 100%; max-width: 160px;\">"
        inputs_container.appendChild(div)

    calculate_and_draw()


shape_select.addEventListener('change', create_proxy(update_inputs))


def setup_tabs():
    def on_tab_click(event):
        btn = event.currentTarget
        for b in document.querySelectorAll('.tab-btn'):
            b.classList.remove('active')
        for v in document.querySelectorAll('.steps-view'):
            v.classList.remove('active')
        btn.classList.add('active')
        target_id = btn.getAttribute('data-tab')
        document.getElementById(target_id).classList.add('active')

    tab_proxy = create_proxy(on_tab_click)
    for btn in document.querySelectorAll('.tab-btn'):
        btn.addEventListener('click', tab_proxy)


setup_tabs()

container = document.getElementById('threejs-container')
scene = THREE.Scene.new()
scene.background = THREE.Color.new(0xf8fafc)

camera = THREE.PerspectiveCamera.new(45, container.clientWidth / container.clientHeight, 0.1, 1000)

renderer_opts = to_js({"antialias": True}, dict_converter=window.Object.fromEntries)
renderer = THREE.WebGLRenderer.new(renderer_opts)
renderer.setSize(container.clientWidth, container.clientHeight)
renderer.setPixelRatio(window.devicePixelRatio)
container.appendChild(renderer.domElement)

controls = THREE.OrbitControls.new(camera, renderer.domElement)
controls.enableDamping = True
controls.dampingFactor = 0.05

dir_light1 = THREE.DirectionalLight.new(0xffffff, 0.9)
dir_light1.position.set(10, 15, 10)
scene.add(dir_light1)

dir_light2 = THREE.DirectionalLight.new(0xffffff, 0.4)
dir_light2.position.set(-10, -10, -10)
scene.add(dir_light2)

scene.add(THREE.AmbientLight.new(0xffffff, 0.6))

grid_helper = THREE.GridHelper.new(30, 30, 0xd0d0d0, 0xe2e8f0)
grid_helper.position.y = -5
scene.add(grid_helper)

current_mesh = None
current_wireframe = None


def create_shape_3d(shape_type, params, color, opacity):
    global current_mesh, current_wireframe
    if current_mesh:
        scene.remove(current_mesh)
        if hasattr(current_mesh, 'geometry'):
            current_mesh.geometry.dispose()
        if hasattr(current_mesh, 'material'):
            current_mesh.material.dispose()

    if current_wireframe:
        scene.remove(current_wireframe)
        if hasattr(current_wireframe, 'geometry'):
            current_wireframe.geometry.dispose()
        if hasattr(current_wireframe, 'material'):
            current_wireframe.material.dispose()

    mat = THREE.MeshPhongMaterial.new()
    mat.color = THREE.Color.new(color)
    mat.transparent = True
    mat.opacity = float(opacity)
    mat.side = THREE.DoubleSide
    mat.shininess = 60

    geometry = None

    if shape_type == 'sphere':
        geometry = THREE.SphereGeometry.new(params['radius'], 32, 32)

    elif shape_type == 'hemisphere':
        geometry = THREE.SphereGeometry.new(params['radius'], 32, 32, 0, math.pi * 2, 0, math.pi / 2)

    elif shape_type == 'quarter-sphere':
        geometry = THREE.SphereGeometry.new(params['radius'], 32, 32, 0, math.pi / 2, 0, math.pi / 2)

    elif shape_type == 'cylinder':
        geometry = THREE.CylinderGeometry.new(params['radius'], params['radius'], params['height'], 32)

    elif shape_type == 'cone':
        geometry = THREE.ConeGeometry.new(params['radius'], params['height'], 32)

    elif shape_type == 'prism':
        geometry = THREE.BoxGeometry.new(params['length'], params['height'], params['width'])

    elif shape_type == 'capsule':
        r, h = params['radius'], params['height']
        pts = []
        steps = 16
        for i in range(steps + 1):
            theta = -math.pi / 2 + (i / steps) * (math.pi / 2)
            pts.append(THREE.Vector2.new(r * math.cos(theta), r * math.sin(theta) - h / 2))
        for i in range(steps + 1):
            theta = (i / steps) * (math.pi / 2)
            pts.append(THREE.Vector2.new(r * math.cos(theta), r * math.sin(theta) + h / 2))
        geometry = THREE.LatheGeometry.new(to_js(pts), 32)

    elif shape_type == 'torus':
        geometry = THREE.TorusGeometry.new(params['major_r'], params['tube_r'], 20, 60)

    elif shape_type == 'ellipsoid':
        geometry = THREE.SphereGeometry.new(1, 32, 32)
        geometry.scale(params['axis_a'], params['axis_b'], params['axis_c'])

    if geometry:
        current_mesh = THREE.Mesh.new(geometry, mat)
        scene.add(current_mesh)

        wire_geom = THREE.WireframeGeometry.new(geometry)
        wire_opts = to_js({"color": 0x1d4ed8, "transparent": True, "opacity": 0.25}, dict_converter=window.Object.fromEntries)
        wire_mat = THREE.LineBasicMaterial.new(wire_opts)
        current_wireframe = THREE.LineSegments.new(wire_geom, wire_mat)
        scene.add(current_wireframe)

        geometry.computeBoundingSphere()
        bs = geometry.boundingSphere
        radius = bs.radius if bs else 8.0
        camera.position.set(radius * 1.8, radius * 1.3, radius * 2.2)
        controls.target.set(0, 0, 0)
        controls.update()


def calculate_and_draw(event=None):
    shape = shape_select.value
    color = document.getElementById('color-input').value
    opacity = document.getElementById('opacity-input').value
    document.getElementById('legend-color').style.backgroundColor = color

    v_steps = ''
    a_steps = ''
    result_text = ''
    params = {}

    # ۱) کره
    if shape == 'sphere':
        r = get_val('radius', 5.0)
        params = {'radius': r}
        vol = (4/3) * math.pi * (r ** 3)
        area = 4 * math.pi * (r ** 2)

        v_steps = f'<p>فرمول حجم کره:</p><div class="math-display">V = \\frac{{4}}{{3}} \\pi r^3</div><p>جایگذاری مقادیر:</p><div class="math-display">V = \\frac{{4}}{{3}} \\times \\pi \\times ({r})^3 \\approx {vol:.2f}</div>'
        a_steps = f'<p>فرمول مساحت رویه کره:</p><div class="math-display">A = 4 \\pi r^2</div><p>جایگذاری مقادیر:</p><div class="math-display">A = 4 \\times \\pi \\times ({r})^2 \\approx {area:.2f}</div>'
        result_text = f"حجم: {vol:.2f} | مساحت کل: {area:.2f}"

    # ۲) نیم‌کره
    elif shape == 'hemisphere':
        r = get_val('radius', 5.0)
        params = {'radius': r}
        vol = (2/3) * math.pi * (r ** 3)
        area = 3 * math.pi * (r ** 2)

        v_steps = f'<p>فرمول حجم نیم‌کره:</p><div class="math-display">V = \\frac{{2}}{{3}} \\pi r^3</div><p>جایگذاری مقادیر:</p><div class="math-display">V = \\frac{{2}}{{3}} \\times \\pi \\times ({r})^3 \\approx {vol:.2f}</div>'
        a_steps = f'<p>مساحت کل نیم‌کره توپر (پوسته + قاعده دایره‌ای):</p><div class="math-display">A = 2\\pi r^2 + \\pi r^2 = 3\\pi r^2</div><p>محاسبه:</p><div class="math-display">A = 3 \\times \\pi \\times ({r})^2 \\approx {area:.2f}</div>'
        result_text = f"حجم: {vol:.2f} | مساحت کل: {area:.2f}"

    # ۳) ربع‌کره
    elif shape == 'quarter-sphere':
        r = get_val('radius', 5.0)
        params = {'radius': r}
        vol = (1/3) * math.pi * (r ** 3)
        area = 2 * math.pi * (r ** 2)

        v_steps = f'<p>فرمول حجم ربع‌کره:</p><div class="math-display">V = \\frac{{1}}{{3}} \\pi r^3</div><p>جایگذاری مقادیر:</p><div class="math-display">V = \\frac{{1}}{{3}} \\times \\pi \\times ({r})^3 \\approx {vol:.2f}</div>'
        a_steps = f'<p>مساحت کل ربع‌کره (یک‌چهارم پوسته + دو برش نیم‌دایره):</p><div class="math-display">A = \\pi r^2 + 2 \\left(\\frac{{\\pi r^2}}{{2}}\\right) = 2\\pi r^2</div><p>محاسبه:</p><div class="math-display">A = 2 \\times \\pi \\times ({r})^2 \\approx {area:.2f}</div>'
        result_text = f"حجم: {vol:.2f} | مساحت کل: {area:.2f}"

    # ۴) استوانه
    elif shape == 'cylinder':
        r = get_val('radius', 3.0)
        h = get_val('height', 7.0)
        params = {'radius': r, 'height': h}
        vol = math.pi * r * r * h
        area = 2 * math.pi * r * (r + h)

        v_steps = f'<p>فرمول حجم استوانه:</p><div class="math-display">V = \\pi r^2 h</div><p>محاسبه:</p><div class="math-display">V = \\pi \\times ({r})^2 \\times {h} \\approx {vol:.2f}</div>'
        a_steps = f'<p>فرمول مساحت کل استوانه:</p><div class="math-display">A = 2\\pi r h + 2\\pi r^2 = 2\\pi r(r + h)</div><p>محاسبه:</p><div class="math-display">A = 2 \\times \\pi \\times {r} \\times ({r} + {h}) \\approx {area:.2f}</div>'
        result_text = f"حجم: {vol:.2f} | مساحت کل: {area:.2f}"

    # ۶) مخروط
    elif shape == 'cone':
        r = get_val('radius', 4.0)
        h = get_val('height', 8.0)
        params = {'radius': r, 'height': h}
        s = math.sqrt(r*r + h*h)
        vol = (1/3) * math.pi * r * r * h
        area = math.pi * r * (r + s)

        v_steps = f'<p>فرمول حجم مخروط:</p><div class="math-display">V = \\frac{{1}}{{3}} \\pi r^2 h</div><p>محاسبه:</p><div class="math-display">V = \\frac{{1}}{{3}} \\times \\pi \\times ({r})^2 \\times {h} \\approx {vol:.2f}</div>'
        a_steps = f'<p>خط مولد ($s$) و مساحت کل مخروط:</p><div class="math-display">s = \\sqrt{{r^2 + h^2}} = \\sqrt{{{r}^2 + {h}^2}} \\approx {s:.2f}</div><div class="math-display">A = \\pi r (r + s) = \\pi \\times {r} \\times ({r} + {s:.2f}) \\approx {area:.2f}</div>'
        result_text = f"حجم: {vol:.2f} | مساحت کل: {area:.2f}"

    # ۸) مکعب مستطیل / منشور چهارضلعی
    elif shape == 'prism':
        l = get_val('length', 5.0)
        w = get_val('width', 4.0)
        h = get_val('height', 6.0)
        params = {'length': l, 'width': w, 'height': h}
        vol = l * w * h
        area = 2 * (l*w + l*h + w*h)

        v_steps = f'<p>فرمول حجم مکعب مستطیل:</p><div class="math-display">V = L \\times W \\times H</div><p>محاسبه:</p><div class="math-display">V = {l} \\times {w} \\times {h} = {vol:.2f}</div>'
        a_steps = f'<p>فرمول مساحت کل مکعب مستطیل:</p><div class="math-display">A = 2(LW + LH + WH)</div><p>محاسبه:</p><div class="math-display">A = 2({l*w} + {l*h} + {w*h}) = {area:.2f}</div>'
        result_text = f"حجم: {vol:.2f} | مساحت کل: {area:.2f}"

    # ۱۱) کپسول
    elif shape == 'capsule':
        r = get_val('radius', 3.0)
        h = get_val('height', 5.0)
        params = {'radius': r, 'height': h}
        vol = math.pi * (r ** 2) * h + (4/3) * math.pi * (r ** 3)
        area = 2 * math.pi * r * h + 4 * math.pi * (r ** 2)

        v_steps = f'<p>حجم کپسول (استوانه + کره کامل):</p><div class="math-display">V = \\pi r^2 h + \\frac{{4}}{{3}} \\pi r^3</div><p>محاسبه:</p><div class="math-display">V = \\pi \\times ({r})^2 \\times {h} + \\frac{{4}}{{3}}\\pi \\times ({r})^3 \\approx {vol:.2f}</div>'
        a_steps = f'<p>مساحت رویه کپسول (مساحت جانبی استوانه + رویه کره):</p><div class="math-display">A = 2\\pi r h + 4\\pi r^2</div><p>محاسبه:</p><div class="math-display">A = 2\\pi \\times {r} \\times {h} + 4\\pi \\times ({r})^2 \\approx {area:.2f}</div>'
        result_text = f"حجم: {vol:.2f} | مساحت کل: {area:.2f}"

    # ۱۲) طوقه / دونات (Torus)
    elif shape == 'torus':
        r = get_val('tube_r', 1.5)
        R = get_val('major_r', 4.5)
        if r >= R:
            r, R = R / 3.0, R

        params = {'tube_r': r, 'major_r': R}
        vol = 2 * (math.pi ** 2) * R * (r ** 2)
        area = 4 * (math.pi ** 2) * R * r

        v_steps = f'<p>فرمول حجم طوقه (دوناتی):</p><div class="math-display">V = 2 \\pi^2 R r^2</div><p>محاسبه:</p><div class="math-display">V = 2 \\times \\pi^2 \\times {R} \\times ({r})^2 \\approx {vol:.2f}</div>'
        a_steps = f'<p>فرمول مساحت پوسته رویه طوقه:</p><div class="math-display">A = 4 \\pi^2 R r</div><p>محاسبه:</p><div class="math-display">A = 4 \\times \\pi^2 \\times {R} \\times {r} \\approx {area:.2f}</div>'
        result_text = f"حجم: {vol:.2f} | مساحت کل: {area:.2f}"

    # ۱۳) بیضی‌گون (Ellipsoid)
    elif shape == 'ellipsoid':
        a = get_val('axis_a', 4.0)
        b = get_val('axis_b', 3.0)
        c = get_val('axis_c', 5.0)
        params = {'axis_a': a, 'axis_b': b, 'axis_c': c}
        vol = (4/3) * math.pi * a * b * c

        p = 1.6075
        area = 4 * math.pi * ((( (a**p)*(b**p) + (a**p)*(c**p) + (b**p)*(c**p) ) / 3.0) ** (1/p))

        v_steps = f'<p>فرمول حجم بیضی‌گون:</p><div class="math-display">V = \\frac{{4}}{{3}} \\pi a b c</div><p>محاسبه:</p><div class="math-display">V = \\frac{{4}}{{3}} \\times \\pi \\times {a} \\times {b} \\times {c} \\approx {vol:.2f}</div>'
        a_steps = f'<p>فرمول تقریبی مساحت پوسته بیضی‌گون (Knud Thomsen):</p><div class="math-display">A \\approx 4\\pi \\left( \\frac{{a^p b^p + a^p c^p + b^p c^p}}{{3}} \\right)^{{1/p}} \\quad (p \\approx 1.6075)</div><p>محاسبه:</p><div class="math-display">A \\approx {area:.2f}</div>'
        result_text = f"حجم: {vol:.2f} | مساحت تقریبی: {area:.2f}"

    document.getElementById('tab-v').innerHTML = v_steps
    document.getElementById('tab-a').innerHTML = a_steps
    document.getElementById('banner-result').innerText = result_text

    render_katex_math()
    create_shape_3d(shape, params, color, opacity)


def render_katex_math():
    katex_opts = to_js({"throwOnError": False, "displayMode": True}, dict_converter=window.Object.fromEntries)
    for el in document.querySelectorAll('.math-display'):
        try:
            katex.render(el.innerText, el, katex_opts)
        except Exception:
            pass


document.getElementById('btn-calculate').addEventListener('click', create_proxy(calculate_and_draw))
document.getElementById('color-input').addEventListener('input', create_proxy(calculate_and_draw))
document.getElementById('opacity-input').addEventListener('input', create_proxy(calculate_and_draw))

update_inputs()


def animate(timestamp=None):
    global current_mesh, current_wireframe
    requestAnimationFrame(create_proxy(animate))

    if current_mesh:
        current_mesh.rotation.y += 0.006
        if current_wireframe:
            current_wireframe.rotation.y = current_mesh.rotation.y
            current_wireframe.rotation.x = current_mesh.rotation.x
            current_wireframe.rotation.z = current_mesh.rotation.z

    controls.update()
    renderer.render(scene, camera)


animate()


def on_window_resize(event=None):
    if container:
        camera.aspect = container.clientWidth / container.clientHeight
        camera.updateProjectionMatrix()
        renderer.setSize(container.clientWidth, container.clientHeight)


window.addEventListener('resize', create_proxy(on_window_resize))