/*
 * TabWidget.cpp - tabwidget for LMMS
 *
 * Copyright (c) 2005-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
 *
 * This file is part of LMMS - https://lmms.io
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public
 * License along with this program (see COPYING); if not, write to the
 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301 USA.
 *
 */


#include "TabWidget.h"

#include <QMouseEvent>
#include <QPainter>
#include <QPixmap>
#include <QToolTip>
#include <QWheelEvent>

#include "DeprecationHelper.h"
#include "embed.h"
#include "gui_templates.h"

TabWidget::TabWidget(const QString & caption, QWidget * parent, bool usePixmap,
					 bool resizable) :
	QWidget( parent ),
	m_resizable( resizable ),
	m_activeTab( 0 ),
	m_caption( caption ),
	m_usePixmap( usePixmap ),
	m_tabText( 0, 0, 0 ),
	m_tabTitleText( 0, 0, 0 ),
	m_tabSelected( 0, 0, 0 ),
	m_tabBackground( 0, 0, 0 ),
	m_tabBorder( 0, 0, 0 )
{

	// Create taller tabbar when it's to display artwork tabs
	m_tabbarHeight = usePixmap ? GRAPHIC_TAB_HEIGHT : TEXT_TAB_HEIGHT;

	m_tabheight = caption.isEmpty() ? m_tabbarHeight - 3 : m_tabbarHeight - 4;

	setFont( pointSize<8>( font() ) );

	setAutoFillBackground( true );
	QColor bg_color = QApplication::palette().color( QPalette::Active, QPalette::Background ). darker( 132 );
	QPalette pal = palette();
	pal.setColor( QPalette::Background, bg_color );
	setPalette( pal );

}

void TabWidget::addTab( QWidget * w, const QString & name, const char *pixmap, int idx )
{
	setFont( pointSize<8>( font() ) );

	// Append tab when position is not given
	if( idx < 0/* || m_widgets.contains( idx ) == true*/ )
	{
		while( m_widgets.contains( ++idx ) == true )
		{
		}
	}

	// Tab's width when it is a text tab. This isn't correct for artwork tabs, but it's fixed later during the PaintEvent
	int tab_width = horizontalAdvance(fontMetrics(), name) + 10;

	// Register new tab
	widgetDesc d = { w, pixmap, name, tab_width };
	m_widgets[idx] = d;

	// Position tab's window
	if (!m_resizable)
	{
		w->setFixedSize( width() - 4, height() - m_tabbarHeight );
	}
	w->move( 2, m_tabbarHeight - 1 );
	w->hide();

	// Show tab's window if it's active
	if( m_widgets.contains( m_activeTab ) )
	{
		// make sure new tab doesn't overlap current widget
		m_widgets[m_activeTab].w->show();
		m_widgets[m_activeTab].w->raise();
	}
}




void TabWidget::setActiveTab( int idx )
{
	if( m_widgets.contains( idx ) )
	{
		int old_active = m_activeTab;
		m_activeTab = idx;
		m_widgets[m_activeTab].w->raise();
		m_widgets[m_activeTab].w->show();
		if( old_active != idx && m_widgets.contains( old_active ) )
		{
			m_widgets[old_active].w->hide();
		}
		update();
	}
}


// Return the index of the tab at position "pos"
int TabWidget::findTabAtPos( const QPoint *pos )
{

	if( pos->y() > 1 && pos->y() < m_tabbarHeight - 1 )
	{
		int cx = ((m_caption == "") ? 4 : 14) + horizontalAdvance(fontMetrics(), m_caption);

		for( widgetStack::iterator it = m_widgets.begin(); it != m_widgets.end(); ++it )
		{
			if( pos->x() >= cx && pos->x() <= cx + ( *it ).nwidth )
			{
				return( it.key() );
			}
			cx += ( *it ).nwidth;
		}
	}

	// Haven't found any tab at position "pos"
	return( -1 );
}


// Overload the QWidget::event handler to display tooltips (from https://doc.qt.io/qt-4.8/qt-widgets-tooltips-example.html)
bool TabWidget::event(QEvent *event)
{

	if ( event->type() == QEvent::ToolTip )
	{
		QHelpEvent *helpEvent = static_cast<QHelpEvent *>(event);

		int idx = findTabAtPos( & helpEvent->pos() );

		if ( idx != -1 )
		{
			// Display tab's tooltip
			QToolTip::showText( helpEvent->globalPos(), m_widgets[idx].name );
		}
		else
		{
			// The tooltip event doesn't relate to any tab, let's ignore it
			QToolTip::hideText();
			event->ignore();
		}

		return true;
	}

	// not a Tooltip event, let's propagate it to the other event handlers
	return QWidget::event(event);
}


// Activate tab when clicked
void TabWidget::mousePressEvent( QMouseEvent * me )
{

	// Find index of tab that has been clicked
	QPoint pos = me->pos();
	int idx = findTabAtPos( &pos );

	// When found, activate tab that has been clicked
	if ( idx != -1 )
	{
		setActiveTab( idx );
		update();
		return;
	}
}




void TabWidget::resizeEvent( QResizeEvent * )
{
	if (!m_resizable)
	{
		for ( widgetStack::iterator it = m_widgets.begin();
							it != m_widgets.end(); ++it )
		{
			( *it ).w->setFixedSize( width() - 4, height() - m_tabbarHeight );
		}
	}
}




void TabWidget::paintEvent( QPaintEvent * pe )
{
	QPainter p( this );
	p.setFont( pointSize<7>( font() ) );

	// Draw background
	QBrush bg_color = p.background();
	p.fillRect( 0, 0, width() - 1, height() - 1, bg_color );

	// Draw external borders
	p.setPen( tabBorder() );
	p.drawRect( 0, 0, width() - 1, height() - 1 );

	// Draw tabs' bar background
	p.fillRect( 1, 1, width() - 2, m_tabheight + 2, tabBackground() );

	// Draw title, if any
	if( ! m_caption.isEmpty() )
	{
		p.setFont( pointSize<8>( p.font() ) );
		p.setPen( tabTitleText() );
		p.drawText( 5, 11, m_caption );
	}

	// Calculate the tabs' x (tabs are painted next to the caption)
	int tab_x_offset = m_caption.isEmpty() ? 4 : 14 + horizontalAdvance(fontMetrics(), m_caption);

	// Compute tabs' width depending on the number of tabs (only applicable for artwork tabs)
	widgetStack::iterator first = m_widgets.begin();
	widgetStack::iterator last = m_widgets.end();
	int tab_width = width();
	if ( first != last )
	{
		tab_width = ( width() - tab_x_offset ) / std::distance( first, last );
	}

	// Draw all tabs
	p.setPen( tabText() );
	for( widgetStack::iterator it = first ; it != last ; ++it )
	{
		// Draw a text tab or a artwork tab.
		if( m_usePixmap )
		{
			// Fixes tab's width, because original size is only correct for text tabs
			( *it ).nwidth = tab_width;

			// Get artwork
			QPixmap artwork( embed::getIconPixmap( ( *it ).pixmap ) );

			// Highlight active tab
			if( it.key() == m_activeTab )
			{
				p.fillRect( tab_x_offset, 0, ( *it ).nwidth, m_tabbarHeight - 1, tabSelected() );
			}

			// Draw artwork
			p.drawPixmap(tab_x_offset + ( ( *it ).nwidth - artwork.width() ) / 2, 1, artwork );
		}
		else
		{
			// Highlight tab when active
			if( it.key() == m_activeTab )
			{
				p.fillRect( tab_x_offset, 2, ( *it ).nwidth - 6, m_tabbarHeight - 4, tabSelected() );
			}

			// Draw text
			p.drawText( tab_x_offset + 3, m_tabheight + 1, ( *it ).name );
		}

		// Next tab's horizontal position
		tab_x_offset += ( *it ).nwidth;
	}
}




// Switch between tabs with mouse wheel
void TabWidget::wheelEvent( QWheelEvent * we )
{
	if(position(we).y() > m_tabheight)
	{
		return;
	}

	we->accept();
	int dir = (we->angleDelta().y() < 0) ? 1 : -1;
	int tab = m_activeTab;
	while( tab > -1 && static_cast<int>( tab ) < m_widgets.count() )
	{
		tab += dir;
		if( m_widgets.contains( tab ) )
		{
			break;
		}
	}
	setActiveTab( tab );
}




// Let parent widgets know how much space this tab widget needs
QSize TabWidget::minimumSizeHint() const
{
	if (m_resizable)
	{
		int maxWidth = 0, maxHeight = 0;
		for ( widgetStack::const_iterator it = m_widgets.begin();
							it != m_widgets.end(); ++it )
		{
			maxWidth = std::max(maxWidth, it->w->minimumSizeHint().width());
			maxHeight = std::max(maxHeight, it->w->minimumSizeHint().height());
		}
		// "-1" :
		// in "addTab", under "Position tab's window", the widget is
		// moved up by 1 pixel
		return QSize(maxWidth + 4, maxHeight + m_tabbarHeight - 1);
	}
	else { return QWidget::minimumSizeHint(); }
}




QSize TabWidget::sizeHint() const
{
	if (m_resizable)
	{
		int maxWidth = 0, maxHeight = 0;
		for ( widgetStack::const_iterator it = m_widgets.begin();
							it != m_widgets.end(); ++it )
		{
			maxWidth = std::max(maxWidth, it->w->sizeHint().width());
			maxHeight = std::max(maxHeight, it->w->sizeHint().height());
		}
		// "-1" :
		// in "addTab", under "Position tab's window", the widget is
		// moved up by 1 pixel
		return QSize(maxWidth + 4, maxHeight + m_tabbarHeight - 1);
	}
	else { return QWidget::sizeHint(); }
}




// Return the color to be used to draw a TabWidget's title text (if any)
QColor TabWidget::tabTitleText() const
{
	return m_tabTitleText;
}

// Set the color to be used to draw a TabWidget's title text (if any)
void TabWidget::setTabTitleText( const QColor & c )
{
	m_tabTitleText = c;
}

// Return the color to be used to draw a TabWidget's text (if any)
QColor TabWidget::tabText() const
{
	return m_tabText;
}

// Set the color to be used to draw a TabWidget's text (if any)
void TabWidget::setTabText( const QColor & c )
{
	m_tabText = c;
}

// Return the color to be used to highlight a TabWidget'selected tab (if any)
QColor TabWidget::tabSelected() const
{
	return m_tabSelected;
}

// Set the color to be used to highlight a TabWidget'selected tab (if any)
void TabWidget::setTabSelected( const QColor & c )
{
	m_tabSelected = c;
}

// Return the color to be used for the TabWidget's background
QColor TabWidget::tabBackground() const
{
	return m_tabBackground;
}

// Set the color to be used for the TabWidget's background
void TabWidget::setTabBackground( const QColor & c )
{
	m_tabBackground = c;
}

// Return the color to be used for the TabWidget's borders
QColor TabWidget::tabBorder() const
{
	return m_tabBorder;
}

// Set the color to be used for the TabWidget's borders
void TabWidget::setTabBorder( const QColor & c )
{
	m_tabBorder = c;
}
