/*
    nanogui/tabwidget.cpp -- A wrapper around the widgets TabHeader and StackedWidget
    which hooks the two classes together.

    The tab widget was contributed by Stefan Ivanov.

    NanoGUI was developed by Wenzel Jakob <wenzel.jakob@epfl.ch>.
    The widget drawing code is based on the NanoVG demo application
    by Mikko Mononen.

    All rights reserved. Use of this source code is governed by a
    BSD-style license that can be found in the LICENSE.txt file.
*/

#include <nanogui/tabwidget.h>
#include <nanogui/tabheader.h>
#include <nanogui/stackedwidget.h>
#include <nanogui/theme.h>
#include <nanogui/opengl.h>
#include <nanogui/window.h>
#include <nanogui/screen.h>
#include <algorithm>

NAMESPACE_BEGIN(nanogui)

TabWidget::TabWidget(Widget* parent)
    : Widget(parent)
    , mHeader(new TabHeader(nullptr)) // create using nullptr, add children below
    , mContent(new StackedWidget(nullptr)) {

    // since TabWidget::addChild is going to throw an exception to prevent
    // mis-use of this class, add the child directly
    Widget::addChild(childCount(), mHeader);
    Widget::addChild(childCount(), mContent);

    mHeader->setCallback([this](int i) {
        mContent->setSelectedIndex(i);
        if (mCallback)
            mCallback(i);
    });
}

void TabWidget::addChild(int /*index*/, Widget * /*widget*/) {
    // there may only be two children: mHeader and mContent, created in the constructor
    throw std::runtime_error(
        "TabWidget: do not add children directly to the TabWidget, create tabs "
        "and add children to the tabs.  See TabWidget class documentation for "
        "example usage."
    );
}

void TabWidget::setActiveTab(int tabIndex) {
    mHeader->setActiveTab(tabIndex);
    mContent->setSelectedIndex(tabIndex);
}

int TabWidget::activeTab() const {
    assert(mHeader->activeTab() == mContent->selectedIndex());
    return mContent->selectedIndex();
}

int TabWidget::tabCount() const {
    assert(mContent->childCount() == mHeader->tabCount());
    return mHeader->tabCount();
}

Widget* TabWidget::createTab(int index, const std::string &label) {
    Widget* tab = new Widget(nullptr);
    addTab(index, label, tab);
    return tab;
}

Widget* TabWidget::createTab(const std::string &label) {
    return createTab(tabCount(), label);
}

void TabWidget::addTab(const std::string &name, Widget *tab) {
    addTab(tabCount(), name, tab);
}

void TabWidget::addTab(int index, const std::string &label, Widget *tab) {
    assert(index <= tabCount());
    // It is important to add the content first since the callback
    // of the header will automatically fire when a new tab is added.
    mContent->addChild(index, tab);
    mHeader->addTab(index, label);
    assert(mHeader->tabCount() == mContent->childCount());
}

int TabWidget::tabLabelIndex(const std::string &label) {
    return mHeader->tabIndex(label);
}

int TabWidget::tabIndex(Widget* tab) {
    return mContent->childIndex(tab);
}

void TabWidget::ensureTabVisible(int index) {
    if (!mHeader->isTabVisible(index))
        mHeader->ensureTabVisible(index);
}

const Widget *TabWidget::tab(const std::string &tabName) const {
    int index = mHeader->tabIndex(tabName);
    if (index == -1 || index == mContent->childCount())
        return nullptr;
    return mContent->children()[index];
}

Widget *TabWidget::tab(const std::string &tabName) {
    int index = mHeader->tabIndex(tabName);
    if (index == -1 || index == mContent->childCount())
        return nullptr;
    return mContent->children()[index];
}

const Widget *TabWidget::tab(int index) const {
    if (index < 0 || index >= mContent->childCount())
        return nullptr;
    return mContent->children()[index];
}

Widget *TabWidget::tab(int index) {
    if (index < 0 || index >= mContent->childCount())
        return nullptr;
    return mContent->children()[index];
}

bool TabWidget::removeTab(const std::string &tabName) {
    int index = mHeader->removeTab(tabName);
    if (index == -1)
        return false;
    mContent->removeChild(index);
    return true;
}

void TabWidget::removeTab(int index) {
    assert(mContent->childCount() < index);
    mHeader->removeTab(index);
    mContent->removeChild(index);
    if (activeTab() == index)
        setActiveTab(index == (index - 1) ? index - 1 : 0);
}

const std::string &TabWidget::tabLabelAt(int index) const {
    return mHeader->tabLabelAt(index);
}

void TabWidget::performLayout(NVGcontext* ctx) {
    int headerHeight = mHeader->preferredSize(ctx).y();
    int margin = mTheme->mTabInnerMargin;
    mHeader->setPosition({ 0, 0 });
    mHeader->setSize({ mSize.x(), headerHeight });
    mHeader->performLayout(ctx);
    mContent->setPosition({ margin, headerHeight + margin });
    mContent->setSize({ mSize.x() - 2 * margin, mSize.y() - 2*margin - headerHeight });
    mContent->performLayout(ctx);
}

Vector2i TabWidget::preferredSize(NVGcontext* ctx) const {
    auto contentSize = mContent->preferredSize(ctx);
    auto headerSize = mHeader->preferredSize(ctx);
    int margin = mTheme->mTabInnerMargin;
    auto borderSize = Vector2i(2 * margin, 2 * margin);
    Vector2i tabPreferredSize = contentSize + borderSize + Vector2i(0, headerSize.y());
    return tabPreferredSize;
}

void TabWidget::draw(NVGcontext* ctx) {
    int tabHeight = mHeader->preferredSize(ctx).y();
    auto activeArea = mHeader->activeButtonArea();


    for (int i = 0; i < 3; ++i) {
        nvgSave(ctx);
        if (i == 0)
            nvgIntersectScissor(ctx, mPos.x(), mPos.y(), activeArea.first.x() + 1, mSize.y());
        else if (i == 1)
            nvgIntersectScissor(ctx, mPos.x() + activeArea.second.x(), mPos.y(), mSize.x() - activeArea.second.x(), mSize.y());
        else
            nvgIntersectScissor(ctx, mPos.x(), mPos.y() + tabHeight + 2, mSize.x(), mSize.y());

        nvgBeginPath(ctx);
        nvgStrokeWidth(ctx, 1.0f);
        nvgRoundedRect(ctx, mPos.x() + 0.5f, mPos.y() + tabHeight + 1.5f, mSize.x() - 1,
                       mSize.y() - tabHeight - 2, mTheme->mButtonCornerRadius);
        nvgStrokeColor(ctx, mTheme->mBorderLight);
        nvgStroke(ctx);

        nvgBeginPath(ctx);
        nvgRoundedRect(ctx, mPos.x() + 0.5f, mPos.y() + tabHeight + 0.5f, mSize.x() - 1,
                       mSize.y() - tabHeight - 2, mTheme->mButtonCornerRadius);
        nvgStrokeColor(ctx, mTheme->mBorderDark);
        nvgStroke(ctx);
        nvgRestore(ctx);
    }

    Widget::draw(ctx);
}

NAMESPACE_END(nanogui)
