package com.example.statecodelab.todo

import androidx.compose.animation.animate
import androidx.compose.animation.animateContentSize
import androidx.compose.animation.animatedFloat
import androidx.compose.animation.core.TweenSpec
import androidx.compose.foundation.Box
import androidx.compose.foundation.Icon
import androidx.compose.foundation.Text
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.contentColor
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.ExperimentalLayout
import androidx.compose.foundation.layout.InnerPadding
import androidx.compose.foundation.layout.IntrinsicSize
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.RowScope.gravity
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.preferredHeight
import androidx.compose.foundation.layout.preferredWidth
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.lazy.LazyColumnFor
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.Button
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Surface
import androidx.compose.material.TextButton
import androidx.compose.material.TextField
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.AlarmOn
import androidx.compose.material.icons.filled.CropSquare
import androidx.compose.material.icons.filled.Done
import androidx.compose.material.icons.filled.DoneAll
import androidx.compose.material.icons.filled.Event
import androidx.compose.material.icons.filled.GroupWork
import androidx.compose.material.icons.filled.PrivacyTip
import androidx.compose.material.icons.filled.RestoreFromTrash
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.onCommit
import androidx.compose.runtime.remember
import androidx.compose.runtime.savedinstancestate.savedInstanceState
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.drawOpacity
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.RectangleShape
import androidx.compose.ui.graphics.vector.VectorAsset
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.ui.tooling.preview.Preview
import com.example.statecodelab.util.generateRandomTodoItem
import kotlin.random.Random

//@Composable
//fun TodoScreenLiveData(
//    items: LiveData<List<TodoItem>>,
//    addItem: (TodoItem) -> Unit,
//    removeItem: (TodoItem) -> Unit
//) {
//    val itemsList by items.observeAsState(listOf())
//    TodoScreen(itemsList, addItem, removeItem)
//}

@Composable
fun TodoScreen(
    items: List<TodoItem>,
    currentlyEditing: TodoItem?,
    onAddItem: (TodoItem) -> Unit,
    onRemoveItem: (TodoItem) -> Unit,
    onStartEdit: (TodoItem) -> Unit,
    onEditItemChange: (TodoItem) -> Unit,
    onEditDone: () -> Unit
) {
    Column {
        val enableTopSection = currentlyEditing == null
        TodoItemsInputContainer(elevate = enableTopSection) {
            if (enableTopSection) {
                TodoItemCreationEditor(onAddItem)
            } else {
                Text(
                    "Editing item",
                    style = MaterialTheme.typography.h6,
                    textAlign = TextAlign.Center,
                    modifier = Modifier
                        .gravity(Alignment.CenterVertically)
                        .padding(16.dp)
                        .fillMaxWidth()
                )
            }
        }
        LazyColumnFor(
            items = items,
            modifier = Modifier.weight(1f),
            contentPadding = InnerPadding(top = 8.dp)
        ) { todo ->
            Box(
                // TODO: This causes LazyColumnFor to crash so disabling for now
                Modifier.animateContentSize()
            ) {
                if (currentlyEditing?.id == todo.id) {
                    TodoItemInlineEditor(
                        item = currentlyEditing,
                        onEditItemChange = onEditItemChange,
                        onEditDone = onEditDone,
                        onRemoveItem = { onRemoveItem(todo) }
                    )
                } else {
                    TodoRow(
                        todo,
                        { onStartEdit(it) },
                        Modifier.fillParentMaxWidth()
                    )
                }
            }
        }
        // for now use buttons to add random items
        Button(
            onClick = { onAddItem(generateRandomTodoItem()) },
            modifier = Modifier.fillMaxWidth(),
        ) {
            Text("Add random item")
        }
    }
}

@Composable
fun TodoEditButton(onClick: () -> Unit, text: String, enabled: Boolean = true) {
    Button(onClick = onClick, shape = CircleShape, enabled = enabled, elevation = 0.dp) {
        Text(text)
    }
}

@Composable
fun TodoItemsInputContainer(elevate: Boolean, modifier: Modifier = Modifier, content: @Composable ColumnScope.() -> Unit) {
    val animatedElevation = animate(if (elevate) 1.dp else 0.dp, TweenSpec(500))
    Surface(
        color = MaterialTheme.colors.onSurface.copy(alpha = 0.05f),
        elevation = animatedElevation,
        shape = RectangleShape,
    ) {
        Column(
            modifier.animateContentSize(animSpec = TweenSpec(300)),
            children = content
        )
    }
}

@OptIn(ExperimentalLayout::class)
@Composable
fun TodoItemInlineEditor(
    item: TodoItem,
    onEditItemChange: (TodoItem) -> Unit,
    onEditDone: () -> Unit,
    onRemoveItem: () -> Unit
) {

    TodoItemInput(
        text = item.task,
        onTextChange = { onEditItemChange(item.copy(task = it)) },
        icon = item.icon,
        onIconChange = { onEditItemChange(item.copy(icon = it)) },
        primaryAction = onEditDone,
        buttonSlot = {
            Row {
                val shrinkButtons = Modifier.widthIn(20.dp)
                TextButton(onClick = onEditDone, shrinkButtons) {
                    Text(
                        "\uD83D\uDCBE", // floppy disk
                        textAlign = TextAlign.End,
                        modifier = Modifier.width(30.dp)
                    )
                }
                TextButton(onClick = onRemoveItem, shrinkButtons) {
                    Text(
                        "❌",
                        textAlign = TextAlign.End,
                        modifier = Modifier.width(30.dp)
                    )
                }
            }
        },
        iconsVisible = true
    )
}

@OptIn(ExperimentalLayout::class)
@Composable
fun TodoItemCreationEditor(onItemComplete: (TodoItem) -> Unit, buttonText: String = "Add") {
    val (text, onTextChange) = savedInstanceState { "" }
    val (icon, onIconChange) = remember { mutableStateOf(DefaultIconSelection) }

    val submit = {
        if (text != "") {
            onItemComplete(TodoItem(text, icon))
            onTextChange("")
            onIconChange(DefaultIconSelection)
        }
    }

    val showFullEditor = text != ""
    TodoItemInput(
        text = text,
        onTextChange = onTextChange,
        icon = icon,
        onIconChange = onIconChange,
        primaryAction = submit,
        iconsVisible = showFullEditor
    ) {
        TodoEditButton(onClick = submit, text = buttonText, enabled = text != "",)
    }
}

@Composable
fun TodoItemInput(
    text: String,
    onTextChange: (String) -> Unit,
    icon: VectorAsset,
    onIconChange: (VectorAsset) -> Unit,
    primaryAction: () -> Unit,
    iconsVisible: Boolean,
    modifier: Modifier = Modifier,
    buttonSlot: @Composable() () -> Unit,
) {
    Column {
        InputTextAndButton(
            text = text,
            onTextChange = onTextChange,
            primaryAction = primaryAction,
            modifier = modifier.padding(horizontal = 16.dp).padding(top = 16.dp),
            buttonSlot = buttonSlot
        )
        if (iconsVisible) {
            AnimatedIconRow(icon, onIconChange, Modifier.padding(top = 8.dp))
        } else {
            Spacer(modifier = Modifier.preferredHeight(16.dp))
        }
    }
}

@OptIn(ExperimentalLayout::class)
@Composable
private fun InputTextAndButton(
    text: String,
    onTextChange: (String) -> Unit,
    primaryAction: () -> Unit,
    modifier: Modifier = Modifier,
    buttonSlot: @Composable() () -> Unit
) {
    Row(modifier.preferredHeight(IntrinsicSize.Min)) {
        TextField(
            value = text,
            onValueChange = { onTextChange(it) },
            label = { /* no label */ },
            backgroundColor = Color.Transparent,
            imeAction = ImeAction.Done,
            onImeActionPerformed = { action, softKeyboardController ->
                if (action == ImeAction.Done) {
                    primaryAction()
                    softKeyboardController?.hideSoftwareKeyboard()
                }
            },
            modifier = Modifier
                .weight(1f)
                .padding(end = 8.dp)
        )
        Box(Modifier.gravity(Alignment.CenterVertically), children = buttonSlot)
    }
}

@Composable
private fun AnimatedIconRow(
    icon: VectorAsset,
    onIconChange: (VectorAsset) -> Unit,
    modifier: Modifier = Modifier
) {
    val alpha = animatedFloat(0f)
    onCommit(Unit) {
        alpha.animateTo(1f, TweenSpec(300, 150))
    }
    IconRow(icon, onIconChange, modifier.drawOpacity(alpha.value))
}

private val DefaultIconSelection = Icons.Default.CropSquare

@Composable
private fun IconRow(
    icon: VectorAsset,
    onIconChange: (VectorAsset) -> Unit,
    modifier: Modifier = Modifier
) {
    Row(modifier) {
        SelectableIconButton(
            Icons.Default.CropSquare,
            onIconChange,
            icon == Icons.Default.CropSquare,
            Modifier.weight(1f)
        )
        SelectableIconButton(
            Icons.Default.Done,
            onIconChange,
            icon == Icons.Default.Done,
            Modifier.weight(1f)
        )
        SelectableIconButton(
            Icons.Default.Event,
            onIconChange,
            icon == Icons.Default.Event,
            Modifier.weight(1f)
        )
        SelectableIconButton(
            Icons.Default.PrivacyTip,
            onIconChange,
            icon == Icons.Default.PrivacyTip,
            Modifier.weight(1f)
        )
        SelectableIconButton(
            Icons.Default.RestoreFromTrash,
            onIconChange,
            icon == Icons.Default.RestoreFromTrash,
            Modifier.weight(1f)
        )
    }
}

@OptIn(ExperimentalLayout::class)
@Composable
fun SelectableIconButton(
    icon: VectorAsset,
    onIconSelected: (VectorAsset) -> Unit,
    isSelected: Boolean,
    modifier: Modifier = Modifier
) {
    val tint = if (isSelected) {
        MaterialTheme.colors.primary
    } else {
        MaterialTheme.colors.onSurface.copy(alpha = 0.6f)
    }
    Button(
        onClick = { onIconSelected(icon) },
        shape = CircleShape,
        backgroundColor = Color.Transparent,
        border = null,
        elevation = 0.dp
    ) {
        Column {
            Icon(icon, tint = tint)

            if (isSelected) {
                Box(Modifier
                    .padding(top = 3.dp)
                    .preferredWidth(icon.defaultWidth)
                    .preferredHeight(1.dp)
                    .background(tint)
                )
            } else {
                Spacer(modifier = Modifier.preferredHeight(4.dp))
            }
        }

    }
}
@Composable
fun TodoRow(todo: TodoItem, onItemClicked: (TodoItem) -> Unit, modifier: Modifier = Modifier) {
    Row(
        modifier
            .clickable { onItemClicked(todo) }
            .padding(horizontal = 16.dp)
            .padding(vertical = 8.dp),
        horizontalArrangement = Arrangement.SpaceBetween
    ) {
        Text(todo.task)
        val tint = remember(todo.id) { randomTint() }
        Icon(todo.icon, tint = contentColor().copy(alpha = tint))
    }
}

fun randomTint(): Float {
    return Random.nextFloat().coerceAtLeast(0.3f).coerceAtMost(0.9f)
}

@Preview
@Composable
fun PreviewTodoScreen() {
    val items = listOf(
        TodoItem("Learn compose", Icons.Default.AlarmOn),
        TodoItem("Take the codelab"),
        TodoItem("Apply state", Icons.Default.GroupWork),
        TodoItem("Build dynamic UIs", Icons.Default.DoneAll)
    )
    TodoScreen(items, null, {} , {}, {}, {}, {})
}

@Preview
@Composable
fun PreviewTodoRow() {
    val todo = remember { generateRandomTodoItem() }
    TodoRow(todo = todo, onItemClicked = {}, Modifier.fillMaxWidth())
}

@Preview
@Composable
fun PreviewTextAndInput() {
    InputTextAndButton(text = "Editable text", onTextChange = {}, primaryAction = {}) {
        Button(onClick = {}) {
            Text("Any button here")
        }
    }
}

@Preview
@Composable
fun PreviewIconRow() = IconRow(icon = DefaultIconSelection, onIconChange = {})



// this is to workaround a compiler bug in the pre-release dev16 compiler
val f: (Int, Int, Int) -> Unit = { _, _, _ -> }