/*
 * RadixTree.h
 *
 * This source file is part of the FoundationDB open source project
 *
 * Copyright 2013-2018 Apple Inc. and the FoundationDB project authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef FLOW__RADIXTREE_H
#define FLOW__RADIXTREE_H
#pragma once

#include <cassert>
#include <string>
#include <utility>
#include <vector>
#include <iostream>
#include <functional>
#include <map>
#include <stdexcept>

#include "fdbserver/IKeyValueContainer.h"
#include "flow/Arena.h"

// forward declaration
const int LEAF_BYTE = -1;
const int INLINE_KEY_SIZE = sizeof(StringRef);

StringRef radix_substr(const StringRef& key, int begin, int num) {
	int size = key.size();
	if (begin > size) {
		throw std::out_of_range("out of range in radix_substr<StringRef>");
	}
	if ((begin + num) > size) {
		num = size - begin;
	}
	return key.substr(begin, num);
}

StringRef radix_join(const StringRef& key1, const StringRef& key2, Arena& arena) {
	int rsize = key1.size() + key2.size();
	uint8_t* s = new (arena) uint8_t[rsize];

	memcpy(s, key1.begin(), key1.size());
	memcpy(s + key1.size(), key2.begin(), key2.size());

	return StringRef(s, rsize);
}

StringRef radix_constructStr(const StringRef& key, int begin, int num, Arena& arena) {
	int size = key.size();
	if (begin > size) {
		throw std::out_of_range("out of range in radix_substr<StringRef>");
	}
	if ((begin + num) > size) {
		num = size - begin;
	}
	return StringRef(arena, key.substr(begin, num));
}

class radix_tree {
public:
	typedef std::size_t size_type;

private:
	// union used inside both base node and leaf node
	union inlineUnion {
		inlineUnion() : data() {} // constructor

		uint8_t inlineData[INLINE_KEY_SIZE];
		StringRef data;
	};

	struct node {
		// constructor for all kinds of node (root/internal/leaf)
		node() : m_is_leaf(0), m_is_inline(0), m_inline_length(0), m_depth(0), key(), arena(), m_parent(nullptr) {}

		node(const node&) = delete; // delete
		node& operator=(const node& other) {
			m_is_leaf = other.m_is_leaf;
			m_is_inline = other.m_is_inline;
			m_inline_length = other.m_inline_length;
			m_depth = other.m_depth;
			memcpy(key.inlineData, other.key.inlineData, INLINE_KEY_SIZE);
			arena = other.arena;
			m_parent = other.m_parent;

			return *this;
		}

		void setKey(const StringRef& content, int start, int num) {
			bool isInline = num <= INLINE_KEY_SIZE;
			if (isInline) {
				memcpy(key.inlineData, content.begin() + start, num);
				m_inline_length = num;
				if (!m_is_inline)
					arena = Arena();
			} else {
				Arena new_arena(num);
				key.data = radix_constructStr(content, start, num, new_arena);
				arena = new_arena;
			}
			m_is_inline = isInline;
		}

		StringRef getKey() const {
			if (m_is_inline) {
				return StringRef(&key.inlineData[0], m_inline_length);
			} else {
				return key.data;
			}
		}

		inline int getKeySize() const { return m_is_inline ? m_inline_length : key.data.size(); }

		inline int16_t getFirstByte() const {
			if (m_is_inline) {
				return m_inline_length == 0 ? LEAF_BYTE : key.inlineData[0];
			} else {
				return key.data.size() == 0 ? LEAF_BYTE : key.data[0];
			}
		}

		inline size_type getArenaSize() const { return m_is_inline ? 0 : arena.getSize(); }

		uint32_t m_is_leaf : 1;
		uint32_t m_is_fixed : 1; // if true, then we have fixed number of children (3)
		uint32_t m_is_inline : 1;
		uint32_t m_inline_length : 4;
		// m_depth can be seen as common prefix length with your ancestors
		uint32_t m_depth : 25;
		// key is the prefix, a substring that shared by your children
		inlineUnion key;
		// arena assign memory for key
		Arena arena;
		node* m_parent;
	};

	struct leafNode : FastAllocated<leafNode> {
		leafNode(const StringRef& content) : base(), is_inline(0), inline_length(0), arena() {
			base.m_is_leaf = 1;
			setValue(content);
		}

		~leafNode() = default;

		void setValue(const StringRef& content) {
			bool isInline = content.size() <= INLINE_KEY_SIZE;
			if (isInline) {
				memcpy(value.inlineData, content.begin(), content.size());
				inline_length = content.size();
				if (!is_inline)
					arena = Arena();
			} else {
				Arena new_arena(content.size());
				value.data = StringRef(new_arena, content);
				arena = new_arena;
			}
			is_inline = isInline;
		}

		StringRef getValue() {
			if (is_inline) {
				return StringRef(&value.inlineData[0], inline_length);
			} else {
				return value.data;
			}
		}

		inline size_type getLeafArenaSize() { return is_inline ? 0 : arena.getSize(); }

		node base; // 32 bytes

		uint32_t is_inline : 1;
		uint32_t inline_length : 31;
		inlineUnion value; // using the same data structure to store value
		Arena arena;
	};

	struct internalNode : FastAllocated<internalNode> {
		internalNode() : base(), m_children(std::vector<std::pair<int16_t, node*>>()) {
			m_children.reserve(4);
			base.m_is_fixed = 0;
		}

		~internalNode() {
			for (auto it = 0; it < m_children.size(); ++it) {
				node* current = m_children[it].second;
				if (current->m_is_leaf) {
					delete (leafNode*)current;
				} else {
					current->m_is_fixed ? delete (internalNode4*)current : delete (internalNode*)current;
				}
			}
			m_children.clear();
		}

		node base;
		// ordered map by char, m_children.begin() return the smallest value
		std::vector<std::pair<int16_t, node*>> m_children;
	};

	struct internalNode4 : FastAllocated<internalNode4> {
		internalNode4() : base(), num_children(0) {
			base.m_is_fixed = 1;
			memset(keys, 0, sizeof(keys));
			memset(m_children, 0, sizeof(m_children));
		}

		~internalNode4() { num_children = 0; }

		node base;
		int16_t num_children;
		int16_t keys[3];
		node* m_children[3];
	};

public:
	class iterator : public std::iterator<std::forward_iterator_tag, std::pair<StringRef, StringRef>> {
	public:
		node* m_pointee;

		iterator() : m_pointee(nullptr) {}
		iterator(const iterator& r) : m_pointee(r.m_pointee) {}
		iterator(node* p) : m_pointee(p) {}
		iterator& operator=(const iterator& r) {
			m_pointee = r.m_pointee;
			return *this;
		}
		~iterator() = default;

		const iterator& operator++();
		const iterator& operator--();
		bool operator!=(const iterator& lhs) const;
		bool operator==(const iterator& lhs) const;
		StringRef getKey(uint8_t* content) const;
		StringRef getValue() const {
			ASSERT(m_pointee->m_is_leaf);
			return ((leafNode*)m_pointee)->getValue();
		}

	private:
		node* increment(node* target) const;
		node* decrement(node* target) const;
	};

	explicit radix_tree() : m_size(0), m_node(0), inline_keys(0), total_bytes(0), m_root(nullptr) {}

	~radix_tree() {}

	radix_tree(const radix_tree& other) = delete; // delete
	radix_tree& operator=(const radix_tree other) = delete; // delete

	inline std::tuple<size_type, size_type, size_type> size() const {
		return std::make_tuple(m_size, m_node, inline_keys);
	}

	// Return the amount of memory used by an entry in the RadixTree
	static int getElementBytes(node* node) {
		int result = 0;
		if (node->m_is_leaf) {
			result = sizeof(leafNode) + ((leafNode*)node)->getLeafArenaSize();
		} else if (node->m_is_fixed) {
			result = sizeof(internalNode4);
		} else {
			ASSERT(!node->m_is_fixed);
			result = sizeof(internalNode);
		}
		return result;
	}

	// dummy method interface(to keep every interface same as IndexedSet )
	static int getElementBytes() {
		ASSERT(false);
		return 0;
	}

	bool empty() const { return m_size == 0; }

	void clear() {
		if (m_root != nullptr) {
			delete (internalNode*)m_root;
			m_root = nullptr;
		}
		m_size = 0;
		m_node = 0;
		inline_keys = 0;
		total_bytes = 0;
	}
	// iterators
	iterator find(const StringRef& key);
	iterator begin();
	iterator end() const;
	iterator previous(iterator i);
	// modifications
	std::pair<iterator, bool> insert(const StringRef& key, const StringRef& val, bool replaceExisting = true);
	int insert(const std::vector<std::pair<KeyValueMapPair, uint64_t>>& pairs, bool replaceExisting = true) {
		// dummy method interface(to keep every interface same as IndexedSet )
		ASSERT(false);
		return 0;
	}
	void erase(iterator it);
	void erase(iterator begin, iterator end);
	// lookups
	iterator lower_bound(const StringRef& key);
	iterator upper_bound(const StringRef& key);
	// access
	uint64_t sumTo(iterator to) const;

private:
	size_type m_size;
	// number of nodes that has been created
	size_type m_node;
	// number of nodes with key.size() <= 12
	size_type inline_keys;
	uint64_t total_bytes;
	node* m_root;

	// modification
	void add_child(node* parent, node* child);
	void add_child_vector(node* parent, node* child);
	void add_child4(node* parent, node* child);
	void delete_child(node* parent, node* child);
	void delete_child_vector(node* parent, node* child);
	void delete_child4(node* parent, node* child);
	// access
	static int find_child(node* parent, int16_t ch); // return index
	static int child_size(node* parent); // how many children does parent node have
	static node* get_child(node* parent, int index); // return node pointer

	// direction 0 = left, 1 = right
	template <int reverse>
	static node* descend(node* i) {
		while (!i->m_is_leaf) {
			ASSERT(child_size(i) != 0);
			if (reverse) {
				i = get_child(i, child_size(i) - 1);
			} else {
				i = get_child(i, 0);
			}
		}
		return i;
	}

	node* find_node(const StringRef& key, node* node, int depth);
	node* append(node* parent, const StringRef& key, const StringRef& val);
	node* prepend(node* node, const StringRef& key, const StringRef& val);
	bool erase(node* child);
	iterator lower_bound(const StringRef& key, node* node);
	iterator upper_bound(const StringRef& key, node* node);
};
/////////////////////// iterator //////////////////////////
void radix_tree::add_child(node* parent, node* child) {
	if (parent->m_is_fixed) {
		add_child4(parent, child);
	} else {
		add_child_vector(parent, child);
	}
}

void radix_tree::add_child4(node* parent, node* child) {
	int16_t ch = child->getFirstByte();
	internalNode4* parent_ref = (internalNode4*)parent;
	int i = 0;

	for (; i < parent_ref->num_children; ++i) {
		if (parent_ref->keys[i] >= ch)
			break;
	}

	if (!parent_ref->num_children) {
		// empty
		parent_ref->num_children++;
		parent_ref->keys[0] = ch;
		parent_ref->m_children[0] = child;
		// DEBUG
		total_bytes += getElementBytes(child) + child->getArenaSize();
	} else if (i >= 0 && i < parent_ref->num_children && parent_ref->keys[i] == ch) {
		// replace
		node* original = parent_ref->m_children[i];
		total_bytes -= (getElementBytes(original) + original->getArenaSize());
		parent_ref->m_children[i] = child;
		total_bytes += getElementBytes(child) + child->getArenaSize();
	} else if (parent_ref->num_children < 3) {
		// Shift to make room
		memmove(parent_ref->keys + i + 1, parent_ref->keys + i, (parent_ref->num_children - i) * sizeof(int16_t));
		memmove(
		    parent_ref->m_children + i + 1, parent_ref->m_children + i, (parent_ref->num_children - i) * sizeof(void*));

		// Insert element
		parent_ref->keys[i] = ch;
		parent_ref->m_children[i] = child;
		parent_ref->num_children++;
		// DEBUG
		total_bytes += getElementBytes(child) + child->getArenaSize();
	} else {
		ASSERT(parent_ref->num_children >= 3);

		internalNode* new_node = new radix_tree::internalNode();
		new_node->base = parent_ref->base; // equal operator
		for (int index = 0; index < parent_ref->num_children; index++) {
			new_node->m_children.emplace_back(parent_ref->keys[index], parent_ref->m_children[index]);
			parent_ref->m_children[index]->m_parent = (node*)new_node;
		}
		// Insert new element
		new_node->m_children.insert(new_node->m_children.begin() + i, std::make_pair(ch, child));
		child->m_parent = (node*)new_node;
		// update parent info
		add_child(new_node->base.m_parent, (node*)new_node);
		// DEBUG
		total_bytes += new_node->m_children.size() * sizeof(std::pair<int16_t, void*>) + getElementBytes(child) +
		               child->getArenaSize();
		delete parent_ref;
	}
}

void radix_tree::add_child_vector(node* parent, node* child) {
	int16_t ch = child->getFirstByte();
	internalNode* parent_ref = (internalNode*)parent;
	int i = 0;

	for (; i < parent_ref->m_children.size(); ++i) {
		if (parent_ref->m_children[i].first >= ch)
			break;
	}

	if (parent_ref->m_children.empty() || i == parent_ref->m_children.size() || parent_ref->m_children[i].first > ch) {
		parent_ref->m_children.insert(parent_ref->m_children.begin() + i, std::make_pair(ch, child));
		// DEBUG
		total_bytes += getElementBytes(child) + child->getArenaSize() + sizeof(std::pair<int16_t, void*>);
	} else {
		ASSERT(parent_ref->m_children[i].first == ch);
		// replace with the new child
		node* original = parent_ref->m_children[i].second;
		total_bytes -= (getElementBytes(original) + original->getArenaSize());
		parent_ref->m_children[i] = std::make_pair(ch, child); // replace with the new child
		total_bytes += getElementBytes(child) + child->getArenaSize();
	}
}

void radix_tree::delete_child(radix_tree::node* parent, radix_tree::node* child) {
	if (parent->m_is_fixed) {
		delete_child4(parent, child);
	} else {
		delete_child_vector(parent, child);
	}
}

void radix_tree::delete_child4(radix_tree::node* parent, radix_tree::node* child) {
	int16_t ch = child->getFirstByte();
	internalNode4* parent_ref = (internalNode4*)parent;
	int i = 0;

	for (; i < parent_ref->num_children; i++) {
		if (parent_ref->keys[i] == ch)
			break;
	}
	ASSERT(i != parent_ref->num_children);
	memmove(parent_ref->keys + i, parent_ref->keys + i + 1, (parent_ref->num_children - 1 - i) * sizeof(int16_t));
	memmove(
	    parent_ref->m_children + i, parent_ref->m_children + i + 1, (parent_ref->num_children - 1 - i) * sizeof(void*));
	parent_ref->num_children--;
	total_bytes -= (getElementBytes(child) + child->getArenaSize());
}

void radix_tree::delete_child_vector(radix_tree::node* parent, radix_tree::node* child) {
	int16_t ch = child->getFirstByte();
	internalNode* parent_ref = (internalNode*)parent;
	int i = 0;

	for (; i < parent_ref->m_children.size(); i++) {
		if (parent_ref->m_children[i].first == ch)
			break;
	}
	ASSERT(i != parent_ref->m_children.size());
	parent_ref->m_children.erase(parent_ref->m_children.begin() + i);
	total_bytes -= (getElementBytes(child) + child->getArenaSize() + sizeof(std::pair<int16_t, void*>));
	if (parent_ref->m_children.size() && parent_ref->m_children.size() <= parent_ref->m_children.capacity() / 4)
		parent_ref->m_children.shrink_to_fit();
}

int radix_tree::find_child(radix_tree::node* parent, int16_t ch) {
	int i = 0;
	if (parent->m_is_fixed) {
		internalNode4* parent_ref = (internalNode4*)parent;
		for (; i < parent_ref->num_children; ++i) {
			if (parent_ref->keys[i] == ch)
				return i;
		}
	} else {
		internalNode* parent_ref = (internalNode*)parent;
		for (; i != parent_ref->m_children.size(); ++i) {
			if (parent_ref->m_children[i].first == ch)
				return i;
		}
	}
	return i;
}

int radix_tree::child_size(radix_tree::node* parent) {
	if (parent->m_is_fixed) {
		return ((internalNode4*)parent)->num_children;
	} else {
		return ((internalNode*)parent)->m_children.size();
	}
}

radix_tree::node* radix_tree::get_child(node* parent, int index) {
	if (parent->m_is_fixed) {
		ASSERT(index < ((internalNode4*)parent)->num_children);
		return ((internalNode4*)parent)->m_children[index];
	} else {
		return ((internalNode*)parent)->m_children[index].second;
	}
}

radix_tree::node* radix_tree::iterator::increment(node* target) const {
	radix_tree::node* parent = target->m_parent;
	if (parent == nullptr)
		return nullptr;

	int index = find_child(parent, target->getFirstByte());
	ASSERT(index != child_size(parent));
	++index;

	if (index == child_size(parent))
		return increment(target->m_parent);
	else
		return descend<0>(get_child(parent, index));
}

radix_tree::node* radix_tree::iterator::decrement(radix_tree::node* target) const {
	radix_tree::node* parent = target->m_parent;
	if (parent == nullptr)
		return nullptr;

	int index = find_child(parent, target->getFirstByte());
	ASSERT(index != child_size(parent));

	if (index == 0)
		return decrement(target->m_parent);
	else {
		--index;
		return descend<1>(get_child(parent, index));
	}
}

bool radix_tree::iterator::operator!=(const radix_tree::iterator& lhs) const {
	return m_pointee != lhs.m_pointee;
}

bool radix_tree::iterator::operator==(const radix_tree::iterator& lhs) const {
	return m_pointee == lhs.m_pointee;
}

const radix_tree::iterator& radix_tree::iterator::operator++() {
	if (m_pointee != nullptr) // it is undefined behaviour to dereference iterator that is out of bounds...
		m_pointee = increment(m_pointee);
	return *this;
}

const radix_tree::iterator& radix_tree::iterator::operator--() {
	if (m_pointee != nullptr && m_pointee->m_is_leaf) {
		m_pointee = decrement(m_pointee);
	}
	return *this;
}

/*
 * reconstruct the key
 */
StringRef radix_tree::iterator::getKey(uint8_t* content) const {
	if (m_pointee == nullptr)
		return StringRef();

	ASSERT(m_pointee->m_is_leaf);
	//  memset(content, 0, len);

	auto node = m_pointee;
	uint32_t pos = m_pointee->m_depth;
	while (true) {
		memcpy(content + pos, node->getKey().begin(), node->getKeySize());
		node = node->m_parent;
		if (node == nullptr || pos <= 0)
			break;
		pos -= node->getKeySize();
	}
	return StringRef(content, (m_pointee->m_depth + m_pointee->getKeySize()));
}

radix_tree::iterator radix_tree::end() const {
	return iterator(nullptr);
}

radix_tree::iterator radix_tree::begin() {
	if (m_root == nullptr || m_size == 0)
		return iterator(nullptr);
	else {
		return descend<0>(m_root);
	}
}

/////////////////////// lookup //////////////////////////
radix_tree::iterator radix_tree::find(const StringRef& key) {
	if (m_root == nullptr)
		return iterator(nullptr);

	auto node = find_node(key, m_root, 0);
	StringRef key_sub = radix_substr(key, node->m_depth, (key.size() - node->m_depth));

	if (node->m_is_leaf && key_sub == node->getKey())
		return node;
	else
		return nullptr;
}

/*
 * corner case : insert "apache, append", then search for "appends". find_node() will return leaf node with m_key ==
 * "pend"; if search for "ap", find_node() will return internal node with m_key = ap
 */
radix_tree::node* radix_tree::find_node(const StringRef& key, node* node, int depth) {
	if (node->m_is_leaf)
		return node;

	int size = child_size(node);
	//	printf("try to find key %s on node %s [%d]\n", printable(key).c_str(), printable(node->getKey()).c_str(), size);
	for (int it = 0; it < size; ++it) {
		auto current = get_child(node, it);
		// for leaf node with empty key, exact match
		if (depth == key.size() && current->getKeySize() == 0) {
			ASSERT(current->m_is_leaf); // find the exact match
			return current;
		}
		// they have at least one byte in common
		if (depth < key.size() && key[depth] == current->getFirstByte()) {
			int len_node = current->getKeySize();
			StringRef key_sub = radix_substr(key, depth, len_node);

			if (key_sub == current->getKey()) {
				if (current->m_is_leaf)
					return current;
				else
					return find_node(key, current, depth + len_node);
			} else {
				// return the current match (which is the smallest match)
				// radix tree won't have siblings that share the same prefix
				return current;
			}
		}
	}

	return node;
}

/*
 * Returns the smallest node x such that *x>=key, or end()
 */
radix_tree::iterator radix_tree::lower_bound(const StringRef& key) {
	if (m_root == nullptr || m_size == 0)
		return iterator(nullptr);
	return lower_bound(key, m_root);
}

radix_tree::iterator radix_tree::lower_bound(const StringRef& key, node* node) {
	iterator result(nullptr);
	int size = child_size(node);

	for (int it = 0; it < size; ++it) {
		auto current = get_child(node, it);
		// short cut as find_node
		if (key.size() == current->m_depth && current->getKeySize() == 0) {
			return iterator(current);
		}

		StringRef key_sub = radix_substr(key, current->m_depth, current->getKeySize());
		StringRef node_data = current->getKey();

		if (key_sub == node_data) {
			if (current->m_is_leaf && ((key.size() - current->m_depth) == current->getKeySize()))
				return iterator(current); // exact match
			else if (!current->m_is_leaf)
				result = lower_bound(key, current);
		} else if (node_data > key_sub) {
			return descend<0>(current);
		}
		if (result != end())
			return result;
	}

	return result;
}

/*
 * Returns the smallest x such that *x>key, or end()
 */
radix_tree::iterator radix_tree::upper_bound(const StringRef& key) {
	if (m_root == nullptr || m_size == 0)
		return iterator(nullptr);
	return upper_bound(key, m_root);
}

radix_tree::iterator radix_tree::upper_bound(const StringRef& key, node* node) {
	if (node == nullptr || node->m_is_leaf)
		return iterator(node);

	iterator result(nullptr);
	int size = child_size(node);

	for (int it = 0; it < size; ++it) {
		auto current = get_child(node, it);
		StringRef key_sub = radix_substr(key, current->m_depth, current->getKeySize());
		StringRef node_data = current->getKey();

		if (!current->m_is_leaf && node_data == key_sub) {
			result = upper_bound(key, current);
		} else if (node_data > key_sub) {
			return descend<0>(current);
		}
		if (result != end())
			return result;
	}
	return result;
}

// Return the sum of getT(x) for begin()<=x<to
uint64_t radix_tree::sumTo(iterator to) const {
	if (to == end()) {
		return m_root ? total_bytes : 0;
	} else {
		throw std::invalid_argument("sumTo method only support end() input");
	}
}

radix_tree::iterator radix_tree::previous(radix_tree::iterator i) {
	if (i == end()) {
		// for iterator == end(), find the largest element
		return descend<1>(m_root);
	} else if (i == begin()) {
		return iterator(nullptr);
	} else {
		--i;
		return i;
	}
}

/////////////////////// modification //////////////////////////
/*
 * @param parent : direct parent of this newly inserted node
 * @param val : using val to create a newly inserted node
 */
radix_tree::node* radix_tree::append(node* parent, const StringRef& key, const StringRef& val) {
	int depth = parent->m_depth + parent->getKeySize();
	int len = key.size() - depth;

	radix_tree::node* node_c = (node*)new radix_tree::leafNode(val);
	node_c->m_depth = depth;
	node_c->m_parent = parent;

	if (len == 0) {
		// node_c->key is empty (len = 0);
		inline_keys++;
	} else {
		node_c->setKey(key, depth, len);
		// DEBUG
		if (len <= INLINE_KEY_SIZE)
			inline_keys++;
	}
	// printf("node_c key is %s value is %s %p\n", printable(node_c->getKey()).c_str(),
	// printable(((leafNode*)node_c)->getValue()).c_str(), node_c);
	add_child(parent, node_c);
	m_node++;
	return node_c;
}

/*
 * step one : find common substring of node->m_key and val(findnode() method has already guaranteed that they have
 * something in common) step two : split the existing node into two based on the common substring step three : append
 * newly inserted node to node_a
 *
 * @param node : split node
 * @param val : using val to create a newly inserted node
 */
radix_tree::node* radix_tree::prepend(node* split, const StringRef& key, const StringRef& val) {
	int len1 = split->getKeySize();
	int len2 = key.size() - split->m_depth;
	int count = 0;
	// deep copy original data using a temp_arena(becomes invalid once out)
	Arena temp_arena(split->getKeySize());
	StringRef original_data(temp_arena, split->getKey());

	for (; count < len1 && count < len2; count++) {
		if (!(original_data[count] == key[count + split->m_depth]))
			break;
	}
	ASSERT(count != 0);

	// create a new internal node
	node* node_a = (node*)new radix_tree::internalNode4();
	m_node++;

	node_a->m_parent = split->m_parent;
	node_a->setKey(original_data, 0, count);
	node_a->m_depth = split->m_depth;
	add_child(node_a->m_parent, node_a); // replace original node* with node_a*

	// DEBUG
	if (count <= INLINE_KEY_SIZE)
		inline_keys++;
	if (split->getKeySize() > INLINE_KEY_SIZE && (len1 - count) <= INLINE_KEY_SIZE)
		inline_keys++;

	// modify original internal node
	split->m_depth += count;
	split->m_parent = node_a;
	split->setKey(original_data, count, len1 - count);
	add_child(node_a, split);
	return append(node_a, key, val);
}

std::pair<radix_tree::iterator, bool> radix_tree::insert(const StringRef& key,
                                                         const StringRef& val,
                                                         bool replaceExisting) {
	if (m_root == nullptr) {
		m_root = (node*)new radix_tree::internalNode();
		total_bytes += getElementBytes(m_root);
	}

	auto node = find_node(key, m_root, 0);
	// short cut for root node
	if (node == m_root) {
		m_size++;
		return std::pair<iterator, bool>(append(m_root, key, val), true);
	}

	StringRef key_sub = radix_substr(key, node->m_depth, node->getKeySize());
	if (key_sub == node->getKey()) {
		if (node->m_is_leaf) {
			if (node->m_depth + node->getKeySize() == key.size()) {
				// case one : exact match, replace with new value
				bool inserted = false;
				if (replaceExisting) {
					size_type original_value = ((leafNode*)node)->getLeafArenaSize();
					((leafNode*)node)->setValue(val);
					total_bytes += ((leafNode*)node)->getLeafArenaSize() - original_value;
					inserted = true;
				}
				return std::pair<iterator, bool>(node, inserted);
			} else {
				// case two : prepend (e.g leaf is "a", inserted key is "ab");
				m_size++;
				return std::pair<iterator, bool>(prepend(node, key, val), true);
			}
		} else {
			m_size++;
			return std::pair<iterator, bool>(append(node, key, val), true);
		}
	} else {
		m_size++;
		return std::pair<iterator, bool>(prepend(node, key, val), true);
	}
}

void radix_tree::erase(iterator it) {
	erase(it.m_pointee);
}

bool radix_tree::erase(radix_tree::node* child) {
	if (m_root == nullptr)
		return false;
	ASSERT(child != nullptr);

	if (!child->m_is_leaf)
		return false;

	radix_tree::node* parent;

	parent = child->m_parent;
	delete_child(parent, child);
	// DEBUG
	if (child->getKeySize() <= INLINE_KEY_SIZE)
		inline_keys--;
	delete (leafNode*)child;
	m_size--;
	m_node--;

	// can't do the merge if parent is root node
	if (parent == m_root)
		return true;

	if (child_size(parent) > 1)
		return true;
	ASSERT(child_size(parent) == 1);

	// parent has only one child left, merge parent with the sibling
	node* brother = get_child(parent, 0);

	// DEBUG
	if (brother->getKeySize() <= INLINE_KEY_SIZE)
		inline_keys--;
	delete_child(parent, brother);

	Arena temp_arena;
	StringRef new_data = radix_join(parent->getKey(), brother->getKey(), temp_arena);
	brother->setKey(new_data, 0, new_data.size());
	brother->m_depth = parent->m_depth;
	brother->m_parent = parent->m_parent;
	// delete parent and replace with brother
	add_child(parent->m_parent, brother);
	// DEBUG
	if (brother->getKeySize() <= INLINE_KEY_SIZE)
		inline_keys++;
	if (parent->getKeySize() <= INLINE_KEY_SIZE)
		inline_keys--;

	parent->m_is_fixed ? delete (internalNode4*)parent : delete (internalNode*)parent;
	m_node--;

	return true;
}

// Erase the items in the indicated range.
void radix_tree::erase(radix_tree::iterator begin, radix_tree::iterator end) {
	std::vector<radix_tree::node*> node_set;
	for (auto it = begin; it != end; ++it) {
		node_set.push_back(it.m_pointee);
	}

	for (int i = 0; i < node_set.size(); ++i) {
		erase(node_set[i]);
	}
}

#endif
