import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.remember
import app.softwork.bootstrapcompose.Column
import app.softwork.bootstrapcompose.Row
import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive
import org.jetbrains.compose.web.css.*
import org.jetbrains.compose.web.dom.AttrBuilderContext
import org.jetbrains.compose.web.dom.Div
import org.w3c.dom.HTMLDivElement
import space.kscience.dataforge.context.Context
import space.kscience.dataforge.meta.Meta
import space.kscience.plotly.Plot
import space.kscience.plotly.layout
import space.kscience.plotly.models.Trace
import space.kscience.visionforge.Colors
import space.kscience.visionforge.html.Vision
import space.kscience.visionforge.html.zIndex
import space.kscience.visionforge.markup.VisionOfMarkup
import space.kscience.visionforge.plotly.asVision
import space.kscience.visionforge.solid.*
import space.kscience.visionforge.solid.three.compose.ThreeView
import kotlin.math.sqrt

@Composable
fun Plot(
    context: Context,
    meta: Meta = Meta.EMPTY,
    attrs: AttrBuilderContext<HTMLDivElement>? = null,
    block: Plot.() -> Unit,
) = Vision(
    context = context,
    attrs = attrs,
    meta = meta,
    vision = Plot().apply(block).asVision()
)

@Composable
fun Markup(
    context: Context,
    markup: VisionOfMarkup,
    meta: Meta = Meta.EMPTY,
    attrs: AttrBuilderContext<HTMLDivElement>? = null,
) = Vision(
    context = context,
    attrs = attrs,
    meta = meta,
    vision = markup
)


private val h = 100.0

@Composable
fun GravityDemo(context: Context) {
    val velocityTrace = remember {
        Trace {
            name = "velocity"
        }
    }

    val energyTrace = remember {
        Trace {
            name = "energy"
        }
    }

    val markup = remember { VisionOfMarkup() }

    val solid = remember {
        SolidGroup {
            pointLight(200, 200, 200, name = "light") {
                color(Colors.white)
            }
            ambientLight()

            sphere(5.0, "ball") {
                detail = 16
                color("red")
                y = h


                box(200, 5, 200, name = "floor") {
                    y = -2.5
                }
            }
        }
    }

    LaunchedEffect(solid) {
        val ball = solid["ball"]!!
        val g = 10.0
        val dt = 0.1
        var time = 0.0
        var velocity = 0.0
        while (isActive) {
            delay(20)
            time += dt
            velocity -= g * dt
            val energy = g * ball.y.toDouble() + velocity * velocity / 2
            ball.y = ball.y.toDouble() + velocity * dt

            velocityTrace.appendXYLatest(time, ball.y)
            energyTrace.appendXYLatest(time, energy)

            if (ball.y.toDouble() <= 2.5) {
                //conservation of energy
                velocity = sqrt(2 * g * h)
            }

            markup.content = """
                ## Bouncing sphere parameters
                
                **velocity** = $velocity
                
                **energy** = $energy
            """.trimIndent()
        }
    }

    Div({
        style {
            height(100.vh - 50.pt)
        }
    }) {
        Div({
            style {
                height(50.vh)
            }
        }) {
            ThreeView(context, solid)
        }
        Row(attrs = {
            style {
                alignContent(AlignContent.Stretch)
                alignItems(AlignItems.Stretch)
                height(50.vh - 50.pt)
            }
        }) {
            Column {
                Plot(context) {
                    traces(velocityTrace, energyTrace)
                    layout {
                        xaxis.title = "time"
                    }
                }
            }
            Column {
                Markup(context, markup, attrs = {
                    style {
                        width(100.percent)
                        height(100.percent)
                        border(2.pt, LineStyle.Solid, Color.blue)
                        paddingLeft(8.pt)
                        backgroundColor(Color.white)
                        flex(1)
                        zIndex(10000)
                    }
                })
            }
        }
    }
}