'use strict';

var Steppy = require('twostep').Steppy,
	_ = require('underscore'),
	nlevel = require('nlevel');


exports.init = function(dbPath, params, callback) {
	var ldb = nlevel.db(dbPath, params, callback);

	exports.builds = new nlevel.DocsSection(ldb, 'builds', {
		projections: [
			{key: {createDate: 1}, value: pickId},
			{key: {descCreateDate: descCreateDate, id: 1}},
			{key: {
				projectName: pickProjectName,
				descCreateDate: descCreateDate,
				id: 1
			}},
			// note that's unordered projection (coz number is numeric)
			{key: {
				projectName: pickProjectName,
				number: 1,
				id: 1
			}}
		]
	});

	exports.builds._beforePut = function(builds, callback) {
		var self = this,
			build;

		Steppy(
			function() {
				if (self._beforePutInProgress) {
					return setTimeout(function() {
						exports.builds._beforePut.call(self, builds, callback);
					}, 5);
				}

				self._beforePutInProgress = true;

				if (builds.length > 1) {
					throw new Error('Build put hooks work only with single build');
				}
				build = builds[0];

				// generate number for build
				if (!build.number && build.status === 'in-progress') {
					// find last build with number in the same project
					self.find({
						start: {projectName: build.project.name, descCreateDate: ''},
						filter: function(build) {
							return 'number' in build;
						},
						limit: 1
					}, this.slot());
				} else {
					this.pass([]);
				}

				generateIds(self, builds, this.slot());
			},
			function(err, prevBuilds) {
				var prevBuild = prevBuilds[0];
				if (!build.number && build.status === 'in-progress') {
					build.number = prevBuild ? prevBuild.number + 1 : 1;
				}

				this.pass(null);

				self._beforePutInProgress = false;
			},
			callback
		);
	};
};

function pickProjectName(build) {
	return build.project.name;
}


function generateIds(section, docs, callback) {
	Steppy(
		function() {
			var isAllDocsWithId = _(docs).all(function(doc) {
				return 'id' in doc;
			});
			if (isAllDocsWithId) {
				return callback();
			}

			var isAllDocsWithoutId = _(docs).all(function(doc) {
				return 'id' in doc === false;
			});
			if (!isAllDocsWithoutId) {
				throw new Error(
					'Documents with id and without should not be mixed'
				);
			}

			section.find({
				start: {createDate: ''}, limit: 1, reverse: true
			}, this.slot());
		},
		function(err, lastDocs) {
			var id = lastDocs[0] && ++lastDocs[0].id || 1;

			_(docs).each(function(doc) {
				doc.id = id;
				id++;
			});

			this.pass(null);
		},
		callback
	);
}

function pickId(doc) {
	return {id: doc.id};
}

// reversed date - for sorting forward (it's fatster for leveldb then
// reverse: true, see levelup reverse notes for details) but have documents
// sorted by some date in descending order
var maxTime = new Date('03:14:07 UTC 2138-01-19').getTime();

function descCreateDate(doc) {
	return maxTime - doc.createDate;
}

@@ -631,33 +631,32 @@
 %09exports.builds.
-_
 beforePut = func
@@ -740,201 +740,8 @@
 ) %7B%0A
-%09%09%09%09if (self._beforePutInProgress) %7B%0A%09%09%09%09%09return setTimeout(function() %7B%0A%09%09%09%09%09%09exports.builds._beforePut.call(self, builds, callback);%0A%09%09%09%09%09%7D, 5);%0A%09%09%09%09%7D%0A%0A%09%09%09%09self._beforePutInProgress = true;%0A%0A
 %09%09%09%09
@@ -1513,73 +1513,597 @@
 l);%0A
-%0A%09%09%09%09self._beforePutInProgress = false
+%09%09%09%7D,%0A%09%09%09callback%0A%09%09);%0A%09%7D;%0A%7D;%0A%0A/*%0A * Introduce safe %60beforePut%60 (instead of directly use %60_beforePut%60) for%0A * id generation etc%0A */%0Anlevel.DocsSection.prototype._beforePut = function(docs, callback) %7B%0A%09var self = this;%0A%09Steppy(%0A%09%09function() %7B%0A%09%09%09if (self._beforePutInProgress) %7B%0A%09%09%09%09return setTimeout(function() %7B%0A%09%09%09%09%09nlevel.DocsSection.prototype._beforePut.call(%0A%09%09%09%09%09%09self, docs, callback%0A%09%09%09%09%09)
 ;%0A%09%09%09
+%09
 %7D,
-%0A%09%09%09callback%0A%09%09);%0A%09%7D
+ 5);%0A%09%09%09%7D%0A%0A%09%09%09self._beforePutInProgress = true;%0A%0A%09%09%09self.beforePut(docs, this.slot());%0A%09%09%7D,%0A%09%09function() %7B%0A%09%09%09self._beforePutInProgress = false;%0A%09%09%09this.pass(null);%0A%09%09%7D,%0A%09%09callback%0A%09)
 ;%0A%7D;
@@ -2169,17 +2169,16 @@
 ame;%0A%7D%0A%0A
-%0A
 function

Introduce safe (instead of directly use )