/*
 * LightMeter - Simple Android App to calculate exposure for photographers
 * Copyright (C) 2024  Jona Heitzer
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

package eu.wa5p.lightmeter

import android.content.Context
import android.hardware.Sensor
import android.hardware.SensorEvent
import android.hardware.SensorEventListener
import android.hardware.SensorManager
import android.os.Bundle
import android.util.Log
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Slider
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import eu.wa5p.lightmeter.ui.theme.LightMeterTheme
import kotlin.math.abs

const val TAG: String = "lightmeter MainActivity"

private val commonIsoValues = listOf(50, 100, 200, 400, 800, 1600, 3200, 6400)
private val commonApertureValues = listOf(0.85, 1.4, 2.0, 2.8, 4.0, 5.6, 8.0, 11.0, 16.0, 22.0)

data class ShutterSpeed(val value: Double, val label: String)
val shutterSpeeds = listOf(
    ShutterSpeed(1.0 / 8000, "1/8000 s"),
    ShutterSpeed(1.0 / 4000, "1/4000 s"),
    ShutterSpeed(1.0 / 2000, "1/2000 s"),
    ShutterSpeed(1.0 / 1000, "1/1000 s"),
    ShutterSpeed(1.0 / 500, "1/500 s"),
    ShutterSpeed(1.0 / 250, "1/250 s"),
    ShutterSpeed(1.0 / 125, "1/125 s"),
    ShutterSpeed(1.0 / 60, "1/60 s"),
    ShutterSpeed(1.0 / 30, "1/30 s"),
    ShutterSpeed(1.0 / 15, "1/15 s"),
    ShutterSpeed(1.0 / 8, "1/8 s"),
    ShutterSpeed(1.0 / 4, "1/4 s"),
    ShutterSpeed(1.0 / 2, "1/2 s"),
    ShutterSpeed(1.0, "1 s"),
    ShutterSpeed(2.0, "2 s"),
    ShutterSpeed(4.0, "4 s"),
    ShutterSpeed(8.0, "8 s"),
    ShutterSpeed(15.0, "15 s"),
    ShutterSpeed(30.0, "30 s"),
    ShutterSpeed(60.0, "60 s")
)

class MainActivity : ComponentActivity(), SensorEventListener {
    private lateinit var sensorManager: SensorManager
    private var illuminanceSensor: Sensor? = null
    private var lightmeterState = mutableStateOf(
        LightmeterState(
            illuminance = 0.0,
            aperture = 2.8,
            iso = 100,
            shutterSpeed = 0.01
        )
    )

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        enableEdgeToEdge()
        setContent {
            LightMeterTheme {
                LightmeterHomeScreen(
                    state = lightmeterState,
                    recalculateShutterSpeed = { recalculateShutterSpeed() }
                )
            }
        }

        sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
        illuminanceSensor = sensorManager.getDefaultSensor(Sensor.TYPE_LIGHT)
    }

    private fun recalculateShutterSpeed() {
        val illuminance = lightmeterState.value.illuminance
        val iso = lightmeterState.value.iso
        val aperture = lightmeterState.value.aperture
        val shutterSpeed = (aperture * aperture) / (illuminance * (iso / 100))
        lightmeterState.value = lightmeterState.value.copy(
            iso = iso,
            aperture = aperture,
            shutterSpeed = shutterSpeed
        )
    }

    private fun updateLightmeterState(illuminance: Double) {
        lightmeterState.value = lightmeterState.value.copy(
            illuminance = illuminance
        )
        recalculateShutterSpeed()
    }

    override fun onSensorChanged(event: SensorEvent?) {
        event?.values?.let {
            for (v in it) {
                Log.d(TAG, "sensor changed: $v lux")
                updateLightmeterState(v.toDouble())
            }
        }
    }

    override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {
        Log.d(TAG, "accuracy changed: sensor: ${sensor?.name}. accuracy: $accuracy")
    }

    override fun onResume() {
        super.onResume()
        sensorManager.registerListener(this, illuminanceSensor, SensorManager.SENSOR_DELAY_NORMAL)
    }

    override fun onPause() {
        super.onPause()
        sensorManager.unregisterListener(this)
    }

    override fun onDestroy() {
        super.onDestroy()
        sensorManager.unregisterListener(this)
    }
}

@Composable
fun LightmeterListItem(
    label: String,
    value: String,
    unit: String
) {
    Row(modifier = Modifier.padding(bottom = 8.dp)) {
        Text(
            modifier = Modifier
                .weight(0.4f)
                .padding(all = 8.dp),
            text = label
        )
        Text(
            modifier = Modifier
                .weight(0.4f)
                .padding(all = 8.dp),
            textAlign = TextAlign.Right,
            text = value
        )
        Text(
            modifier = Modifier
                .weight(0.2f)
                .padding(all = 8.dp),
            text = unit
        )
    }
}

@Preview(showBackground = true)
@Composable
fun LightmeterListItemPreview() {
    LightMeterTheme {
        LightmeterListItem(
            label = "Illuminance",
            value = "1234",
            unit = "lux"
        )
    }
}

private fun formatShutterSpeed(shutterSpeed: Double): String {
    val closestShutterSpeed = shutterSpeeds.minByOrNull {
        abs(it.value - shutterSpeed)
    }

    return closestShutterSpeed?.label ?: "Unknown"
}

@Composable
fun ShutterSpeed(state: MutableState<LightmeterState>) {
    Row(modifier = Modifier.padding(bottom = 8.dp)) {
        Text(
            modifier = Modifier
                .weight(0.5f)
                .padding(all = 8.dp),
            textAlign = TextAlign.Center,
            fontWeight = FontWeight.Bold,
            fontSize = 24.sp,
            text = formatShutterSpeed(state.value.shutterSpeed)
        )
    }
}

@Preview(showBackground = true)
@Composable
fun PreviewShutterSpeed() {
    LightMeterTheme {
        ShutterSpeed(state = previewlightmeterState)
    }
}

@Composable
fun IsoSlider(state: MutableState<LightmeterState>, recalculateShutterSpeed: () -> Unit) {
    // Convert common ISO values to the range used by the slider
    val minIso = commonIsoValues.first().toFloat()
    val maxIso = commonIsoValues.last().toFloat()

    // Find the closest ISO value to the slider's value
    val isoValue = state.value.iso.toFloat()
    val closestIso = commonIsoValues.minByOrNull { abs(it - isoValue) } ?: minIso

    Row(
        modifier = Modifier.padding(all = 8.dp),
        verticalAlignment = Alignment.CenterVertically
    ) {
        Text(
            modifier = Modifier.weight(0.3f),
            text = "ISO"
        )
        Slider(
            modifier = Modifier.weight(0.7f),
            value = closestIso.toFloat(),
            onValueChange = { value ->
                val newIso = commonIsoValues.minByOrNull { abs(it - value) } ?: minIso
                state.value = state.value.copy(iso = newIso.toInt())
                recalculateShutterSpeed()
            },
            valueRange = minIso..maxIso,
            steps = (commonIsoValues.size - 1) * 10
        )
    }
}
val previewlightmeterState = mutableStateOf(
    LightmeterState(
        illuminance = 0.0,
        aperture = 1.8,
        iso = 100,
        shutterSpeed = 0.01
    )
)

fun previewRecalculateShutterSpeed() {}

@Preview(showBackground = true)
@Composable
fun IsoSliderPreview() {
    LightMeterTheme {
        IsoSlider(
            state = remember { previewlightmeterState },
            recalculateShutterSpeed = { previewRecalculateShutterSpeed() }
        )
    }
}

@Composable
fun ApertureSlider(
    state: MutableState<LightmeterState>,
    recalculateShutterSpeed: () -> Unit
) {
    // Convert common aperture values to the range used by the slider
    val minAperture = commonApertureValues.first().toFloat()
    val maxAperture = commonApertureValues.last().toFloat()

    // Find the closest aperture value to the slider's value
    val apertureValue = state.value.aperture.toFloat()
    val closestAperture = commonApertureValues.minByOrNull { abs(it - apertureValue) } ?: minAperture

    Row(
        modifier = Modifier.padding(all = 8.dp),
        verticalAlignment = Alignment.CenterVertically
    ) {
        Text(
            modifier = Modifier.weight(0.3f),
            text = "Aperture"
        )
        Slider(
            modifier = Modifier.weight(0.7f),
            value = closestAperture.toFloat(),
            onValueChange = { value ->
                val newAperture = commonApertureValues.minByOrNull { abs(it - value) } ?: minAperture
                state.value = state.value.copy(aperture = newAperture.toDouble())
                recalculateShutterSpeed()
            },
            valueRange = minAperture..maxAperture,
            steps = (commonApertureValues.size - 1) * 10
        )
    }
}
@Preview(showBackground = true)
@Composable
fun ApertureSliderPreview() {
    LightMeterTheme {
        ApertureSlider(
            state = remember { previewlightmeterState },
            recalculateShutterSpeed = { previewRecalculateShutterSpeed() }
        )
    }
}

@Composable
fun LightmeterHomeScreen(
    state: MutableState<LightmeterState>,
    recalculateShutterSpeed: () -> Unit
) {
    Column(modifier = Modifier
        .fillMaxSize()
        .padding(top = 64.dp)
        .padding(all = 32.dp)
    ) {
        Column(
            verticalArrangement = Arrangement.Top
        ) {
            LightmeterListItem(
                label = "Illuminance",
                value = "%.2f".format(state.value.illuminance),
                unit = "lux"
            )
            LightmeterListItem(
                label = "ISO",
                value = state.value.iso.toString(),
                unit = ""
            )
            LightmeterListItem(
                label = "Aperture",
                value = "%.2f".format(state.value.aperture),
                unit = "f"
            )
            LightmeterListItem(
                label = "Shutter Speed",
                value = "%.2f".format(state.value.shutterSpeed),
                unit = "s"
            )
        }
        
        Column (
            modifier = Modifier.padding(top = 64.dp),
            verticalArrangement = Arrangement.Center
        ) {
            ShutterSpeed(state = state)
        }

        Column(
            modifier = Modifier.padding(top = 64.dp),
            verticalArrangement = Arrangement.Bottom
        ) {
            IsoSlider(
                state = state,
                recalculateShutterSpeed = recalculateShutterSpeed
            )
            ApertureSlider(
                state = state,
                recalculateShutterSpeed = recalculateShutterSpeed
            )
        }
    }
}

@Preview(showBackground = true)
@Composable
fun LightmeterHomeScreenPreview() {
    LightMeterTheme {
        LightmeterHomeScreen(
            state = remember { previewlightmeterState },
            recalculateShutterSpeed = { previewRecalculateShutterSpeed() }
        )
    }
}