const ZERO = new Buffer([0]);

function SerializePrimitive (name, value) {
	return Buffer.concat([new Buffer(name), ZERO, new Buffer(JSON.stringify(value)), ZERO]);
}

function SerializeObject (name, object) {
	let bufs = [];

	for(let key in object) {
		let val = object[key];

		if(typeof val !== "object") {
			bufs.push(SerializePrimitive((name? name + "/" : "") + key, val));
		} else {
			let b = SerializeObject((name? name + "/" : "") + key, val);

			bufs.push(b);
		}
	}

	return Buffer.concat(bufs);
}

function ParsePrimitive (buf) {
	let [ name, value ] = buf.toString().split("\u0000");

	return { name: name, value: JSON.parse(value) };
}

function ParseObject (buf) {
	let obj = {};
	let pairs = buf.toString().split("\u0000");

	for(let i=0, l=buf.length;i<l;i+=2) {
		let k = pairs[i];
		let v = pairs[i + 1];
		try {
			if(k && v) InsertToObject(obj, pairs[i], JSON.parse(pairs[i + 1]));
		} catch (exc) {}
	}

	return obj;
}

function InsertToObject (obj, key, val) {
	let o = obj;
	let keys = GetKeys(key);

	for(let i=0, l=keys.length - 1;i<l;i++) {
		let k = keys[i];
		if(!k) continue;
		if(o[k] === undefined) o[k] = {};

		o = o[k];
	}

	o[keys[keys.length - 1]] = val;

	return o;
}

function GetKeys (path) {
	return path.split("/");
}

function adjust (buf, l) {
	let new_buf = [];
	let pair = true;
	for(let i=0;i<l;i++) {
		let c = buf[i];

		if(c === 0) pair = !pair;

		new_buf.push(c);
	}

	for(let i=l-1;i>=0;i--) {
		let c = new_buf[i];
		if(c === undefined) break;

		if(c === 0) break;
		new_buf.pop();
	}

	if(!pair) {
		new_buf.pop();
		for(let i=l-1;i>=0;i--) {
			let c = new_buf[i];

			if(c === 0) break;
			new_buf.pop();
		}
	}

	return new Buffer(new_buf);
}

function merge (a, b) {
	for(let i in b) {
		if(typeof b[i] === "object" && typeof a[i] === "object") {
			merge(a[i], b[i]);
			continue;
		}

		if(a[i] !== undefined) continue;

		a[i] = b[i];
	}

	return a;
}

exports.stringify = (k, v) => SerializeObject(k,v);
exports.parse	  = ParseObject;
exports.shift     = adjust;
exports.getKeys   = GetKeys;
exports.insertToObject = InsertToObject;
exports.stringifyPrimitive = (k, v) => SerializePrimitive(k, v);
exports.merge = merge;