#include "SH_assembly.h"
#include "SH_base.h"
#include <QFileInfo>
#include "picrypt.h"
#include "piqt.h"

static QByteArray header("SHA", 3);

enum ID {
	idFileName = 1,
	idFileContent,
	idDirName,
	idResName,
};


SHAssembly::SHAssembly() {
	
}


SHAssembly::~SHAssembly() {
	clear();
}


bool SHAssembly::pack(const QString & assembly, const QStringList & files, const QStringList & res) {
	clear();
	QFile::remove(assembly);
	QFile f(assembly);
	if (!f.open(QIODevice::ReadWrite)) return false;
	QDataStream ss(&f);
	ss.writeRawData(header.constData(), header.size());
	QStringList res_dirs("res"), res_files;
	foreach (QString i, res) {
		if (i.isEmpty()) continue;
		if (i.endsWith('/')) {
			i.chop(1);
			QStringList dl, fl;
			dirContent("res/" + i, dl, fl);
			res_dirs << "res/" + i;
			foreach (QString j, dl)
				res_dirs << "res/" + i + "/" + j;
			foreach (QString j, fl)
				res_files << "res/" + i + "/" + j;
		} else
			res_files << "res/" + i;
	}
	//qDebug() << "dirs" << res_dirs;
	//qDebug() << "files" << res_files;
	ChunkStream cs;
	foreach (QString f, files) {
		if (f.isEmpty()) continue;
		QFile f_(f);
		if (!f_.open(QIODevice::ReadOnly)) continue;
		QByteArray fc = f_.readAll();
		//qDebug() << "readed" << f_.fileName() << fc.size();
		fc = qCompress(fc);
		//qDebug() << "compressed" << fc.size();
		cs.add(idFileName, QFileInfo(f).fileName()).add(idFileContent, PI2QByteArray(PICrypt::crypt(Q2PIByteArray(fc), key())));
	}
	foreach (QString d, res_dirs) {
		cs.add(idDirName, d);
	}
	foreach (QString f, res_files) {
		QFile f_(f);
		if (!f_.open(QIODevice::ReadOnly)) continue;
		QByteArray fc = f_.readAll();
		//qDebug() << "readed" << f_.fileName() << fc.size();
		fc = qCompress(fc);
		//qDebug() << "compressed" << fc.size();
		cs.add(idResName, f).add(idFileContent, PI2QByteArray(PICrypt::crypt(Q2PIByteArray(fc), key())));
	}
	//qDebug() << files;
	//qDebug() << res_dirs;
	//qDebug() << res_files;
	ss << cs.data();
	return true;
}


QStringList SHAssembly::unpack_(const QString & file, const QString & dir_to, bool remove) {
	clear();
	QStringList ret;
	QFile f(file);
	if (!f.open(QIODevice::ReadOnly)) return ret;
	QDataStream ss(&f);
	QByteArray hdr(header.size(), '\0');
	ss.readRawData(hdr.data(), header.size());
	if (hdr != header) {
		qDebug() << "[SHAssembly] Invalid header!";
		return ret;
	}
	ChunkStream cs(ss);
	QDir dir(dir_to);
	QString basedir = dir.path() + "/", fp;
	QByteArray fc;
	while (!cs.atEnd()) {
		int id = cs.read();
		switch (id) {
		case idResName:
		case idFileName:
			fp = basedir + cs.getData<QString>();
			//qDebug() << "unpack" << fp;
			if (id == idFileName)
				ret << fp;
			break;
		case idFileContent: {
			fc = cs.getData<QByteArray>();
			QFile::remove(fp);
			QFile f_(fp);
			//qDebug() << "open" << f_.fileName();
			if (!f_.open(QIODevice::ReadWrite)) continue;
			//qDebug() << "readed" << f_.fileName() << fc.size();
			fc = qUncompress(PI2QByteArray(PICrypt::decrypt(Q2PIByteArray(fc), key())));
			//qDebug() << "uncompressed" << fc.size();
			f_.write(fc);
			} break;
		case idDirName:
			dir.mkdir(cs.getData<QString>());
			break;
		default: break;
		};
	}
	if (remove) files_ = ret;
	return ret;
}


void SHAssembly::clear() {
	foreach (QString f, files_)
		QFile::remove(f);
	files_.clear();
}


const PIByteArray & SHAssembly::key() {
	static PIByteArray ret = PICrypt::hash("SHS_assembly");
	return ret;
}
