// Official roles GET controllers

'use strict';

var aFrom           = require('es5-ext/array/from')
  , ensureArray     = require('es5-ext/array/valid-array')
  , flatten         = require('es5-ext/array/#/flatten')
  , isNaturalNumber = require('es5-ext/number/is-natural')
  , toArray         = require('es5-ext/object/to-array')
  , ensureObject    = require('es5-ext/object/valid-object')
  , ensureCallable  = require('es5-ext/object/valid-callable')
  , ensureString    = require('es5-ext/object/validate-stringifiable-value')
  , memoize         = require('memoizee/plain')
  , ensureDatabase  = require('dbjs/valid-dbjs')
  , db              = require('mano').db
  , getCompare      = require('../../utils/get-compare')
  , getSearchFilter = require('../../utils/get-search-filter')
  , serializeView   = require('../../utils/db-view/serialize')
  , getEvents       = require('../../utils/dbjs-get-path-events')
  , QueryHandler    = require('../../utils/query-handler')

  , map = Array.prototype.map, ceil = Math.ceil, stringify = JSON.stringify
  , itemsPerPage = 50;

require('memoizee/ext/max-age');

var getTableQueryHandler = function (statusMap) {
	var queryHandler = new QueryHandler(exports.tableQueryConf);
	queryHandler._statusMap = statusMap;
	return queryHandler;
};

var getBusinessProcessQueryHandler = function (statusMap) {
	return new QueryHandler([
		{
			name: 'id',
			ensure: function (value) {
				var bp;
				if (!value) throw new Error("Missing id");
				bp = db.BusinessProcess.getById(value);
				if (!bp) return null;
				if (!statusMap.all.data.has(bp)) return null;
				return value;
			}
		}
	]);
};

module.exports = exports = function (data) {
	ensureObject(data);
	var roleName = ensureString(data.roleName)
	  , statusMap = ensureObject(data.statusMap)
	  , bpListProps = aFrom(data.listProperties)
	  , bpListComputedProps = data.listComputedProperties && aFrom(data.listComputedProperties)
	  , searchFilter = getSearchFilter(ensureArray(data.searchablePropertyNames))
	  , tableQueryHandler = getTableQueryHandler(statusMap)
	  , businessProcessQueryHandler = getBusinessProcessQueryHandler(statusMap)
	  , dbSubmitted = bpListComputedProps ? ensureDatabase(data.dbSubmitted) : null
	  , getOrderIndex = ensureCallable(data.getOrderIndex);

	var getTableData = memoize(function (query) {
		var list, pageCount, offset, size;
		// Status
		list = statusMap[query.status || 'all'].data;
		// Search
		if (query.search) list = list.filter(searchFilter(query.search));
		size = list.size;
		if (!size) return { size: size };
		pageCount = ceil(size / itemsPerPage);
		if (query.page > pageCount) return { size: size };
		// Sort
		list = list.toArray(getCompare(getOrderIndex));
		// Pagination
		offset = (query.page - 1) * itemsPerPage;
		list = list.slice(offset, offset + itemsPerPage);
		return {
			view: serializeView(list, getOrderIndex),
			size: size,
			data: flatten.call(map.call(list, function (object) {
				var subObject
				  , events = bpListProps.map(function (path) { return getEvents(object, path); });
				events.unshift(object._lastOwnEvent_);
				if (!dbSubmitted) return events;
				subObject = dbSubmitted.BusinessProcess.getById(object.__id__);
				return [
					events,
					bpListComputedProps.map(function (path) { return getEvents(subObject, path); })
				];
			})).map(String)
		};
	}, {
		normalizer: function (args) { return String(toArray(args[0], null, null, true)); },
		maxAge: 10 * 1000
	});

	return {
		'get-business-processes-view': function (query) {
			return getTableData(tableQueryHandler.resolve(query));
		},
		'get-business-process-data': function (query) {
			query = businessProcessQueryHandler.resolve(query);
			if (!query.id) return { passed: false };
			db.User.getById(this.req.$user).visitedBusinessProcesses[roleName]
				.add(db.BusinessProcess.getById(query.id));
			return { passed: true };
		}
	};
};

exports.tableQueryConf = [{
	name: 'status',
	ensure: function (value) {
		if (!value) return;
		if (!this._statusMap[value]) {
			throw new Error("Unreconized status value " + stringify(value));
		}
		if (value === 'all') throw new Error("Unexpected default key status");
		return value;
	}
}, { name: 'search' }, {
	name: 'page',
	ensure: function (value) {
		var num;
		if (isNaN(value)) throw new Error("Unrecognized page value " + stringify(value));
		num = Number(value);
		if (!isNaturalNumber(num)) throw new Error("Unreconized page value " + stringify(value));
		if (!num) throw new Error("Unexpected page value " + stringify(value));
		return value;
	}
}];

@@ -150,24 +150,78 @@
 lid-array')%0A
+  , find            = require('es5-ext/array/#/find')%0A
   , flatten 
@@ -261,24 +261,24 @@
 #/flatten')%0A
-
   , isNatura
@@ -1077,16 +1077,36 @@
 th.ceil,
+ keys = Object.keys,
  stringi
@@ -2354,16 +2354,101 @@
 erIndex)
+%0A%09  , statuses = keys(statusMap).filter(function (name) %7B return (name !== 'all'); %7D)
 ;%0A%0A%09var 
@@ -2520,24 +2520,32 @@
 offset, size
+, result
 ;%0A%09%09// Statu
@@ -3000,20 +3000,22 @@
 e);%0A%09%09re
-turn
+sult =
  %7B%0A%09%09%09vi
@@ -3520,13 +3520,229 @@
 ng)%0A
-
 %09%09%7D;%0A
+%09%09if (!query.status) %7B%0A%09%09%09list.forEach(function (bp) %7B%0A%09%09%09%09this%5Bbp.__id__%5D = find.call(statuses,%0A%09%09%09%09%09function (status) %7B return statusMap%5Bstatus%5D.data.has(bp); %7D);%0A%09%09%09%7D, result.statusMap = %7B%7D);%0A%09%09%7D%0A%09%09return result;%0A
 %09%7D, 

Attach statusMap in case of 'all' status