//=============================================================================
//
//   File : KviModeEditor.cpp
//   Creation date : Sat Apr 14 2001 13:52:11 by Szymon Stefanek
//
//   This file is part of the KVIrc irc client distribution
//   Copyright (C) 2001-2010 Szymon Stefanek (pragma at kvirc dot net)
//
//   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 opinion) 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. If not, write to the Free Software Foundation,
//   Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
//=============================================================================

#include "KviModeEditor.h"
#include "KviIconManager.h"
#include "KviLocale.h"
#include "KviQString.h"
#include "KviIrcConnection.h"
#include "KviIrcConnectionServerInfo.h"
#include "KviChannelWindow.h"
#include "KviIrcConnectionUserInfo.h"
#include "KviWindowToolWidget.h"

#include <QScrollArea>
#include <QLayout>
#include <QLabel>
#include <QCheckBox>
#include <QLineEdit>
#include <QPushButton>


KviModeEditor::KviModeEditor(QWidget * par,KviWindowToolPageButton* button,const char * name,KviChannelWindow * pChan)
: KviWindowToolWidget(par,button)
{
	setObjectName(name);
	m_pChannel = pChan;

	QGridLayout *pMasterLayout = new QGridLayout(this);

	setFocusPolicy(Qt::ClickFocus);

	QScrollArea * pScrollArea = new QScrollArea(this);
	pScrollArea->viewport()->setBackgroundRole(QPalette::Background);
	pMasterLayout->addWidget(pScrollArea,0,0);

	pMasterLayout->setRowStretch(1,1);

	m_pButton = new QPushButton("", this);
	pMasterLayout->addWidget(m_pButton,1,0);
	connect(m_pButton,SIGNAL(clicked()),this,SLOT(commit()));

	QWidget * pBackground = new QWidget(pScrollArea->viewport());

	QGridLayout *g = new QGridLayout(pBackground);

	QLabel * l = new QLabel("",pBackground);
	l->setPixmap(*(g_pIconManager->getSmallIcon(KviIconManager::Mode)));
	g->addWidget(l,0,0);

	l = new QLabel(__tr2qs("Channel Modes"),pBackground);

	g->addWidget(l,0,1,1,1);

	QFrame * f = new QFrame(pBackground);
	f->setFrameStyle(QFrame::HLine | QFrame::Sunken);
	g->addWidget(f,1,0,1,3);

	QCheckBox * pCheckBox = 0;
	QLineEdit * pLineEdit = 0;
	int iRow = 1;
	QString szTmp;
	char cMode = 0;

	//NOTE: this is a fallback is for some reason we don't have a serverInfo() struct available fot this connection

	// first, the basic checkable modes pstnmi
	QString szModes = "pstnmi";

	while(!szModes.isEmpty())
	{
		cMode = szModes[0].unicode();
		szModes.remove(0,1);

		szTmp = QString("%1: %2").arg(cMode).arg(*(getModeDescription(cMode)));
		pCheckBox = new QCheckBox(szTmp,pBackground);
		m_pCheckBoxes.insert(cMode,pCheckBox);
		if(pChan)
			pCheckBox->setChecked(pChan->plainChannelMode().contains(cMode));
		iRow++;
		g->addWidget(pCheckBox,iRow,0,1,3);
	}

	// second, che basic modes with parameter lk
	szModes = "lk";

	while(!szModes.isEmpty())
	{
		cMode = szModes[0].unicode();
		szModes.remove(0,1);

		szTmp = QString("%1: %2").arg(cMode).arg(*(getModeDescription(cMode)));
		pCheckBox = new QCheckBox(szTmp,pBackground);
		m_pCheckBoxes.insert(cMode,pCheckBox);
		iRow++;
		g->addWidget(pCheckBox,iRow,0,1,3);

		connect(pCheckBox,SIGNAL(toggled(bool)),this,SLOT(checkBoxToggled(bool)));
		pLineEdit = new QLineEdit(pBackground);
		m_pLineEdits.insert(cMode,pLineEdit);
		iRow++;
		g->addWidget(pLineEdit,iRow,1,1,2);

		if(pChan)
		{
			if(pChan->hasChannelMode(cMode))
			{
				pCheckBox->setChecked(true);
				pLineEdit->setText(pChan->channelModeParam(cMode));
			} else {
				pLineEdit->setEnabled(false);
			}
		}
	}

	// third, check if the we have any info about other modes supported by the server
	KviIrcConnectionServerInfo * pServerInfo = 0;
	if(m_pChannel) pServerInfo = m_pChannel->serverInfo();
	if(!pServerInfo)
	{
		g->setRowStretch(++iRow,1);
		g->setColumnStretch(2,1);
		pScrollArea->setWidget(pBackground);
		return;
	}

	// 4th, more plain modes supported by the server
	szModes = pServerInfo->supportedPlainModes();

	// remove modes that we already implemented
	szModes.remove("p");
	szModes.remove("s");
	szModes.remove("t");
	szModes.remove("n");
	szModes.remove("m");
	szModes.remove("i");

	while(!szModes.isEmpty())
	{
		cMode = szModes[0].unicode();
		szModes.remove(0,1);

		szTmp = QString("%1: %2").arg(cMode).arg(*(getModeDescription(cMode)));
		pCheckBox = new QCheckBox(szTmp,pBackground);
		m_pCheckBoxes.insert(cMode,pCheckBox);
		if(pChan)
			pCheckBox->setChecked(pChan->plainChannelMode().contains(cMode));

		iRow++;
		g->addWidget(pCheckBox,iRow,0,1,3);
	}

	iRow++;

	// parameterized modes
	szModes = pServerInfo->supportedParameterModes();
	szModes.append(pServerInfo->supportedParameterWhenSetModes());

	// remove modes that we already implemented
	szModes.remove("k");
	szModes.remove("l");

	while(!szModes.isEmpty())
	{
		cMode = szModes[0].unicode();
		szModes.remove(0,1);

		szTmp = QString("%1: %2").arg(cMode).arg(*(getModeDescription(cMode)));
		pCheckBox = new QCheckBox(szTmp,pBackground);
		m_pCheckBoxes.insert(cMode,pCheckBox);
		iRow++;
		g->addWidget(pCheckBox,iRow,0,1,3);

		connect(pCheckBox,SIGNAL(toggled(bool)),this,SLOT(checkBoxToggled(bool)));
		pLineEdit = new QLineEdit(pBackground);
		m_pLineEdits.insert(cMode,pLineEdit);
		iRow++;
		g->addWidget(pLineEdit,iRow,1,1,2);

		if(pChan->hasChannelMode(cMode))
		{
			pCheckBox->setChecked(true);
			pLineEdit->setText(pChan->channelModeParam(cMode));
		} else {
			pLineEdit->setEnabled(false);
		}
	}

	g->setRowStretch(++iRow,1);
	g->setColumnStretch(2,1);
	pScrollArea->setWidget(pBackground);

	updateOpStatus();

	if(m_pChannel)
		connect(m_pChannel, SIGNAL(opStatusChanged()), this, SLOT(updateOpStatus()));
}

KviModeEditor::~KviModeEditor()
{
	qDeleteAll(m_pLineEdits);
	qDeleteAll(m_pCheckBoxes);
}

void KviModeEditor::updateOpStatus()
{
	bool isEnabled=true;
	if(m_pChannel)
	{
		if(!( m_pChannel->isMeHalfOp() ||
			m_pChannel->isMeOp() ||
			m_pChannel->isMeChanOwner() ||
			m_pChannel->isMeChanAdmin() ||
			m_pChannel->connection()->userInfo()->hasUserMode('o') ||
			m_pChannel->connection()->userInfo()->hasUserMode('O'))
		) isEnabled=false;
	}

	if(m_pButton)
		m_pButton->setText(isEnabled ? __tr2qs("&Apply") : __tr2qs("Close"));

	foreach(QLineEdit* pLineEdit, m_pLineEdits)
		pLineEdit->setEnabled(isEnabled);

	foreach(QCheckBox* pCheckBox, m_pCheckBoxes)
		pCheckBox->setEnabled(isEnabled);
}

void KviModeEditor::checkBoxToggled(bool bChecked)
{
	// enable/disable the lineedit if any
	if(!sender())
		return;

	QChar c = m_pCheckBoxes.key((QCheckBox *)sender());
	if(c.isNull())
		return;

	QLineEdit * pLineEdit = m_pLineEdits.value(c.unicode());
	if(!pLineEdit)
		return;

	pLineEdit->setEnabled(bChecked);
}

void KviModeEditor::commit()
{
	if(!m_pChannel)
	{
		emit done();
		return;
	}

	QMap<char, QString> szPlusModes;
	QMap<char, QString> szMinusModes;

	QCheckBox * pCheckBox;
	QLineEdit * pLineEdit;
	char cMode = 0;

	QList<char> modes = m_pCheckBoxes.keys();

	for(int i=0; i < modes.count();++i)
	{
		cMode = modes.at(i);
		pCheckBox = m_pCheckBoxes.value(cMode);
		if(!pCheckBox)
			continue; // wtf!?

		pLineEdit = m_pLineEdits.value(cMode);
		if(pLineEdit)
		{
			// mode with parameter (with an exception)
			if(pCheckBox->isChecked())
			{
				// mode is checked
				if(!m_pChannel->hasChannelMode(cMode) ||
					(pLineEdit->text().trimmed() != m_pChannel->channelModeParam(cMode)))
				{
					// mode was not checked before, or the parameter has changed
					szPlusModes.insert(cMode, pLineEdit->text().trimmed());
				}
			} else {
				// mode is not checked
				if(m_pChannel->hasChannelMode(cMode))
				{
					// but it was checked before

					// checks if this specific mode does not need a parameter when set
					if(modeNeedsParameterOnlyWhenSet(cMode))
					{
						szMinusModes.insert(cMode,QString());
					} else {
						szMinusModes.insert(cMode, m_pChannel->channelModeParam(cMode));
					}
				}
			}
		} else {
			//mode without parameter
			if(pCheckBox->isChecked())
			{
				if(!m_pChannel->plainChannelMode().contains(cMode))
					szPlusModes.insert(cMode, QString());
			} else {
				if(m_pChannel->plainChannelMode().contains(cMode))
					szMinusModes.insert(cMode,QString());
			}
		}
	}

	// now flush out mode changes
	int iModesPerLine=3; // a good default
	KviIrcConnectionServerInfo * pServerInfo = 0;
	if(m_pChannel) pServerInfo = m_pChannel->serverInfo();
	if(pServerInfo)
	{
		iModesPerLine = pServerInfo->maxModeChanges();
		if(iModesPerLine < 1) iModesPerLine = 1;
	}

	QString szModes;
	QStringList szParameters;
	int iModes=0;

	QMap<char, QString>::const_iterator iter = szMinusModes.constBegin();
	while (iter != szMinusModes.constEnd())
	{
		if(iter == szMinusModes.constBegin())
			szModes.append("-");
		szModes.append(iter.key());
		szParameters.append(iter.value());
		++iModes;
		++iter;

		//time to commit?
		if(iModes == iModesPerLine)
		{
			QString szCommitModes = szModes;
			if(iter == szMinusModes.constEnd())
				szModes.clear();
			else
				szModes="-";
			if(szParameters.count())
			{
				szCommitModes.append(QChar(' '));
				szCommitModes.append(szParameters.join(QString(" ")));
				szParameters.clear();
			}
			iModes=0;

			emit setMode(szCommitModes);
		}
	}

	iter = szPlusModes.constBegin();
	while (iter != szPlusModes.constEnd())
	{
		if(iter == szPlusModes.constBegin())
			szModes.append("+");
		szModes.append(iter.key());
		szParameters.append(iter.value());
		++iModes;
		++iter;

		//time to commit? this should be an ==, but includes the minus sign so "+aaa" = 4 chars
		if(iModes == iModesPerLine)
		{
			QString szCommitModes = szModes;
			if(iter == szPlusModes.constEnd())
				szModes.clear();
			else
				szModes="+";
			if(szParameters.count())
			{
				szCommitModes.append(QChar(' '));
				szCommitModes.append(szParameters.join(QString(" ")));
				szParameters.clear();
			}
			iModes=0;

			emit setMode(szCommitModes);
		}
	}

	if(iModes)
	{
		QString szCommitModes = szModes;
		szModes.clear();
		if(szParameters.count())
		{
			szCommitModes.append(QChar(' '));
			szCommitModes.append(szParameters.join(QString(" ")));
			szParameters.clear();
		}
		emit setMode(szCommitModes);
	}

	emit done();
}

inline const QString * KviModeEditor::getModeDescription(char cMode)
{
	if(!m_pChannel) return 0;
	KviIrcConnectionServerInfo * pServerInfo = m_pChannel->serverInfo();
	if(pServerInfo)
		return &(pServerInfo->getChannelModeDescription(cMode));
	return 0;
}

inline bool KviModeEditor::modeNeedsParameterOnlyWhenSet(char cMode)
{
	if(!m_pChannel) return 0;
	KviIrcConnectionServerInfo * pServerInfo = m_pChannel->serverInfo();
	if(pServerInfo)
		return pServerInfo->supportedParameterWhenSetModes().contains(cMode);
	return false;
}

#ifndef COMPILE_USE_STANDALONE_MOC_SOURCES
#include "KviModeEditor.moc"
#endif //!COMPILE_USE_STANDALONE_MOC_SOURCES
