/*
Copyright (C) 2006 Evan Teran
                   eteran@alum.rit.edu

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; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/

#include "BinaryString.h"
#include <QValidator>
#include <QStringList>
#include <QString>
#include <cctype>	// for std::isxdigit

class HexStringValidator : public QValidator {
public:
	HexStringValidator(QObject * parent) : QValidator(parent) {}
	virtual ~HexStringValidator() {}
	
public:

	virtual void fixup(QString &input) const {
		const int len = input.length();
		QString temp;
		int index = 0;
		
		for(int i = 0; i < len; ++i) {
			const QChar ch = input[i];
			if(std::isxdigit(ch.toAscii())) {
				temp += ch.toUpper();
				if((index & 1) != 0) {
					temp += ' ';
				}
				++index;
			}
		}

		// ditch any trailing spaces
		input = temp.trimmed();
	}

	virtual State validate(QString &input, int &pos) const {
		if(input.isEmpty()) {
			return QValidator::Acceptable;
		} else {
		
			// TODO: can we detect if the char which was JUST deleted
			// (if any was deleted) was a space? and special case this?
			// as to not have the minor bug in this case?
				
			int char_pos	= pos - input.left(pos).count(' ');
			int chars		= 0;
			fixup(input);
			
			pos = 0;
			
			while(chars != char_pos) {
				if(input[pos] != ' ') {
					++chars;
				}
				++pos;
			}
			
			// favor the right side of a space
			if(input[pos] == ' ') {
				++pos;
			}

			return QValidator::Acceptable;
		}
	}
};


//------------------------------------------------------------------------------
// Name: setMaxLength(int n)
// Desc: 
//------------------------------------------------------------------------------
void BinaryString::setMaxLength(int n) {
	ui.txtAscii->setMaxLength(n);
	ui.txtUTF16->setMaxLength(n / 2);
	ui.txtHex->setMaxLength(n * 3);
}

//------------------------------------------------------------------------------
// Name: BinaryString(QWidget *parent)
// Desc: constructor
//------------------------------------------------------------------------------
BinaryString::BinaryString(QWidget *parent) : QWidget(parent) {
	ui.setupUi(this);
	ui.txtHex->setValidator(new HexStringValidator(this));
}

//------------------------------------------------------------------------------
// Name: ~BinaryString()
// Desc: destructor
//------------------------------------------------------------------------------
BinaryString::~BinaryString() {
}

//------------------------------------------------------------------------------
// Name: on_txtAscii_textEdited(const QString &text)
// Desc:
//------------------------------------------------------------------------------
void BinaryString::on_txtAscii_textEdited(const QString &text) {

	QByteArray p = text.toAscii();
	QString textHex;
	QString textUTF16;
	wchar_t utf16Char = 0;

	int counter = 0;

	QByteArray::iterator i = p.begin();
	while(i != p.end()) {
		const quint8 ch = *i++;

		textHex += QString().sprintf("%02x ", ch & 0xff);

		utf16Char = (utf16Char >> 8) | (ch << 8);

		if(counter++ & 1) {
			textUTF16 += QChar(utf16Char);
		}
	}

	textHex.remove(textHex.length() - 1, 1);

	ui.txtHex->setText(textHex);
	ui.txtUTF16->setText(textUTF16);
}

//------------------------------------------------------------------------------
// Name: on_txtUTF16_textEdited(const QString &text)
// Desc:
//------------------------------------------------------------------------------
void BinaryString::on_txtUTF16_textEdited(const QString &text) {
		
	QString textAscii;
	QString textHex;

	const QChar *p = text.constData();

	int counter = 0;
	
	while(counter != text.length()) {

		const ushort ch = p++->unicode();

		textAscii += (ch) & 0xff;
		textAscii += (ch >> 8) & 0xff;

		textHex += QString().sprintf("%02x %02x ", (ch) & 0xff, (ch >> 8) & 0xff);
		++counter;
	}

	textHex.remove(textHex.length() - 1, 1);

	ui.txtHex->setText(textHex);
	ui.txtAscii->setText(textAscii);

}

//------------------------------------------------------------------------------
// Name: on_txtHex_textEdited(const QString &text)
// Desc:
//------------------------------------------------------------------------------
void BinaryString::on_txtHex_textEdited(const QString &text) {
		
	wchar_t utf16Char = 0;
	int counter = 0;

	QString textAscii;
	QString textUTF16;

	QStringList list1 = text.split(" ", QString::SkipEmptyParts);

	QStringList::const_iterator i = list1.begin();
	while(i != list1.end()) {

		const quint8 ch = i++->toUInt(0, 16);

		utf16Char = (utf16Char >> 8) | (ch << 8);

		textAscii += ch;

		if(counter++ & 1) {
			textUTF16 += QChar(utf16Char);
		}
	}

	ui.txtUTF16->setText(textUTF16);
	ui.txtAscii->setText(textAscii);
}

//------------------------------------------------------------------------------
// Name: value() const
// Desc:
//------------------------------------------------------------------------------
QByteArray BinaryString::value() const {

	QByteArray ret;
	QStringList list1 = ui.txtHex->text().split(" ", QString::SkipEmptyParts);

	QStringList::const_iterator i = list1.begin();
	while(i != list1.end()) {
		ret += static_cast<quint8>(i++->toUInt(0, 16));
	}

	return ret;
}

//------------------------------------------------------------------------------
// Name: setValue(const QByteArray &value)
// Desc:
//------------------------------------------------------------------------------
void BinaryString::setValue(const QByteArray &value) {

	QString temp;
	
	QByteArray::const_iterator i = value.begin();
	while(i != value.end()) {
		temp += static_cast<quint8>(*i++);
	}

	ui.txtAscii->setText(temp);
	on_txtAscii_textEdited(temp);
}
