package com.jaytux.simd.frontend.ui

import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowDropDown
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.scale
import androidx.compose.ui.focus.onFocusChanged
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.style.TextDecoration
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.PopupProperties
import com.composables.icons.lucide.*
import com.jaytux.simd.frontend.OrError
import com.jaytux.simd.frontend.client.Client
import com.jaytux.simd.frontend.client.Loader
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.launch
import kotlin.uuid.ExperimentalUuidApi

@Composable
fun fontSizeDp(style: TextStyle = LocalTextStyle.current) = with(LocalDensity.current) {
    style.fontSize.toDp()
}

@Composable
fun Spinner(text: String, style: TextStyle = LocalTextStyle.current) = Row {
    CircularProgressIndicator(Modifier.size(fontSizeDp(style) * 2).scale(0.5f))
    Spacer(Modifier.width(5.dp))
    Text(text, Modifier.align(Alignment.CenterVertically), style = style)
}

@Composable
fun ErrorSnackBar(data: SnackbarData) {
    Snackbar(
        containerColor = MaterialTheme.colorScheme.error,
        contentColor = MaterialTheme.colorScheme.onError,
    ) {
        Box(Modifier.padding(5.dp)) {
            Text(data.visuals.message, modifier = Modifier.align(Alignment.Center))
        }
    }
}

@Composable
fun MicroHeader(text: String, modifier: Modifier = Modifier) =
    Text(text, modifier, style = MaterialTheme.typography.bodyLarge)

@Composable
fun Collapsible(expanded: Boolean, onClick: () -> Unit, content: @Composable () -> Unit) {
    Row(Modifier.clickable { onClick() }) {
        Icon(if(expanded) Lucide.ChevronDown else Lucide.ChevronRight, "Expand/Collapse")
        Spacer(Modifier.width(10.dp))
        content()
    }
}

@Composable
fun Checkable(checked: Boolean, onClick: () -> Unit, content: @Composable () -> Unit) {
    Row(Modifier.clickable { onClick() }) {
        Icon(if(checked) Lucide.SquareCheckBig else Lucide.Square, "Check/Uncheck")
        Spacer(Modifier.width(10.dp))
        content()
    }
}

@Composable
fun Indented(content: @Composable ColumnScope.() -> Unit) {
    Row(Modifier.padding(start = 20.dp)) {
        Column {
            content()
        }
    }
}

fun <T> LazyListScope.FilterBlock(
    title: String,
    expanded: Boolean,
    status: String,
    options: List<T>,
    selected: List<Int>,
    onToggleExpand: () -> Unit,
    onToggleSelect: (Int) -> Unit,
    renderT: @Composable (T) -> Unit
) {
    item {
        Collapsible(expanded, onToggleExpand) {
            MicroHeader("$title (${selected.size} selected)")
        }
    }

    if(expanded) {
        if(options.isEmpty()) {
            item {
                Indented {
                    Spinner(status)
                }
            }
        }
        else {
            itemsIndexed(options) { i, t ->
                Indented {
                    Checkable(i in selected, { onToggleSelect(i) }) {
                        renderT(t)
                    }
                }
            }
        }
    }
}

@Composable
fun topBar(data: DataState) = Surface(tonalElevation = 10.dp, shadowElevation = 10.dp, color = MaterialTheme.colorScheme.primaryContainer) {
    val version by data.version
    Box(Modifier.fillMaxWidth().padding(10.dp)) {
        Box(Modifier.align(Alignment.Center)) {
            Text(
                "C/C++ (SIMD) Intrinsics",
                style = MaterialTheme.typography.headlineLarge
            )
        }

        version?.let {
            Column(Modifier.align(Alignment.CenterEnd)) {
                Row {
                    Text(
                        "Intel data: ${it.intelVersion} (last update: ${it.intelUpdate})",
                        style = MaterialTheme.typography.labelSmall
                    )
                    // TODO: add other versioning data (AMD, ARM, ...) when added to DB/backend
                }

                Text("Last database update: ${it.scrapeDate}", style = MaterialTheme.typography.labelSmall)
            }
        }
    }
}

@Composable
fun ColumnScope.FilterColumn(data: DataState) {
    var expandTech by remember { mutableStateOf(false) }
    var expandCategory by remember { mutableStateOf(false) }
    var expandCpuid by remember { mutableStateOf(false) }
    val techs by data.techs; val techMsg by data.techsState; val techSel by data.selectedTechs
    val cats by data.categories; val catMsg by data.categoriesState; val catSel by data.selectedCats
    val cpuid by data.cpuid; val cpuidMsg by data.cpuidState; val cpuidSel by data.selectedCpuids

    Text("Filter intrinsics by:", style = MaterialTheme.typography.headlineSmall)

    LazyColumn {
        FilterBlock(
            "ISA extensions", expandTech, techMsg, techs, techSel,
            { expandTech = !expandTech }, { data.toggleTech(it) }
        ) { Text(it) }
        FilterBlock(
            "Categories", expandCategory, catMsg, cats, catSel,
            { expandCategory = !expandCategory }, { data.toggleCategory(it) }
        ) { Text(it) }
        FilterBlock(
            "CPUIDs", expandCpuid, cpuidMsg, cpuid, cpuidSel,
            { expandCpuid = !expandCpuid }, { data.toggleCpuid(it) }
        ) { Text(it) }
    }
}

@Composable
fun IntrinsicCard(
    summary: Client.IntrinsicSummary,
    details: OrError<Client.IntrinsicDetails>?,
    requestLoad: suspend (Client.IntrinsicSummary) -> Unit
) {
    var expanded by remember { mutableStateOf(false) }
    val scope = rememberCoroutineScope()
    Surface(
        Modifier.fillMaxWidth().padding(5.dp).clickable {
            expanded = !expanded
            if(expanded && details == null) scope.launch { requestLoad(summary) }
        },
        tonalElevation = 2.dp,
        shadowElevation = 2.dp,
        shape = MaterialTheme.shapes.medium
    ) {
        Column(Modifier.padding(10.dp)) {
            Text(summary.name, style = MaterialTheme.typography.headlineSmall)
            if(expanded) {
                Indented {
                    details?.foldCompose({
                        Text(it, color = MaterialTheme.colorScheme.error)
                    }) {
                        Text("Synopsis")
                        Indented {
                            Text("${it.returnType} ${it.name}(${it.params.joinToString(", ") { p -> "${p.type} ${p.name}" }}) [${it.category}]")
                            it.cpuid?.let { cpuid -> Text("CPUID: $cpuid") }
                        }

                        Spacer(Modifier.height(5.dp))
                        Text("Description")
                        Indented {
                            Text(it.description)
                            it.instructions?.let { insn ->
                                Text("Instruction(s):")
                                Indented {
                                    insn.forEach { ins ->
                                        Text("${ins.mnemonic} ${ins.form ?: ""}")
                                    }
                                }
                            }
                        }

                        it.operations?.let { ops ->
                            Spacer(Modifier.height(5.dp))
                            Text("Operations")
                            Indented {
                                Surface(Modifier.fillMaxWidth(), color = MaterialTheme.colorScheme.surfaceDim) {
                                    Text(ops)
                                }
                            }
                        }

                        it.performance?.let { perf ->
                            if(perf.isEmpty()) return@let
                            Spacer(Modifier.height(5.dp))
                            Text("Performance")
                            Indented {
                                Row {
                                    Column {
                                        Text("Architecture")
                                        perf.forEach { p -> Text(p.platform) }
                                    }

                                    Spacer(Modifier.width(20.dp))

                                    Column {
                                        Text("Latency (cycles)")
                                        perf.forEach { p -> Text(p.latency?.toString() ?: "-") }
                                    }

                                    Spacer(Modifier.width(20.dp))

                                    Column {
                                        Text("Throughput (CPI)")
                                        perf.forEach { p -> Text(p.throughput?.toString() ?: "-") }
                                    }
                                }
                            }
                        }
                    } ?: Text("Loading details...", style = MaterialTheme.typography.bodySmall)
                }
            }
        }
    }
}

@OptIn(ExperimentalUuidApi::class)
@Composable
fun ColumnScope.IntrinsicColumn(data: DataState) {
    var nameFilter by remember { mutableStateOf("") }
    var descFilter by remember { mutableStateOf("") }
    var retFilter by remember { mutableStateOf<String>("") }
    val intrinsicState by data.summaryState
    val intrinsicList by data.filterData
    val typeOptions by data.cppTypes
    val typeMsg by data.cppTypesState

    var expandFilter by remember { mutableStateOf(false) }

    OutlinedTextField(
        nameFilter, { nameFilter = it; data.doFilter(nameFilter, descFilter) },
        Modifier.fillMaxWidth(),
        leadingIcon = { Icon(Lucide.SearchCode, "Search") },
        label = { Text("Filter by intrinsic name...") }
    )

    Text(
        (if (expandFilter) "Hide" else "Show") + " additional filters",
        Modifier.clickable { expandFilter = !expandFilter }.align(Alignment.End),
        style = MaterialTheme.typography.labelSmall,
        color = MaterialTheme.colorScheme.primary,
        textDecoration = TextDecoration.Underline
    )

    if (expandFilter) {
        var showingOptions by remember { mutableStateOf(false) }
        OutlinedTextField(
            retFilter, { retFilter = it; showingOptions = true },
            Modifier.clickable { showingOptions = true }.fillMaxWidth(0.66f).onFocusChanged {
                showingOptions = it.isFocused
                if(!it.isFocused) data.setReturn(retFilter)
            },
            trailingIcon = {
                Icon(if (showingOptions) Lucide.ChevronUp else Lucide.ChevronDown, "Show options")
            },
            label = { Text("Filter by return type...") },
            isError = retFilter !in typeOptions
        )
        DropdownMenu(
            showingOptions, { showingOptions = false },
            properties = PopupProperties()
        ) {
            if (typeOptions.isEmpty()) {
                DropdownMenuItem(
                    { Spinner(typeMsg) }, { retFilter = "" }
                )
            } else {
                DropdownMenuItem(
                    { Text("(none)") }, { retFilter = "" }
                )
                typeOptions.filter{ retFilter in it }.take(10).forEach { it ->
                    DropdownMenuItem(
                        { Text(it) }, { retFilter = it }
                    )
                }
            }
        }

        OutlinedTextField(
            descFilter,
            { descFilter = it; data.doFilter(nameFilter, descFilter) },
            Modifier.fillMaxWidth(),
            label = { Text("Filter by description...") })
    }

    if(intrinsicList.isEmpty()) {
        Box(Modifier.fillMaxSize()) {
            Column(Modifier.align(Alignment.Center)) {
                Text("Loading intrinsics...")
                Spinner(intrinsicState)
            }
        }
    }
    else {
        if(intrinsicState != Loader.progressFinished) {
            Spinner(intrinsicState, MaterialTheme.typography.bodySmall)
        }

        LazyColumn {
            items(intrinsicList) { it ->
                val details by data[it.id]
                IntrinsicCard(it, details) { data.loadDetails(it.id) }
            }
        }
    }
}


































