/*
 * expressionserializer.cpp
 *
 *  Created on: Jan 4, 2016
 *      Author: pgess
 */

#include "serialization/expressionserializer.h"
#include <boost/multiprecision/cpp_int.hpp>
#include <boost/bimap.hpp>
#include <boost/bimap/multiset_of.hpp>
#include <boost/bimap/unordered_set_of.hpp>

using namespace std;
using namespace boost::bimaps;
namespace xreate {

struct Index {
	string name;
	size_t degree; //count of parameters
	unsigned char level; //level in expression tree (depth of tree layer)

	bool operator< (const Index other) const{
		if (name != other.name) return name < other.name;
		if (degree != other.degree) return degree < other.degree;
		if (name != other.name) return level < other.level;
		return false;
	}
};

class ExpressionSerializerPrivate {
	//boost::bimap<Index, multiset_of<size_t>> __registry;
	struct {
		map<Index,size_t> left;
	} __registry;
	map<unsigned char, size_t> __range;

public:
	void pack(const Expression& e, unsigned char level, OptionalPackedExpression& target){
		if (!target) return;
		switch (e.op){
			case Operator::NONE: {
				switch (e.__state) {
					case Expression::STRING:
					case Expression::IDENT: {
						Index index{e.getValueString(), 0, level};
						if (!__registry.left.count(index)){
							target = boost::none;
							return;
						}

						size_t id = __registry.left.at(index);
						size_t range = __range[level];
						(*target) << make_pair(id, range);
						return;
					}
					default: break;
				}
			break;
			}

			case Operator::CALL: {
				Index index{e.getValueString(), e.operands.size(), level};
				if(!__registry.left.count(index)){
					target = boost::none;
					return;
				}

				size_t id = __registry.left.at(index);
				size_t range = __range[level];
				(*target) << make_pair(id, range);

				for (const Expression& operand: e.operands){
					pack(operand, level+1, target);
				}
				return;
			}
			default: break;
		}

		assert(false && "Expression too complicate for serialization");
	}

	void registerExpression(const Expression&e, unsigned char level){
		switch (e.op){
			case Operator::CALL: {
				Index index{e.getValueString(), e.operands.size(), level};
				if (__registry.left.insert(make_pair(index, __range[level])).second){
					__range[level]++;
				}

				for (const Expression& operand: e.operands){
					registerExpression(operand, level+1);
				}
			return;
			}

			case Operator::NONE: {
				switch (e.__state) {
					case Expression::STRING:
					case Expression::IDENT: {
						Index index{e.getValueString(), 0, level};
						if (__registry.left.insert(make_pair(index, __range[level])).second){
							__range[level]++;
						}
					return;
					}
					default: break;
				}
			break;
			}

			default: break;
		}

		assert(false && "Expression too complicate for serialization");
	}
};

ExpressionSerializer::ExpressionSerializer()
	: strategy(new ExpressionSerializerPrivate()){
}

ExpressionSerializer::~ExpressionSerializer() {
	delete strategy;
}

void
ExpressionSerializer::registerExpression(const Expression&e){
	if (e.isValid())
	strategy->registerExpression(e, 0);
}

PackedExpression
ExpressionSerializer::getId(const Expression& e){
	OptionalPackedExpression result(move(PackedExpression()));
	strategy->pack(e, 0, result);
	assert(result);
	return move(*result);
}

OptionalPackedExpression
ExpressionSerializer::getIdOptional(const Expression& e) const{
	OptionalPackedExpression result(move(PackedExpression()));
	strategy->pack(e, 0, result);
	return result;
}

ExpressionSerializerIntegral::ExpressionSerializerIntegral(const std::vector<Expression>&& expressions)
	: std::vector<Expression>(move(expressions)), serializer(*this){

	size_t id =0;
	for (const Expression& e: expressions){
			__registry.emplace(serializer.getId(e), id++);
	}
}

size_t
ExpressionSerializerIntegral::size() const{
	return  PARENT::size();
}

size_t
ExpressionSerializerIntegral::count(const Expression& e) const {
	return (getIdOptional(e)? 1: 0);
}

ExpressionSerializerIntegral::const_iterator
ExpressionSerializerIntegral::begin() const {
	return PARENT::begin();
}

ExpressionSerializerIntegral::const_iterator
ExpressionSerializerIntegral::end() const {
	return PARENT::end();
}

size_t
ExpressionSerializerIntegral::getId(const Expression& e) const{
	const auto& exprPacked = serializer.getIdOptional(e);
	assert(exprPacked);

	return __registry.at(*exprPacked);
}

boost::optional<size_t>
ExpressionSerializerIntegral::getIdOptional(const Expression& e) const{
	const auto& exprPacked = serializer.getIdOptional(e);
	if (!exprPacked){
		return boost::none;
	}

	return __registry.at(*exprPacked);
}

const Expression&
ExpressionSerializerIntegral::get(size_t id) const{
	return at(id);
}


void
PackedExpression::operator<< (const std::pair<size_t, size_t>& value){
	static const size_t sizeSizeT = sizeof(size_t);

	const size_t& id = value.first;
	const size_t& range = value.second;

	int countSufficientBits =  range <=1? 0 : ceil(log2(range));

	if (0 < countRemainedBits  && countRemainedBits < countSufficientBits) {
		size_t* tail = reinterpret_cast<size_t*>(__storage + size- sizeSizeT);
		(*tail)  +=  id >> (countSufficientBits - countRemainedBits);
		countSufficientBits-=countRemainedBits;
		countRemainedBits = 0;
	}

	if (countRemainedBits == 0) {
		if (countSufficientBits == 0) return;

		char* __storageNew = new char[size+sizeSizeT];
		std::memcpy (__storageNew, __storage, size);
		std::memset(__storageNew + size, 0, sizeSizeT);

		delete[] __storage;
		__storage = __storageNew;

		size += sizeSizeT;
		countRemainedBits = 8 * sizeSizeT;
	}

	if (countRemainedBits >= countSufficientBits) {
		size_t* tail = reinterpret_cast<size_t*>(__storage + size- sizeSizeT);
		(*tail)  +=  id << (countRemainedBits - countSufficientBits);
		countRemainedBits -= countSufficientBits;
		return;
	}

	assert("Unreachable block");
}

PackedExpression::PackedExpression(PackedExpression&& other){
	__storage = other.__storage;
	size = other.size;
	countRemainedBits = other.countRemainedBits;

	other.__storage = nullptr;
}

bool
PackedExpression::operator==(const PackedExpression& other) const{
	if (size == other.size && countRemainedBits == other.countRemainedBits){
		return std::memcmp(__storage, other.__storage, size) == 0 ;
	}

	return false;
}

bool
PackedExpression::operator<(const PackedExpression& other) const{
	if (size <  other.size) { return true; }
	if (countRemainedBits < other.countRemainedBits) return true;

	if (size == other.size && countRemainedBits == other.countRemainedBits){
		return std::memcmp(__storage, other.__storage, size) < 0 ;
	}

	return false;
}

bool
PackedExpression::operator!=(const PackedExpression& other) const{
	return ! ((*this) == other);
}

PackedExpression::~PackedExpression() {
	delete[] __storage;
}

//PackedExpression::PackedExpression (const PackedExpression& other)
//	: size(other.size), countRemainedBits(other.countRemainedBits)
//{
//	__storage = new char[size];
//	std::memcpy (__storage, other.__storage, size);
//}

} /* namespace xreate */
