/*
	This file is part of cpp-ethereum.

	cpp-ethereum 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 3 of the License, or
	(at your option) any later version.

	cpp-ethereum 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 cpp-ethereum.  If not, see <http://www.gnu.org/licenses/>.
*/
/** @file TrieCommon.cpp
 * @author Gav Wood <i@gavwood.com>
 * @date 2014
 */

#include "TrieCommon.h"

namespace eth
{

/*
 * Hex-prefix Notation. First nibble has flags: oddness = 2^0 & termination = 2^1
 * NOTE: the "termination marker" and "leaf-node" specifier are completely equivalent.
 * [0,0,1,2,3,4,5]   0x10012345
 * [0,1,2,3,4,5]     0x00012345
 * [1,2,3,4,5]       0x112345
 * [0,0,1,2,3,4]     0x00001234
 * [0,1,2,3,4]       0x101234
 * [1,2,3,4]         0x001234
 * [0,0,1,2,3,4,5,T] 0x30012345
 * [0,0,1,2,3,4,T]   0x20001234
 * [0,1,2,3,4,5,T]   0x20012345
 * [1,2,3,4,5,T]     0x312345
 * [1,2,3,4,T]       0x201234
 */

std::string hexPrefixEncode(bytes const& _hexVector, bool _leaf, int _begin, int _end)
{
	uint begin = _begin;
	uint end = _end < 0 ? _hexVector.size() + 1 + _end : _end;
	bool odd = ((end - begin) % 2) != 0;

	std::string ret(1, ((_leaf ? 2 : 0) | (odd ? 1 : 0)) * 16);
	if (odd)
	{
		ret[0] |= _hexVector[begin];
		++begin;
	}
	for (uint i = begin; i < end; i += 2)
		ret += _hexVector[i] * 16 + _hexVector[i + 1];
	return ret;
}

std::string hexPrefixEncode(bytesConstRef _data, bool _leaf, int _beginNibble, int _endNibble, uint _offset)
{
	uint begin = _beginNibble + _offset;
	uint end = (_endNibble < 0 ? (_data.size() * 2 - _offset) + 1 + _endNibble : _endNibble) + _offset;
	bool odd = (end - begin) & 1;

	std::string ret(1, ((_leaf ? 2 : 0) | (odd ? 1 : 0)) * 16);
	ret.reserve((end - begin) / 2 + 1);

	uint d = odd ? 1 : 2;
	for (auto i = begin; i < end; ++i, ++d)
	{
		byte n = nibble(_data, i);
		if (d & 1)	// odd
			ret.back() |= n;		// or the nibble onto the back
		else
			ret.push_back(n << 4);	// push the nibble on to the back << 4
	}
	return ret;
}

std::string hexPrefixEncode(bytesConstRef _d1, uint _o1, bytesConstRef _d2, uint _o2, bool _leaf)
{
	uint begin1 = _o1;
	uint end1 = _d1.size() * 2;
	uint begin2 = _o2;
	uint end2 = _d2.size() * 2;

	bool odd = (end1 - begin1 + end2 - begin2) & 1;

	std::string ret(1, ((_leaf ? 2 : 0) | (odd ? 1 : 0)) * 16);
	ret.reserve((end1 - begin1 + end2 - begin2) / 2 + 1);

	uint d = odd ? 1 : 2;
	for (auto i = begin1; i < end1; ++i, ++d)
	{
		byte n = nibble(_d1, i);
		if (d & 1)	// odd
			ret.back() |= n;		// or the nibble onto the back
		else
			ret.push_back(n << 4);	// push the nibble on to the back << 4
	}
	for (auto i = begin2; i < end2; ++i, ++d)
	{
		byte n = nibble(_d2, i);
		if (d & 1)	// odd
			ret.back() |= n;		// or the nibble onto the back
		else
			ret.push_back(n << 4);	// push the nibble on to the back << 4
	}
	return ret;
}

byte uniqueInUse(RLP const& _orig, byte except)
{
	byte used = 255;
	for (unsigned i = 0; i < 17; ++i)
		if (i != except && !_orig[i].isEmpty())
		{
			if (used == 255)
				used = (byte)i;
			else
				return 255;
		}
	return used;
}

}