#include "chunkstream.h"


void ChunkStream::setSource(const QByteArray & data) {
	stream_.setVersion(QDataStream::Qt_4_8);
	data_ = const_cast<QByteArray *>(&data);
	_init();
}


void ChunkStream::setSource(QByteArray * data) {
	stream_.setVersion(QDataStream::Qt_4_8);
	data_ = (data ? data : &tmp_data);
	_init();
}


void ChunkStream::setSource(QDataStream & str) {
	stream_.setVersion(QDataStream::Qt_4_8);
	str >> tmp_data;
	data_ = &tmp_data;
	_init();
}


int ChunkStream::read() {
	switch (version_) {
	case Version_1: stream_ >> last_id >> last_data; break;
	case Version_2:
		last_id = readVInt(stream_);
		last_data.resize(readVInt(stream_));
		// piCout << last_id << last_data.size();
		stream_.readRawData(last_data.data(), last_data.size());
		break;
	default: break;
	}
	return last_id;
}


void ChunkStream::readAll() {
	data_map.clear();
	while (!atEnd()) {
		read();
		data_map[last_id] = last_data;
	}
}


ChunkStream::~ChunkStream() {}


void ChunkStream::_init() {
	last_id = -1;
	last_data.clear();
	buffer.close();
	buffer.setBuffer(data_);
	buffer.open(QIODevice::ReadWrite);
	stream_.setDevice(&buffer);
	if (!data_->isEmpty()) {
		uchar v = data_->at(0);
		if ((v & 0x80) == 0x80) {
			v &= 0x7f;
			switch (v) {
			case 2:
				version_ = (uchar)Version_2;
				stream_.skipRawData(1);
				break;
			default: version_ = Version_1; break;
			}
		} else
			version_ = Version_1;
	}
}


uint ChunkStream::readVInt(QDataStream & s) {
	if (s.atEnd()) return 0;
	uchar bytes[4];
	s >> bytes[0];
	uchar abc = 0;
	if (bytes[0] == 0xf0) {
		abc = 3;
		for (int i = 0; i < 4; ++i)
			s >> bytes[i];
	} else {
		for (abc = 0; abc < 3; ++abc) {
			uchar mask = (0x80 >> abc);
			if ((bytes[0] & mask) == mask) {
				// if (s.isEmpty()) return 0;
				bytes[0] &= (mask - 1);
				s >> bytes[abc + 1];
			} else
				break;
		}
	}
	uint ret = 0;
	for (int i = 0; i <= abc; ++i) {
		ret += (bytes[i] << (8 * ((int)abc - i)));
	}
	return ret;
}


void ChunkStream::writeVInt(QDataStream & s, uint val) {
	if (val <= 0x7f) {
		s << uchar(val);
	} else if (val <= 0x3fff) {
		s << uchar((val >> 8) | 0x80) << uchar(val & 0xff);
	} else if (val <= 0x1fffff) {
		s << uchar((val >> 16) | 0xc0) << uchar((val >> 8) & 0xff) << uchar(val & 0xff);
	} else if (val <= 0xfffffff) {
		s << uchar((val >> 24) | 0xe0) << uchar((val >> 16) & 0xff) << uchar((val >> 8) & 0xff) << uchar(val & 0xff);
	} else {
		s << uchar(0xf0) << uchar((val >> 24) & 0xff) << uchar((val >> 16) & 0xff) << uchar((val >> 8) & 0xff) << uchar(val & 0xff);
	}
}
