//=========================================================================//
//                                                                         //
//  PonyProg - Serial Device Programmer                                    //
//                                                                         //
//  Copyright (C) 1997-2025   Claudio Lanconelli                           //
//                                                                         //
//  https://github.com/lancos/ponyprog                                        //
//                                                                         //
//-------------------------------------------------------------------------//
//                                                                         //
// 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 version2 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 LICENSE);     if not, write to the         //
// Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. //
//                                                                         //
//=========================================================================//


#include <QDebug>
#include <QString>
#include <QMessageBox>

#include "bitfield.h"


BitFieldWidget::BitFieldWidget(QWidget *parent, QVector<BitInfo> &vInfo, QVector<MaskDescr> &vMask, unsigned int field, const QString &chipNm) :
	QWidget(parent)
{
	setupUi(this);

	chipName = chipNm;

	vecInfo = &vInfo;

	vecDescr = &vMask;

	bField = field;

	lstComboBoxes = (QVector<QComboBox *>() << comboBox0 << comboBox1 << comboBox2 << comboBox3);

	for (int i = 0; i < lstComboBoxes.count(); i++)
	{
		lstComboBoxes.at(i)->setHidden(true);
	}

	initWidget();
}


BitFieldWidget::~BitFieldWidget()
{
	qDebug() << "BitFieldWidget::~BitFieldWidget()";
}


void BitFieldWidget::initWidget()
{

	if (vecInfo->count() > 0)
	{
		int lastBit = 0;
		for (int i = 0; i < vecInfo->count(); i++)
		{
			QTreeWidgetItem *itm = new QTreeWidgetItem();
			int bitOffset = vecInfo->at(i).bit;
			lastBit = bitOffset;
			QString sDes = vecInfo->at(i).ShortDescr;
			itm->setText(0, QString("Bit %1, ").arg(bitOffset) + sDes);
			if (vecInfo->at(i).LongDescr.length() > 0)
			{
				itm->setText(1, vecInfo->at(i).LongDescr);
			}

			itm->setFlags(itm->flags() | Qt::ItemIsUserCheckable);

			itm->setCheckState(0, Qt::Unchecked);

			if (bField & (1 << bitOffset))
			{
				itm->setCheckState(0, Qt::Checked);
			}
			if (sDes == "SPIEN")
			{
				itm->setFlags(itm->flags() ^ Qt::ItemIsEnabled);
				itm->setCheckState(0, Qt::Checked);
				bField |= (1 << bitOffset);
			}
			if (sDes == "RSTDISBL")
			{
				itm->setCheckState(0, Qt::Unchecked);
				bField &= ~(1 << bitOffset);
			}

			treeWidget->addTopLevelItem(itm);
		}

		// reset not used bits
		for (unsigned int i = (lastBit + 1); i < 32; i++)
		{
			bField &= ~(1U << i);
		}

		treeWidget->expandAll();
		treeWidget->resizeColumnToContents(0);

		connect(treeWidget, SIGNAL(itemChanged(QTreeWidgetItem *, int)), this, SLOT(onBitClicked(QTreeWidgetItem *, int)));
	}

	scanMasks();

	createComboLists();

	// init of comboboxes
	for (int i = 0; i < treeWidget->topLevelItemCount(); i++)
	{
		if (treeWidget->topLevelItem(i)->text(1).length() == 0)
		{
			emit onBitClicked(treeWidget->topLevelItem(i), 0);
		}
	}
}


unsigned int BitFieldWidget::getBitfield()
{
	return bField;
}


void BitFieldWidget::createComboLists()
{
	for (int i = 0; i < maskList.count(); i++)
	{
		QStringList lst;
		QString currentMask = maskList.at(i);
		// two loops are not optimal!
		foreach (MaskDescr mdes, *vecDescr)
		{
			if (mdes.mask.indexOf(QRegExp(currentMask)) >= 0)
			{
				int posBeg = mdes.LongDescr.indexOf("=$n");
				if (posBeg > 0)
				{
					QString tStr = mdes.LongDescr;
					// TODO replace text with text from mdes.ExtDescr
					int posExtText = mdes.ExtDescr.indexOf(chipName + ": ");
					if (posExtText >= 0)
					{
						posExtText += chipName.length();
						QString addrTxt = mdes.ExtDescr.simplified().mid(posExtText + 2, 5);

						tStr.replace(posBeg + 1, 5, addrTxt);
					}

					lst << tStr;
				}
				else
				{
					lst << mdes.LongDescr;
				}
			}
		}

		// when not all combinations are descripted
// 		if (isExp(lst.count()) == false)
		if ((1 << maskBitSum.at(i)) != lst.count())
		{
			lst << "Undefined combination";
		}

		lstComboBoxes.at(i)->setHidden(false);
		lstComboBoxes.at(i)->addItems(lst);

		connect(lstComboBoxes.at(i), SIGNAL(activated(int)), this, SLOT(onComboSelected(int)));
	}
}


void BitFieldWidget::scanMasks()
{
	maskList.clear();
	maskBitSum.clear();

	// analyse from mask entries
	foreach (MaskDescr mdes, *vecDescr)
	{
		QString cMask = mdes.mask;
		cMask.replace(QRegExp("\\d+"), "\\d+");
		// at string begin only
		cMask = "^" + cMask;
		if (maskList.indexOf(cMask) == -1)
		{
			maskList << cMask;
			int numBits = 0;
			QRegExp rx("(\\d+)");
			int pos = 0;
			while ((pos = rx.indexIn(mdes.mask, pos)) != -1)
			{
				QString n =  rx.cap(1);
				numBits += n.length();

				pos += rx.matchedLength();
			}

			qDebug() << mdes.mask << "bits" << numBits;
			maskBitSum << numBits;
		}
	}

	qDebug() << maskList;
}


bool BitFieldWidget::isExp(unsigned int a)
{
	return (a > 0 && (a & (a - 1)) == 0);
}


void BitFieldWidget::onComboSelected(int idx)
{
	QComboBox *s = static_cast<QComboBox *>(sender());
	int globIdx = 0;
	int nMask = -1;

	// no changes in this case
	if (s->currentText() == "Undefined combination")
	{
		return;
	}

	// we have only 4 comboboxes for lock
	for (int i = 0; i < 4; i++)
	{
		if (lstComboBoxes.at(i) == s)
		{
			nMask = i;
			break;
		}
		globIdx += lstComboBoxes.at(i)->count();

		// correcture for undefined
		if (lstComboBoxes.at(i)->findText("Undefined combination") > 0)
		{
			globIdx--;
		}
	}

	if (nMask < 0)
	{
		return;
	}

	globIdx += idx;

	qDebug() << "onComboSelected index" << globIdx;

	if (vecDescr->at(globIdx).mask.length() == 0)
	{
		return;
	}

	// deactivate signal from checkbutton
	disconnect(treeWidget, SIGNAL(itemChanged(QTreeWidgetItem *, int)), this, SLOT(onBitClicked(QTreeWidgetItem *, int)));

	// possible list
	QStringList mskList = vecDescr->at(globIdx).mask.split(" ");
	qDebug() << mskList << vecDescr->at(globIdx).LongDescr;

	foreach (QString cMask, mskList)
	{
		setMaskBits(cMask);
	}

	// activate signal
	connect(treeWidget, SIGNAL(itemChanged(QTreeWidgetItem *, int)), this, SLOT(onBitClicked(QTreeWidgetItem *, int)));

	emit displayBitFields(bField);
}


void BitFieldWidget::setMaskBits(const QString &cMask)
{
	int p = cMask.indexOf("=");
	if (p < 0)
	{
		// not correct format
		return;
	}

	QString mskName = cMask.left(p);
	QString bitStr = cMask.mid(p + 1);
	bool ok;

	unsigned int localField = bitStr.toInt(&ok, 2);



	int idx;
	// at begin of string only
	mskName = "^" + mskName + "\\d+";

	qDebug() << cMask <<  "converted to" << mskName << (Qt::bin) << localField << (Qt::dec);

	// search in QTreeWidget the names
	for (idx = 0; idx < treeWidget->topLevelItemCount(); idx++)
	{
		QString t = treeWidget->topLevelItem(idx)->text(0);
		int pos = t.indexOf(", ");

		if (pos > 0)
		{
			QString nm = t.mid(pos + 2);

			// first element found
			if (nm.indexOf(QRegExp(mskName)) == 0)
			{
				break;
			}
		}
	}

	if (idx >= treeWidget->topLevelItemCount())
	{
		qDebug() << "setMaskBits is wrong";
		return;
	}

	int bitOffset = vecInfo->at(idx).bit;

	for (int i = 0; i < bitStr.length(); i++)
	{
		Qt::CheckState st;
		if (localField & 0x01)
		{
			st = Qt::Unchecked;

			bField |= (1 << bitOffset);
		}
		else
		{
			st = Qt::Checked;

			bField &= ~(1 << bitOffset);
		}

		localField >>= 1;

		treeWidget->topLevelItem(idx)->setCheckState(0, st);

		QString t = treeWidget->topLevelItem(idx)->text(0);
		int pos = t.indexOf(", ");

		if (pos > 0)
		{
			QString nm = t.mid(pos + 2);
			if (nm.indexOf(QRegExp(mskName)) < 0)
			{
				break;
			}
		}
		else
		{
			break;
		}

		bitOffset++;
		idx++;
	}
}



void BitFieldWidget::onBitClicked(QTreeWidgetItem *itm, int col)
{
	Qt::CheckState st = itm->checkState(0);
	QString t =  itm->text(0);
	int pos = t.indexOf(",");

	if (pos < 0)
	{
		return;
	}

	QString fuseName = t.mid(pos + 2);

	if (fuseName == "RSTDISBL")
	{
		QMessageBox msgBox(QMessageBox::Warning, "Warning", "Attention! If you disable Reset pin you can't access the the chip anymore", QMessageBox::Yes | QMessageBox::No);
// 		msgBox.setStyleSheet(programStyleSheet);
		msgBox.setButtonText(QMessageBox::Yes, "Yes");
		msgBox.setButtonText(QMessageBox::No, "No");
		int res = msgBox.exec();
		if (res == QMessageBox::No)
		{
			return;
		}
	}

	t = t.left(pos);
	t.remove("Bit ");

	int bOffset = t.toInt();

	if (st == Qt::Checked)
	{
		bField |= (1 << bOffset);
	}
	else
	{
		bField &= ~(1 << bOffset);
	}

	displayBitFields(bField);

	qDebug() << fuseName << maskList;

	int idxCombo = -1;

	for (int i = 0; i < maskList.count(); i++)
	{
		QString m = maskList.at(i);
		QStringList mSplitted = m.split(" ");

		for (int r = 0; r < mSplitted.count(); r++)
		{
			QString msplt = mSplitted.at(r);
			msplt.remove("=");
			if (fuseName.indexOf(QRegExp(msplt)) >= 0)
			{
				idxCombo = i;
				qDebug() << "found " << msplt << i;
				break;
			}
		}
		if (idxCombo != -1)
		{
			break;
		}
	}

	// maskListFuse.at(idxCombo) has mask to search
	// and idxCombo is the number of list
	if (idxCombo == -1)
	{
		return;
	}

	QString completeMask = maskList.at(idxCombo);

	// now replace the \\d+ with bit information from treewidget
	QStringList mSplitted = completeMask.split(" ");
	for (int r = 0; r < mSplitted.count(); r++)
	{
		QString orig = mSplitted.at(r);
		QString msplt = orig;
		msplt.remove("=");
		qDebug() << msplt;

		QString bitString;
		for (int iTree = 0; iTree < treeWidget->topLevelItemCount(); ++iTree)
		{
			QString t = treeWidget->topLevelItem(iTree)->text(0);
			int pos = t.indexOf(", ");
			if (pos <= 0)
			{
				continue;
			}

			t = t.mid(pos + 2);

			if (t.indexOf(QRegExp(msplt)) >= 0)
			{
				if (treeWidget->topLevelItem(iTree)->checkState(0) == Qt::Checked)
				{
					bitString = "0" + bitString;
				}
				else
				{
					bitString = "1" + bitString;
				}
			}
		}

		if (bitString.length() > 0)
		{
			msplt.replace("\\d+", QString("=" + bitString));
			completeMask.replace(orig, msplt);
		}
	}

	qDebug() << "converted: " << completeMask << "idx" << idxCombo;

	if (completeMask.length() > 0)
	{
		int i;
		for (i = 0; i < vecDescr->count(); i++)
		{
			if (vecDescr->at(i).mask.indexOf(QRegExp(completeMask)) == 0)
			{
				QString comboText = vecDescr->at(i).LongDescr;
				int c = lstComboBoxes.at(idxCombo)->findText(comboText);
				if (c >= 0)
				{
					lstComboBoxes.at(idxCombo)->setCurrentIndex(c);
				}

				break;
			}
		}
		if (i == vecDescr->count())
		{
			int p = lstComboBoxes.at(idxCombo)->findText("Undefined combination");
			if (p >= 0)
			{
				lstComboBoxes.at(idxCombo)->setCurrentIndex(p);
			}
		}
	}
}
