/*
 * SPDX-FileCopyrightText: 2021 Devin Lin <espidev@gmail.com>
 * SPDX-FileCopyrightText: 2026 Akseli Lahtinen <akselmo@akselmo.dev>
 * SPDX-License-Identifier: LGPL-2.0-or-later
 */

pragma ComponentBehavior: Bound

import QtQuick
import QtQml
import QtQuick.Layouts
import QtQuick.Controls as QQC
import QtQuick.Templates as T
import org.kde.kirigami.platform as Platform
import org.kde.kirigami.controls as Controls
import org.kde.kirigami.templates as KT

/*!
  \qmltype NavigationTabBar
  \inqmlmodule org.kde.kirigami

  \brief Page navigation tab-bar, used as an alternative to sidebars for 3-5 elements. A NavigationTabBar can be both used as a footer or a header for a page. It can be combined with secondary toolbars above (if in the footer) to provide page actions.

  Example usage:
  \qml

  import QtQuick
  import org.kde.kirigami as Kirigami

  Kirigami.ApplicationWindow {
      title: "Clock"

      pageStack.initialPage: worldPage

      Kirigami.Page {
          id: worldPage
          title: "World"
          visible: false
      }
      Kirigami.Page {
          id: timersPage
          title: "Timers"
          visible: false
      }
      Kirigami.Page {
          id: stopwatchPage
          title: "Stopwatch"
          visible: false
      }
      Kirigami.Page {
          id: alarmsPage
          title: "Alarms"
          visible: false
      }

      footer: Kirigami.NavigationTabBar {
          actions: [
              Kirigami.Action {
                  icon.name: "globe"
                  text: "World"
                  checked: worldPage.visible
                  onTriggered: {
                       if (!worldPage.visible) {
                           while (pageStack.depth > 0) {
                               pageStack.pop();
                           }
                           pageStack.push(worldPage);
                      }
                  }
              },
              Kirigami.Action {
                  icon.name: "player-time"
                  text: "Timers"
                  checked: timersPage.visible
                  onTriggered: {
                      if (!timersPage.visible) {
                          while (pageStack.depth > 0) {
                              pageStack.pop();
                          }
                          pageStack.push(timersPage);
                      }
                  }
              },
              Kirigami.Action {
                  icon.name: "chronometer"
                  text: "Stopwatch"
                  checked: stopwatchPage.visible
                  onTriggered: {
                      if (!stopwatchPage.visible) {
                          while (pageStack.depth > 0) {
                              pageStack.pop();
                          }
                          pageStack.push(stopwatchPage);
                      }
                  }
              },
              Kirigami.Action {
                  icon.name: "notifications"
                  text: "Alarms"
                  checked: alarmsPage.visible
                  onTriggered: {
                      if (!alarmsPage.visible) {
                          while (pageStack.depth > 0) {
                              pageStack.pop();
                          }
                          pageStack.push(alarmsPage);
                      }
                  }
              }
          ]
      }
  }
\endqml

  A NavigationTabBar can also be combined with a QtQuick SwipeView, through which swiping between Pages becomes possible.

  Example usage with a SwipeView:
  \qml
import QtQuick
import QtQuick.Controls as QQC
import QtQuick.Layouts
import org.kde.kirigami as Kirigami

Kirigami.Page {
    title: "Clock"
    QQC.SwipeView {
        id: swipeView
        anchors.fill: parent
        clip: true
        onCurrentIndexChanged: footer.currentIndex = currentIndex
        Kirigami.Page {
            id: worldPage
            title: "World"
            QQC.Label {
                anchors.centerIn: parent
                text: "Current Page: World"
            }
        }
        Kirigami.Page {
            [...]
        }
        ...
        Kirigami.Page {
            id: alarmsPage
            title: "Alarms"
            QQC.Label {
                anchors.centerIn: parent
                text: "Current Page: Alarms"
            }
        }
    }

    footer: Kirigami.NavigationTabBar {
        actions: [
            Kirigami.Action {
                icon.name: "globe"
                text: "World"
                checked: true
                onTriggered: swipeView.currentIndex = footer.currentIndex
            },
            Kirigami.Action {
                [...]
            },
            ...
            Kirigami.Action {
                icon.name: "notifications"
                text: "Alarms"
                onTriggered: swipeView.currentIndex = footer.currentIndex
            }
        ]
    }
}

  \endqml

  \sa NavigationTabButton
  \since 5.87
 */

QQC.ToolBar {
    id: root

//BEGIN properties
    /*!
      \qmlproperty list<Action> actions
      \brief This property holds the list of actions to be displayed in the toolbar.

      If the \c checked attribute in the action is set to \c true, the tab will be highlighted/selected.
     */
    property list<T.Action> actions

    /*!
      \qmlproperty list<Action> visibleActions
      \brief This property holds a subset of visible actions of the list of actions.

      An action is considered visible if it is either a Kirigami.Action with
      \c visible property set to true, or it is a plain QQC.Action.
     */
    readonly property list<T.Action> visibleActions: actions
        // Note: instanceof check implies `!== null`
        .filter(action => action instanceof Controls.Action
            ? action.visible
            : action !== null
        )

    /*!
      \brief The property holds the maximum width of the toolbar actions, before margins are added.
     */
    property real maximumContentWidth: {
        const minDelegateWidth = Platform.Units.gridUnit * 5;
        // Always have at least the width of 5 items, so that small amounts of actions look natural.
        return minDelegateWidth * Math.max(visibleActions.length, 5);
    }

    /*!
      \brief This property holds the index of currently checked tab.

      If the index set is out of bounds, or the triggered signal did not change any checked property of an action, the index
      will remain the same.
     */
    property int currentIndex: tabGroup.checkedButton && tabGroup.buttons.length > 0 ? (tabGroup.checkedButton as NavigationTabButton).tabIndex : -1

    /*!
      \brief This property holds the number of tab buttons.
     */
    readonly property int count: tabGroup.buttons.length

    /*!
      \qmlproperty ButtonGroup tabGroup
      \brief This property holds the ButtonGroup used to manage the tabs.
     */
    readonly property T.ButtonGroup tabGroup: tabGroup

    /*!
      \brief This property holds the calculated width that buttons on the tab bar use.

      \since 5.102
     */
    property real buttonWidth: {
        // Counting buttons because Repeaters can be counted among visibleChildren
        let visibleButtonCount = 0;
        const minWidth = contentItem.height * 0.75;
        for (const visibleChild of contentItem.visibleChildren) {
            if (contentItem.width / visibleButtonCount >= minWidth && // make buttons go off the screen if there is physically no room for them
                visibleChild instanceof T.AbstractButton) { // Checking for AbstractButtons because any AbstractButton can act as a tab
                ++visibleButtonCount;
            }
        }

        if (visibleButtonCount == 0) {
            return 0;
        }

        return Math.round(contentItem.width / visibleButtonCount);
    }

    /*!
     \ *brief Property for the delegate component that will be instantiated inside tab bar.

     \since 6.23
     */
    property Component delegate: Controls.NavigationTabButton {
        id: delegate

        required property T.Action modelData

        parent: root.contentItem
        action: modelData
        // Workaround setting the action when checkable is not explicitly set making tabs uncheckable
        onActionChanged: action.checkable = true

        Layout.minimumWidth: root.buttonWidth
        Layout.maximumWidth: root.buttonWidth
        Layout.fillHeight: true
    }

    contentWidth: root.maximumContentWidth

    position: {
        if (QQC.ApplicationWindow.window?.footer === root) {
            return QQC.ToolBar.Footer
        } else if (parent?.footer === root) {
            return QQC.ToolBar.Footer
        } else if (parent?.parent?.footer === parent) {
            return QQC.ToolBar.Footer
        } else {
            return QQC.ToolBar.Header
        }
    }

    contentItem: RowLayout {
        id: rowLayout
        spacing: root.spacing
    }
//END properties

    onCurrentIndexChanged: {
        if (currentIndex === -1) {
            if (tabGroup.checkState !== Qt.Unchecked) {
                tabGroup.checkState = Qt.Unchecked;
            }
            return;
        }
        if (!tabGroup.checkedButton || (tabGroup.checkedButton as KT.NavigationTabButton).tabIndex !== currentIndex) {
            const buttonForCurrentIndex = tabGroup.buttons[currentIndex]
            if (buttonForCurrentIndex.action) {
                // trigger also toggles and causes clicked() to be emitted
                buttonForCurrentIndex.action.trigger();
            } else {
                // toggle() does not trigger the action,
                // so don't use it if you want to use an action.
                // It also doesn't cause clicked() to be emitted.
                buttonForCurrentIndex.toggle();
            }
        }
    }

    // Used to manage which tab is checked and change the currentIndex
    T.ButtonGroup {
        id: tabGroup
        exclusive: true
        buttons: root.contentItem.children.filter((child) => child !== instantiator)

        onCheckedButtonChanged: {
            if (!checkedButton) {
                return
            }
            if (root.currentIndex !== (checkedButton as KT.NavigationTabButton).tabIndex) {
                root.currentIndex = (checkedButton as KT.NavigationTabButton).tabIndex;
            }
        }
    }

    // Using a Repeater here because Instantiator was causing issues:
    // NavigationTabButtons that were supposed to be destroyed were still
    // registered as buttons in tabGroup.
    // NOTE: This will make Repeater show up as child through visibleChildren
    Repeater {
        id: instantiator
        model: root.visibleActions
        delegate: root.delegate
    }
}
