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

#include "serialization/expressionserializer.h"
#include <boost/utility.hpp>
#include <boost/utility/in_place_factory.hpp>

#include <cmath>

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::NUMBER:
                        case Expression::STRING:
                        case Expression::IDENT : {

                            Index index;
                            if ((e.__state == Expression::NUMBER))
                                index = {std::to_string(e.getValueDouble()), 0, level};
                                else 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: {
                Index index;

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

                    case Expression::NUMBER: {
                        index = {std::to_string(e.getValueDouble()), 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(boost::in_place());
        //move(PackedExpression())

	strategy->pack(e, 0, result);
	assert(result);
	return move(*result);
}

OptionalPackedExpression
ExpressionSerializer::getIdOptional(const Expression& e) const{
        OptionalPackedExpression result(boost::in_place());
        //move(PackedExpression())

	strategy->pack(e, 0, result);
	return result;
}

ExpressionSerializerIntegral::ExpressionSerializerIntegral():serializer(*this){}

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 OptionalPackedExpression exprPacked = serializer.getIdOptional(e);
	assert(exprPacked);

	return __registry.at(*exprPacked);
}

boost::optional<size_t>
ExpressionSerializerIntegral::getIdOptional(const Expression& e) const{
	const OptionalPackedExpression 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");
}

	#if BOOST_VERSION <= 105500
PackedExpression::PackedExpression(const PackedExpression& other){
	__storage = other.__storage;
	size = other.size;
	countRemainedBits = other.countRemainedBits;
}
	#endif

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 */