package theorycrafter.ui.graphs

import androidx.compose.foundation.layout.*
import androidx.compose.material.Text
import androidx.compose.runtime.*
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import compose.utils.VSpacer
import compose.widgets.*
import eve.data.NNBSP
import eve.data.asScanResolution
import eve.data.asSignatureRadius
import eve.data.millisAsTimeSec
import theorycrafter.FitHandle
import theorycrafter.TheorycrafterContext
import theorycrafter.fitting.lockTime
import theorycrafter.ui.TheorycrafterTheme
import theorycrafter.ui.tooltip
import theorycrafter.ui.widgets.DoubleTextField
import theorycrafter.ui.widgets.maxPrecisionFormatter


/**
 * The graphs window pane for exploring the lock time on various targets.
 */
@Composable
fun LockTimeGraphPane(initialFitHandle: FitHandle?) {
    val lockers: MutableList<Locker> = rememberSaveableListOfGraphSources(initialFitHandle)
    GraphPaneScaffold(
        graph = { modifier ->
            LockTimeGraph(
                lockers = lockers,
                modifier = modifier
            )
        },
        paramsEditor = { modifier ->
            LockersEditor(
                lockers = lockers,
                virtualFitScanResolution = TheorycrafterContext.settings.graphs.virtualFitScanResolution,
                modifier = modifier
            )
        }
    )
}


/**
 * The interface for "things" that the user can specify as the sources of a target lock.
 */
@Stable
interface Locker: GraphSource {


    /**
     * The scan resolution with which the locker obtains a lock.
     */
    val scanResolution: Double


    /**
     * A virtual [Locker] that simply has the specified scan resolution.
     */
    class Virtual(
        override val color: Color,
        override val scanResolution: Double,
    ): Locker {

        override val name: String
            get() = "Virtual"

    }


}


/**
 * The graph of the lock time of the given list of lockers.
 */
@Composable
private fun LockTimeGraph(
    lockers: List<Locker>,
    modifier: Modifier
) {
    val sigRadiusRange = 15.0 .. 2000.0
    val maxDisplayedLockTime = lockers.maxOfOrNull {
        lockTime(scanResolution = it.scanResolution, signatureRadius = sigRadiusRange.start)
    } ?: 10_000.0
    BasicGraph(
        modifier = modifier,
        xRange = sigRadiusRange,
        yRange = 0.0 .. maxDisplayedLockTime,
        xValueFormatter = Double::asSignatureRadius,
        yValueFormatter = Double::millisAsTimeSec,
        lines = lockers.map { locker ->
            GraphLine(
                name = locker.name,
                function = { signatureRadius ->
                    lockTime(scanResolution = locker.scanResolution, signatureRadius = signatureRadius)
                },
                samplingStepPx = DefaultLineSamplingStep,
                lineStyleAtPoint = locker.lineStyle
            )
        }
    )
}


/**
 * The widget for adding, removing and editing a list of [Locker]s
 */
@Composable
private fun LockersEditor(
    lockers: MutableList<Locker>,
    virtualFitScanResolution: MutableState<Double>,
    modifier: Modifier
) {
    val nextLockerColor = nextGraphSourceColor(lockers)

    Row(
        modifier = modifier,
        horizontalArrangement = Arrangement.spacedBy(TheorycrafterTheme.spacing.xxlarge)
    ) {
        LockersList(
            lockers = lockers,
            nextLockerColor = nextLockerColor,
            modifier = Modifier
                .weight(1f, fill = false)
        )
        AddVirtualLocker(
            lockers = lockers,
            nextLockerColor = nextLockerColor,
            virtualFitScanResolution = virtualFitScanResolution,
            modifier = Modifier
                .width(200.dp)
        )
    }
}


/**
 * The list of [Locker]s, where the user can also add fits.
 */
@Composable
private fun LockersList(
    lockers: MutableList<Locker>,
    nextLockerColor: Color?,  // null if we're at max. lockers
    modifier: Modifier = Modifier
) {
    GraphSourceList(
        title = "Targeting Fits",
        sources = lockers,
        mutableSources = lockers,
        infoColumnWidths = listOf(100.dp),
        infoCells = { source, firstInfoColumnIndex -> LockerRowInfoCells(source as Locker, firstInfoColumnIndex) },
        nextColor = nextLockerColor,
        modifier = modifier
    )
}


/**
 * The cells of the info columns of the locker row, when not edited.
 */
@Composable
private fun GridScope.GridRowScope.LockerRowInfoCells(
    locker: Locker,
    firstColumnIndex: Int
) {
    cell(firstColumnIndex, contentAlignment = Alignment.CenterEnd) {
        SingleLineText(
            text = locker.scanResolution.asScanResolution(),
            modifier = Modifier.tooltip("Scan Resolution")
        )
    }
}


/**
 * The UI for adding a virtual locker.
 */
@Composable
private fun AddVirtualLocker(
    lockers: MutableList<Locker>,
    nextLockerColor: Color?,
    virtualFitScanResolution: MutableState<Double>,
    modifier: Modifier
) {
    Column(
        modifier = modifier
            .height(130.dp),  // This is needed due to https://github.com/JetBrains/compose-multiplatform/issues/4760
    ) {
        Text("Virtual Fit", style = TheorycrafterTheme.textStyles.mediumHeading)
        VSpacer(TheorycrafterTheme.spacing.small)

        var scanResolution: Double? by rememberSaveable { mutableStateOf(virtualFitScanResolution.value) }
        DoubleTextField(
            value = scanResolution,
            onValueChange = { scanResolution = it },
            formatter = maxPrecisionFormatter(precision = 1),
            constraint = { it > 0 },
            label = { Text("Scan Resolution") },
            visualTransformation = rememberAppendSuffixTransformation { "${NNBSP}mm" },
            modifier = Modifier
                .fillMaxWidth()
                .weight(1f)
        )

        VSpacer(TheorycrafterTheme.spacing.medium)

        RaisedButtonWithText(
            text = "Add Virtual Fit",
            enabled = (nextLockerColor != null) && (scanResolution != null),
            onClick = {
                val definiteScanResolution = scanResolution!!
                lockers.add(
                    Locker.Virtual(
                        color = nextLockerColor!!,
                        scanResolution = definiteScanResolution
                    )
                )
                virtualFitScanResolution.value = definiteScanResolution
            },
            modifier = Modifier
                .fillMaxWidth()
                .weight(1f)
                .then(
                    when {
                        nextLockerColor == null -> Modifier.tooltip("Max. targeting fits reached")
                        scanResolution == null -> Modifier.tooltip("Illegal scan resolution value")
                        else -> Modifier
                    }
                )
        )
    }
}