package com.cognaxon.Clean_the_Window

import android.Manifest
import android.content.pm.PackageManager
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.Color
import android.graphics.Matrix
import android.os.Bundle
import android.util.Log
import android.util.Size
import android.view.Surface
import android.view.WindowManager
import androidx.activity.ComponentActivity
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.activity.result.contract.ActivityResultContracts
import androidx.camera.core.CameraSelector
import androidx.camera.core.ImageAnalysis
import androidx.camera.core.ImageProxy
import androidx.camera.core.Preview
import androidx.camera.core.resolutionselector.ResolutionSelector
import androidx.camera.core.resolutionselector.ResolutionStrategy
import androidx.camera.lifecycle.ProcessCameraProvider
import androidx.camera.view.PreviewView
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material3.Button
import androidx.compose.material3.Slider
import androidx.compose.material3.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import androidx.lifecycle.compose.LocalLifecycleOwner
import androidx.core.content.ContextCompat
import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.WindowInsetsControllerCompat
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
import com.cognaxon.Clean_the_Window.ui.theme.CleanTheWindowTheme
import kotlinx.coroutines.delay
import java.util.concurrent.Executors

var cameraSensitivity by mutableStateOf(20)
var freezingSpeed by mutableStateOf(25)
var percentageNeededForWinning by mutableStateOf(0.95)
var percentageCleaned by mutableStateOf(0.0)
var iceBitmap by mutableStateOf<Bitmap?>(null)
var iceUpdateCounter by mutableStateOf(0)
var currentImageIndex by mutableStateOf(0)
var originalIcePixels = IntArray(640 * 480)
var diffPixels = IntArray(640 * 480)

var savedDifferenceImage = IntArray(640 * 480)

val backgroundFiles = listOf(
    "clean_window_001.png",
    "clean_window_002.png",
    "clean_window_003.png",
    "clean_window_004.png",
    "clean_window_005.png",
    "clean_window_006.png"
)

val iceFiles = listOf(
    "clean_window_001_ice.png",
    "clean_window_002_ice.png",
    "clean_window_003_ice.png",
    "clean_window_004_ice.png",
    "clean_window_005_ice.png",
    "clean_window_006_ice.png"
)

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        enableEdgeToEdge()
        window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
        
        hideSystemBars()

        setContent {
            CleanTheWindowTheme {
                CameraDifferenceScreen()
            }
        }
    }

    override fun onWindowFocusChanged(hasFocus: Boolean) {
        super.onWindowFocusChanged(hasFocus)
        if (hasFocus) {
            hideSystemBars()
        }
    }

    private fun hideSystemBars() {
        val windowInsetsController = WindowCompat.getInsetsController(window, window.decorView)
        windowInsetsController.systemBarsBehavior = WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
        windowInsetsController.hide(WindowInsetsCompat.Type.systemBars())
    }
}

@Composable
fun CameraDifferenceScreen(modifier: Modifier = Modifier) {
    val context = LocalContext.current
    var hasCameraPermission by remember {
        mutableStateOf(
            ContextCompat.checkSelfPermission(
                context,
                Manifest.permission.CAMERA
            ) == PackageManager.PERMISSION_GRANTED
        )
    }

    val launcher = rememberLauncherForActivityResult(
        contract = ActivityResultContracts.RequestPermission(),
        onResult = { granted ->
            hasCameraPermission = granted
        }
    )

    LaunchedEffect(key1 = true) {
        if (!hasCameraPermission) {
            launcher.launch(Manifest.permission.CAMERA)
        }
    }

    if (hasCameraPermission) {
        MainContent(modifier)
    } else {
        Box(modifier = modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
            Text(text = "Camera permission required")
        }
    }
}

@Composable
fun MainContent(modifier: Modifier = Modifier) {
    val context = LocalContext.current
    var isOriginalMode by remember { mutableStateOf(true) }
    var isBackCamera by remember { mutableStateOf(false) }
    var isButtonVisible by remember { mutableStateOf(false) }
    var lastInteractionTime by remember { mutableStateOf(0L) }
    var showAboutText by remember { mutableStateOf(false) }
    var showSettings by remember { mutableStateOf(false) }

    var tempSensitivity by remember { mutableStateOf((255 - cameraSensitivity).toFloat()) }
    var tempFreezingSpeed by remember { mutableStateOf(freezingSpeed.toFloat()) }

    LaunchedEffect(currentImageIndex) {
        try {
            context.assets.open(iceFiles[currentImageIndex % iceFiles.size]).use { inputStream ->
                val original = BitmapFactory.decodeStream(inputStream)
                val scaled = Bitmap.createScaledBitmap(original, 640, 480, true).copy(Bitmap.Config.ARGB_8888, true)
                scaled.getPixels(originalIcePixels, 0, 640, 0, 0, 640, 480)
                // Reset cleaning progress when image changes
                savedDifferenceImage.fill(0)
                iceBitmap = scaled
                iceUpdateCounter++
            }
        } catch (e: Exception) {
            Log.e("MainContent", "Failed to load ice image", e)
        }
    }

    val backgroundImage = remember(currentImageIndex) {
        try {
            context.assets.open(backgroundFiles[currentImageIndex % backgroundFiles.size]).use { inputStream ->
                BitmapFactory.decodeStream(inputStream)?.asImageBitmap()
            }
        } catch (e: Exception) {
            null
        }
    }

    val iceImage = remember(iceBitmap, iceUpdateCounter) {
        iceBitmap?.asImageBitmap()
    }

    val congratsImage = remember {
        try {
            context.assets.open("clean_window_congratulations_001.png").use { inputStream ->
                BitmapFactory.decodeStream(inputStream)?.asImageBitmap()
            }
        } catch (e: Exception) {
            null
        }
    }

    val aboutText = "Clean the Window! v.2.0\n\nGame for kids. Interact with your device by simple hand waving in the air before the camera. The goal of the game is to clean the ice from the window before the window freezes back.\nThe game is perfect for kids, who still don't have skills how to use keyboard or the mouse. Improves physical coordination and provides a lot of fun.\nThe program requires video camera.\n\nMade by\nhttps://cognaxon.com"

    LaunchedEffect(isButtonVisible, lastInteractionTime) {
        if (isButtonVisible) {
            delay(3000)
            isButtonVisible = false
        }
    }

    LaunchedEffect(showAboutText) {
        if (showAboutText) {
            delay(10000)
            showAboutText = false
        }
    }

    Box(
        modifier = modifier
            .fillMaxSize()
            .pointerInput(Unit) {
                detectTapGestures(
                    onTap = {
                        isButtonVisible = true
                        lastInteractionTime = System.currentTimeMillis()
                    }
                )
            }
    ) {
        CameraView(isOriginalMode = isOriginalMode, isBackCamera = isBackCamera)

        if (isOriginalMode) {
            backgroundImage?.let {
                Image(
                    bitmap = it,
                    contentDescription = null,
                    modifier = Modifier.fillMaxSize(),
                    contentScale = ContentScale.Fit
                )
            }

            iceImage?.let {
                Image(
                    bitmap = it,
                    contentDescription = null,
                    modifier = Modifier.fillMaxSize(),
                    contentScale = ContentScale.Fit
                )
            }
        }

        if (percentageCleaned > percentageNeededForWinning) {
            congratsImage?.let {
                Image(
                    bitmap = it,
                    contentDescription = null,
                    modifier = Modifier.fillMaxSize(),
                    contentScale = ContentScale.Fit
                )
            }
        }

        // Settings Button (0 degrees -> Right)
        AnimatedVisibility(
            visible = isButtonVisible,
            enter = fadeIn(),
            exit = fadeOut(),
            modifier = Modifier
                .align(Alignment.Center)
                .offset(x = 150.dp, y = 0.dp)
        ) {
            Image(
                painter = painterResource(id = R.drawable.ic_settings),
                contentDescription = "Settings",
                modifier = Modifier
                    .size(80.dp)
                    .clickable {
                        lastInteractionTime = System.currentTimeMillis()
                        tempSensitivity = (255 - cameraSensitivity).toFloat()
                        tempFreezingSpeed = freezingSpeed.toFloat()
                        showSettings = true
                        isButtonVisible = false
                    }
            )
        }

        // Mode Toggle Button (60 degrees -> Top Right)
        AnimatedVisibility(
            visible = isButtonVisible,
            enter = fadeIn(),
            exit = fadeOut(),
            modifier = Modifier
                .align(Alignment.Center)
                .offset(x = 75.dp, y = (-130).dp)
        ) {
            val iconRes = if (isOriginalMode) R.drawable.ic_eye_snowflake else R.drawable.ic_eye_human
            Image(
                painter = painterResource(id = iconRes),
                contentDescription = "Toggle Mode",
                modifier = Modifier
                    .size(80.dp)
                    .clickable {
                        lastInteractionTime = System.currentTimeMillis()
                        isOriginalMode = !isOriginalMode
                    }
            )
        }

        // Camera Toggle Button (120 degrees -> Top Left)
        AnimatedVisibility(
            visible = isButtonVisible,
            enter = fadeIn(),
            exit = fadeOut(),
            modifier = Modifier
                .align(Alignment.Center)
                .offset(x = (-75).dp, y = (-130).dp)
        ) {
            val cameraIconRes = if (isBackCamera) R.drawable.ic_camera_front else R.drawable.ic_camera_back
            Image(
                painter = painterResource(id = cameraIconRes),
                contentDescription = "Toggle Camera",
                modifier = Modifier
                    .size(80.dp)
                    .clickable {
                        lastInteractionTime = System.currentTimeMillis()
                        isBackCamera = !isBackCamera
                    }
            )
        }

        // About Button (180 degrees -> Left)
        AnimatedVisibility(
            visible = isButtonVisible,
            enter = fadeIn(),
            exit = fadeOut(),
            modifier = Modifier
                .align(Alignment.Center)
                .offset(x = (-150).dp, y = 0.dp)
        ) {
            Image(
                painter = painterResource(id = R.drawable.ic_about),
                contentDescription = "About",
                modifier = Modifier
                    .size(80.dp)
                    .clickable {
                        lastInteractionTime = System.currentTimeMillis()
                        showAboutText = true
                        isButtonVisible = false
                    }
            )
        }

        // Previous Photo Button (240 degrees -> Bottom Left)
        AnimatedVisibility(
            visible = isButtonVisible,
            enter = fadeIn(),
            exit = fadeOut(),
            modifier = Modifier
                .align(Alignment.Center)
                .offset(x = (-75).dp, y = 130.dp)
        ) {
            Image(
                painter = painterResource(id = R.drawable.ic_previous_photo),
                contentDescription = "Previous Photo",
                modifier = Modifier
                    .size(80.dp)
                    .clickable {
                        lastInteractionTime = System.currentTimeMillis()
                        currentImageIndex = (currentImageIndex - 1 + backgroundFiles.size) % backgroundFiles.size
                    }
            )
        }

        // Next Photo Button (300 degrees -> Bottom Right)
        AnimatedVisibility(
            visible = isButtonVisible,
            enter = fadeIn(),
            exit = fadeOut(),
            modifier = Modifier
                .align(Alignment.Center)
                .offset(x = 75.dp, y = 130.dp)
        ) {
            Image(
                painter = painterResource(id = R.drawable.ic_next_photo),
                contentDescription = "Next Photo",
                modifier = Modifier
                    .size(80.dp)
                    .clickable {
                        lastInteractionTime = System.currentTimeMillis()
                        currentImageIndex = (currentImageIndex + 1) % backgroundFiles.size
                    }
            )
        }

        // About Text Overlay
        AnimatedVisibility(
            visible = showAboutText,
            enter = fadeIn(),
            exit = fadeOut(),
            modifier = Modifier.fillMaxSize()
        ) {
            Box(
                modifier = Modifier
                    .fillMaxSize()
                    .background(androidx.compose.ui.graphics.Color.Black.copy(alpha = 0.8f))
                    .clickable { showAboutText = false },
                contentAlignment = Alignment.Center
            ) {
                Text(
                    text = aboutText,
                    color = androidx.compose.ui.graphics.Color.White,
                    textAlign = TextAlign.Center,
                    modifier = Modifier.padding(32.dp)
                )
            }
        }

        // Settings Overlay
        AnimatedVisibility(
            visible = showSettings,
            enter = fadeIn(),
            exit = fadeOut(),
            modifier = Modifier.fillMaxSize()
        ) {
            Box(
                modifier = Modifier
                    .fillMaxSize()
                    .background(androidx.compose.ui.graphics.Color.Black.copy(alpha = 0.9f))
                    .clickable(enabled = false) { },
                contentAlignment = Alignment.Center
            ) {
                Column(
                    modifier = Modifier
                        .fillMaxWidth(0.6f)
                        .background(androidx.compose.ui.graphics.Color.DarkGray, androidx.compose.foundation.shape.RoundedCornerShape(16.dp))
                        .padding(24.dp),
                    horizontalAlignment = Alignment.CenterHorizontally
                ) {
                    Text("Camera Sensitivity", color = androidx.compose.ui.graphics.Color.White)
                    Slider(
                        value = tempSensitivity,
                        onValueChange = { tempSensitivity = it },
                        valueRange = 0f..255f
                    )
                    
                    Spacer(modifier = Modifier.height(16.dp))
                    
                    Text("Freezing Speed", color = androidx.compose.ui.graphics.Color.White)
                    Slider(
                        value = tempFreezingSpeed,
                        onValueChange = { tempFreezingSpeed = it },
                        valueRange = 0f..255f
                    )
                    
                    Spacer(modifier = Modifier.height(24.dp))
                    
                    Button(onClick = {
                        cameraSensitivity = (255 - tempSensitivity).toInt()
                        freezingSpeed = tempFreezingSpeed.toInt()
                        showSettings = false
                    }) {
                        Text("Ok")
                    }
                }
            }
        }
    }
}

@Composable
fun CameraView(isOriginalMode: Boolean, isBackCamera: Boolean) {
    val context = LocalContext.current
    val lifecycleOwner = LocalLifecycleOwner.current
    val cameraExecutor = remember { Executors.newSingleThreadExecutor() }
    var diffBitmap by remember { mutableStateOf<Bitmap?>(null) }
    
    val previewView = remember { 
        PreviewView(context).apply {
            scaleType = PreviewView.ScaleType.FIT_CENTER
        }
    }
    val display = remember { 
        (context.getSystemService(android.content.Context.WINDOW_SERVICE) as WindowManager).defaultDisplay 
    }

    // Keep track of use cases to update rotation
    val previewState = remember { mutableStateOf<Preview?>(null) }
    val imageAnalysisState = remember { mutableStateOf<ImageAnalysis?>(null) }

    DisposableEffect(Unit) {
        onDispose {
            cameraExecutor.shutdown()
        }
    }

    // Update target rotation when orientation changes
    val configuration = androidx.compose.ui.platform.LocalConfiguration.current
    LaunchedEffect(configuration.orientation) {
        val rotation = display.rotation
        previewState.value?.targetRotation = rotation
        imageAnalysisState.value?.targetRotation = rotation
    }

    LaunchedEffect(isBackCamera) {
        val cameraProviderFuture = ProcessCameraProvider.getInstance(context)
        cameraProviderFuture.addListener({
            val cameraProvider = cameraProviderFuture.get()
            
            val rotation = display.rotation

            val resolutionSelector = ResolutionSelector.Builder()
                .setResolutionStrategy(
                    ResolutionStrategy(
                        Size(640, 480),
                        ResolutionStrategy.FALLBACK_RULE_CLOSEST_HIGHER_THEN_LOWER
                    )
                )
                .build()

            val preview = Preview.Builder()
                .setTargetRotation(rotation)
                .setResolutionSelector(resolutionSelector)
                .build()
            preview.setSurfaceProvider(previewView.surfaceProvider)
            previewState.value = preview

            val imageAnalysis = ImageAnalysis.Builder()
                .setResolutionSelector(resolutionSelector)
                .setTargetRotation(rotation)
                .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
                .build()
            imageAnalysisState.value = imageAnalysis

            val analyzer = DifferenceAnalyzer(isFrontCamera = !isBackCamera) { newBitmap ->
                ContextCompat.getMainExecutor(context).execute {
                    diffBitmap = newBitmap
                    iceUpdateCounter++
                }
            }

            imageAnalysis.setAnalyzer(cameraExecutor, analyzer)

            val cameraSelector = if (isBackCamera) CameraSelector.DEFAULT_BACK_CAMERA else CameraSelector.DEFAULT_FRONT_CAMERA

            try {
                cameraProvider.unbindAll()
                cameraProvider.bindToLifecycle(
                    lifecycleOwner,
                    cameraSelector,
                    preview,
                    imageAnalysis
                )
            } catch (e: Exception) {
                Log.e("CameraView", "Use case binding failed", e)
            }
        }, ContextCompat.getMainExecutor(context))
    }

    Box(
        modifier = Modifier.fillMaxSize(),
        contentAlignment = Alignment.Center
    ) {
        AndroidView(
            factory = { previewView },
            modifier = Modifier.fillMaxSize()
        )
    }
}

class DifferenceAnalyzer(
    private val isFrontCamera: Boolean,
    private val onDifferenceCalculated: (Bitmap) -> Unit
) : ImageAnalysis.Analyzer {
    private var lastFrame: ByteArray? = null

    override fun analyze(image: ImageProxy) {
        val plane = image.planes[0]
        val buffer = plane.buffer
        val width = image.width
        val height = image.height
        val rowStride = plane.rowStride
        val pixelStride = plane.pixelStride
        val rotationDegrees = image.imageInfo.rotationDegrees

        val data = ByteArray(width * height)
        for (row in 0 until height) {
            for (col in 0 until width) {
                data[row * width + col] = buffer.get(row * rowStride + col * pixelStride)
            }
        }

        if (lastFrame != null && lastFrame!!.size == data.size) {
            val currentIceBitmap = iceBitmap
            val icePixels = if (currentIceBitmap != null) IntArray(width * height) else null


            var transparentCount = 0
            for (i in data.indices) {
                val current = data[i].toInt() and 0xFF
                val previous = lastFrame!![i].toInt() and 0xFF
                val diff = Math.abs(current - previous)

                // Accumulate cleaning progress in savedDifferenceImage
                // Freeze back: decrease intensity
                if (savedDifferenceImage[i] > freezingSpeed) {
                    savedDifferenceImage[i] -= freezingSpeed
                } else {
                    savedDifferenceImage[i] = 0
                }

                // Clean: increase intensity based on motion
                if (diff > cameraSensitivity) {
                    savedDifferenceImage[i] = Math.min(255, savedDifferenceImage[i] + diff)
                }

                val alpha = if (savedDifferenceImage[i] > cameraSensitivity) 0 else 255
                if (alpha == 0) transparentCount++

                if (icePixels != null) {
                    // If front camera, mirror the horizontal coordinate
                    val targetIndex = if (isFrontCamera) {
                        val row = i / width
                        val col = i % width
                        row * width + (width - 1 - col)
                    } else i
                    
                    if (alpha == 0)
                    {icePixels[targetIndex] = android.graphics.Color.TRANSPARENT}
                    else
                    {icePixels[targetIndex] = originalIcePixels[targetIndex]}


                }

                diffPixels[i] = 0xFF000000.toInt() or (savedDifferenceImage[i] shl 16) or (savedDifferenceImage[i] shl 8) or savedDifferenceImage[i]
            }
            
            percentageCleaned = transparentCount.toDouble() / (width * height)

            if (currentIceBitmap != null && icePixels != null) {
                // Draw Progress Bar
                val barMaxWidth = (width * 0.9).toInt()
                val barWidth = (barMaxWidth * percentageCleaned).toInt()
                val barHeight = 20
                val barXStart = (width - barMaxWidth) / 2
                val barYStart = height - 60
                val barColor = if (percentageCleaned > 0.5) 0xff0d0a57.toInt() else 0xff6a0817.toInt()

                for (y in barYStart until (barYStart + barHeight)) {
                    if (y >= height) break
                    for (x in barXStart until (barXStart + barWidth)) {
                        if (x >= width) break
                        icePixels[y * width + x] = barColor
                    }
                }

                currentIceBitmap.setPixels(icePixels, 0, width, 0, 0, width, height)
            }
            
            var bitmap = Bitmap.createBitmap(diffPixels, width, height, Bitmap.Config.ARGB_8888)
            
            if (isFrontCamera) {
                val matrix = Matrix()
                // Mirror horizontally for front camera to match standard selfie preview
                matrix.postScale(-1f, 1f)
                bitmap = Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, true)
            }

            onDifferenceCalculated(bitmap)
        }

        lastFrame = data
        image.close()
    }
}
