package com.rosetta.messenger.ui.components.metaball

import android.graphics.Path
import kotlin.math.abs
import kotlin.math.acos
import kotlin.math.atan2
import kotlin.math.cos
import kotlin.math.sin
import kotlin.math.sqrt

/**
 * Point helper class for metaball calculations
 */
data class Point(var x: Float = 0f, var y: Float = 0f)

/**
 * Reusable scratch points to avoid per-frame allocations during scroll.
 */
class MetaballWorkspace {
    val p1 = Point()
    val p2 = Point()
    val p3 = Point()
    val p4 = Point()
    val h1 = Point()
    val h2 = Point()
    val h3 = Point()
    val h4 = Point()
}

/**
 * Creates a metaball path between two circles - exact port from Telegram's ProfileMetaballView.java
 * 
 * This creates the beautiful "liquid droplet" effect when two circles are close together.
 * The path uses cubic Bezier curves to create smooth organic connections.
 * 
 * @param c1 Center of first circle (top/notch)
 * @param c2 Center of second circle (avatar)
 * @param radius1 Radius of first circle
 * @param radius2 Radius of second circle
 * @param v Connector "thickness" parameter (0-1, higher = thicker connection)
 * @param path Path object to write to (will be rewound)
 * @return true if path was created, false if circles are too far apart
 */
fun createMetaballPath(
    c1: Point,
    c2: Point,
    radius1: Float,
    radius2: Float,
    v: Float,
    path: Path,
    workspace: MetaballWorkspace = MetaballWorkspace()
): Boolean {
    val HALF_PI = (Math.PI / 2).toFloat()
    val HANDLE_SIZE = 3.6f

    // Distance between centers
    val d = dist(c1, c2)
    val maxDist = radius1 + radius2 * 2.32f
    var u1 = 0f
    var u2 = 0f
    val connector = v.coerceIn(0f, 1f)

    // Check if circles can be connected
    if (radius1 == 0f || radius2 == 0f || d > maxDist || d <= abs(radius1 - radius2)) {
        return false
    }

    // Calculate overlap angles
    if (d < radius1 + radius2) {
        val acosArg1 = ((radius1 * radius1 + d * d - radius2 * radius2) / (2 * radius1 * d)).coerceIn(-1f, 1f)
        val acosArg2 = ((radius2 * radius2 + d * d - radius1 * radius1) / (2 * radius2 * d)).coerceIn(-1f, 1f)
        u1 = acos(acosArg1)
        u2 = acos(acosArg2)
    }

    val angleBetweenCenters = angle(c2, c1)
    val maxSpread = acos(((radius1 - radius2) / d).coerceIn(-1f, 1f))

    // Calculate connection angles
    val angle1 = angleBetweenCenters + u1 + (maxSpread - u1) * connector
    val angle2 = angleBetweenCenters - u1 - (maxSpread - u1) * connector
    val angle3 = angleBetweenCenters + Math.PI.toFloat() - u2 - (Math.PI.toFloat() - u2 - maxSpread) * connector
    val angle4 = angleBetweenCenters - Math.PI.toFloat() + u2 + (Math.PI.toFloat() - u2 - maxSpread) * connector

    val p1 = workspace.p1
    val p2 = workspace.p2
    val p3 = workspace.p3
    val p4 = workspace.p4
    val h1 = workspace.h1
    val h2 = workspace.h2
    val h3 = workspace.h3
    val h4 = workspace.h4

    // Get points on circle edges
    getVector(c1, angle1, radius1, p1)
    getVector(c1, angle2, radius1, p2)
    getVector(c2, angle3, radius2, p3)
    getVector(c2, angle4, radius2, p4)
    
    // Calculate handle lengths for Bezier curves
    val totalRadius = radius1 + radius2
    val d2Base = minOf(connector * HANDLE_SIZE, dist(p1, p3) / totalRadius)
    val d2 = d2Base * minOf(1f, d * 2 / totalRadius)

    val r1 = radius1 * d2
    val r2 = radius2 * d2
    
    // Get Bezier handle points
    getVector(p1, angle1 - HALF_PI, r1, h1)
    getVector(p2, angle2 + HALF_PI, r1, h2)
    getVector(p3, angle3 + HALF_PI, r2, h3)
    getVector(p4, angle4 - HALF_PI, r2, h4)
    
    // Build the path
    path.rewind()
    path.moveTo(p1.x, p1.y)
    path.cubicTo(h1.x, h1.y, h3.x, h3.y, p3.x, p3.y)
    path.lineTo(p4.x, p4.y)
    path.cubicTo(h4.x, h4.y, h2.x, h2.y, p2.x, p2.y)
    path.lineTo(p1.x, p1.y)
    path.close()

    return true
}

/**
 * Distance between two points
 */
private fun dist(a: Point, b: Point): Float {
    val dx = a.x - b.x
    val dy = a.y - b.y
    return sqrt(dx * dx + dy * dy)
}

/**
 * Angle from point b to point a
 */
private fun angle(a: Point, b: Point): Float {
    return atan2(a.y - b.y, a.x - b.x)
}

/**
 * Get point at angle and radius from center
 */
private fun getVector(center: Point, angle: Float, radius: Float, out: Point) {
    out.x = center.x + radius * cos(angle)
    out.y = center.y + radius * sin(angle)
}
