/**
 * A box with tabs that can display one of several pages.
 */

package compose.widgets

import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxScope
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.ButtonDefaults
import androidx.compose.material.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.drawBehind
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.drawscope.Stroke


/**
 * A row of tabs and a box below that displays the page controlled by which tab is selected.
 */
@Composable
fun TabbedBox(
    selectedPage: Int?,
    onPageSelected: (Int) -> Unit,
    modifier: Modifier = Modifier,
    boxModifier: Modifier = Modifier,
    buttonsAlignment: Alignment.Horizontal = Alignment.CenterHorizontally,
    buttonsShape: RoundedCornerShape,
    border: BorderStroke = ButtonDefaults.outlinedBorder,
    drawContentBorder: Boolean = true,
    selectedBackground: Color = MaterialTheme.colors.secondary,
    content: TabbedBoxScope.() -> Unit
) {
    val currentOnPageSelected by rememberUpdatedState(onPageSelected)

    Column(
        modifier = modifier,
        verticalArrangement = Arrangement.spacedBy(-border.width)
    ) {
        val (tabs, pages) = remember(content) {
            val scope = object : TabbedBoxScope {
                val tabs = mutableListOf<@Composable (Boolean) -> Unit>()
                val pages = mutableListOf<@Composable BoxScope.() -> Unit>()
                override fun page(
                    tabContent: @Composable (Boolean) -> Unit,
                    pageContent: @Composable BoxScope.() -> Unit
                ) {
                    tabs.add(tabContent)
                    pages.add(pageContent)
                }
            }
            scope.content()
            Pair(scope.tabs, scope.pages)
        }

        SegmentedButtons(
            modifier = Modifier
                .align(buttonsAlignment),
            selectedButton = selectedPage,
            onButtonSelected = currentOnPageSelected,
            border = border,
            shape = buttonsShape,
            selectedBackground = selectedBackground,
        ) {
            for (tabContent in tabs) {
                button(content = tabContent)
            }
        }

        Box(
            modifier = Modifier
                .background(selectedBackground)
                .then(boxModifier)
                .drawBehind {
                    val strokeSize = border.width.toPx()

                    if (drawContentBorder) {
                        drawRect(
                            brush = border.brush,
                            topLeft = Offset(strokeSize/2, strokeSize/2),
                            size = Size(size.width - strokeSize, size.height - strokeSize),
                            style = Stroke(border.width.toPx()),

                        )
                    } else {
                        drawLine(
                            brush = border.brush,
                            start = Offset(0f, strokeSize/2),
                            end = Offset(size.width, strokeSize/2),
                            strokeWidth = strokeSize
                        )
                    }
                },
        ) {
            selectedPage?.let {
                pages[it]()
            }
        }
    }
}


/**
 * The scope for defining the tabs and pages.
 */
interface TabbedBoxScope {

    /**
     * Adds a page with [tabContent] to be placed in the tab, and [pageContent] in the box, when the tab is selected.
     */
    fun page(
        tabContent: @Composable (Boolean) -> Unit,
        pageContent: @Composable BoxScope.() -> Unit
    )

}