"use strict";

// const fs = require('fs');
// const {EOL} = require('os');

let enable_logging = false;

function log(...s) {
	if (!enable_logging) {
		return;
	}
	console.log.apply(null, s);
}

function make_node(k) {
	return {
		level: k,
		// temporarily allowing having one more element before split
		keys: [],    // k node can have k-1 keys, they are sorted
		children: [] // k node can have k children
	};
};

function binary_search(arr, el) {
	if (!arr.length) {
		log('Arr is empty');
		return 0;
	}

	if (el < arr[0]) {
		log(`El ${el} is smaller than ${arr[0]}`);
		return 0;
	}

	if (el > arr[arr.length - 1]) {
		log(`El ${el} is bigger than ${arr[arr.length - 1]}`);
		return arr.length;
	}

	let i = 0;
	let j = arr.length - 1;
	let p;
	while (i < j - 1) {
		p = (i + j) >> 1; // dividing by 2 but javascript can't do it without Math.floor
		if (arr[p] <= el) {
			i = p;
		} else {
			j = p;
		}
	}

	return i+1;
}

function btree_insert(node, el) {
	log(`Adding ${el} into`, node);
	if (!node) {
		return [null, el, null];
	}

	const idx = binary_search(node.keys, el);
	log(`Got index ${idx}`);
	const child = node.children[idx];
	let result = btree_insert(child, el);
	if (!result) {
		log('No merging root from sub');
		return null; // we are all good
	} 

	let [left, root, right] = result;
	log(`Got new root ${root} and sub trees`, left, right);
	node.keys.splice(idx, 0, root);
	node.children[idx] = left;
	node.children.splice(idx+1, 0, right);

	log(`Node size: ${node.keys.length}, node level: ${node.level}`);
	if (node.keys.length > node.level) {
		let h = node.level >> 1;
		log(`Splitting`);
		log(node.keys, `h index ${h}`);
		let left = make_node(node.level);
		let right = make_node(node.level);
		let root = node.keys[h];
		console.log(`Children`, node.children);
		left.keys = node.keys.slice(0, h);
		left.children = node.children.slice(0, h+1);
		right.keys = node.keys.slice(h+1);
		right.children = node.children.slice(h+1);
		log('Result', left, root, right);
		return [left, root, right];
	} else {
		return null;
	}
}

function make_tree(t, result) {
	if (!result) {
		return t;
	}

	let [left, el, right] = result;
	let root = make_node(t.level);
	root.keys[0] = el;
	root.children[0] = left;
	root.children[1] = right;
	return root;
}

function display_tree(t, path = '') {
	if (!t) {
		return;
	}

	for (let i = 0; i < t.keys.length; i++) {
		display_tree(t.children[i], path + "/" + i);
		console.log(path + " " + t.keys[i]);
	}
	display_tree(t.children[t.keys.length], path + "/" + t.keys.length);
}

function btree_delete(t, el) {
	console.log(`Got t`, t, `, el: ${el}`);
	if (!t) {
		return t;
	}

	let idx = binary_search(t.keys, el);
	let val = t.keys[idx];
	console.log(`Got index ${idx} with value ${t.keys[idx]}`);
	if (t.keys[idx] === el || t.keys[idx-1] === el) {
		// we need to delete this element
		console.log(`Found key ${el}`);
		let idxEl = t.keys[idx] === el ? idx : idx - 1;
		console.log(`Found idx ${idxEl}`);

		if (!t.children[idx]) {
			t.keys.splice(idxEl, 1);
			t.children.splice(idxEl, 1);
		} else {
			if (t[idx-1].keys >= t.level/2) {
				// we can borrow one element from precedessing
			} else if (t[idx+1].keys >= t.level/2) {
				// we can borrow one element from successor
			} else {
				
			}
		}

		// TODO check if there are enough elements to be balanced

	} else {
		return btree_delete(t.children[idx], el);
	}
}

// let arr = [1, 3, 5, 7];

// const el = -2;
// const pos = binary_search(arr, el);
// arr.splice(pos, 0, el);
// log(`Binary search`, arr);

// let t = make_node(2);
// t = make_tree(t, btree_insert(t, 1));
// t = make_tree(t, btree_insert(t, 2));
// t = make_tree(t, btree_insert(t, 3));
// t = make_tree(t, btree_insert(t, 4));
// t = make_tree(t, btree_insert(t, 5));
// t = make_tree(t, btree_insert(t, 6));
// t = make_tree(t, btree_insert(t, 7));

// display_tree(t);
// enable_logging = true;
// t = make_tree(t, btree_insert(t, 1));
// t = make_tree(t, btree_insert(t, 2));
// t = make_tree(t, btree_insert(t, 3));
// t = make_tree(t, btree_insert(t, 4));
// t = make_tree(t, btree_insert(t, 5));
// t = make_tree(t, btree_insert(t, 6));
// t = make_tree(t, btree_insert(t, 7));
// t = make_tree(t, btree_insert(t, 8));
// t = make_tree(t, btree_insert(t, 5));
// t = make_tree(t, btree_insert(t, 5));
// console.log(t);
//enable_logging = false;
// display_tree(t);

let t = make_node(4);

enable_logging = false;
t = make_tree(t, btree_insert(t, 1));
t = make_tree(t, btree_insert(t, 2));
t = make_tree(t, btree_insert(t, 3));
t = make_tree(t, btree_insert(t, 4));
t = make_tree(t, btree_insert(t, 5));
t = make_tree(t, btree_insert(t, 6));
t = make_tree(t, btree_insert(t, 7));

enable_logging = true;
t = make_tree(t, btree_delete(t, 5));
enable_logging = false;

display_tree(t);