'use strict';

var customError    = require('es5-ext/error/custom')
  , callable       = require('es5-ext/object/valid-callable')
  , normalize      = require('es5-ext/string/#/normalize')
  , d              = require('d')
  , rename         = require('fs2/rename')
  , resolve        = require('path').resolve
  , validDb        = require('dbjs/valid-dbjs')
  , typeMap        = require('./lib/type-map')
  , defNameResolve = require('./lib/resolve-file-name')

  , renameOpts = { intermediate: true }
  , defineProperties = Object.defineProperties, defineProperty = Object.defineProperty
  , nextTick = process.nextTick;

var handleError = function (err) {
	if (err.code !== 'UNSUPPORTED_FILE_TYPE') throw err;
	console.error(err.message);
};

var invokeOnUpload = function () {
	var result = this.onUpload();
	if (result && (typeof result.done === 'function')) result.done(null, handleError);
};

var scheduleOnUpload = function () {
	if (this.onUpload) nextTick(invokeOnUpload.bind(this));
};

module.exports = function (db, uploadPath/*, nameResolve*/) {
	var nameResolve = arguments[2], unserialize = validDb(db).objects.unserialize
	  , validateCreate = db.File._validateCreate_;

	uploadPath = resolve(String(uploadPath));
	nameResolve = (nameResolve != null) ? callable(nameResolve) : defNameResolve;

	defineProperties(db.File, {
		uploadsInProgress: d([]),
		_validateCreate_: d(function (file) {
			if (!file) return [file];
			if (file.ws && file.headers && file.path && file.name) {
				validateCreate.call(this);
				if (!file.name) this.prototype._validateSet_('name', file.name);
				if (!db.File.accept.has(file.type)) {
					throw customError("Unsupported file type", 'UNSUPPORTED_FILE_TYPE');
				}
				return [file];
			}
			throw new TypeError(file + " does not come from multiparty");
		})
	});

	defineProperty(db.File.prototype, '_initialize_', d(function (file) {
		var filename, path;

		if (!file) return;
		this.name = normalize.call(db.Filename.adapt(file.name));
		this.type = typeMap[file.type] || file.type;

		filename = nameResolve(this, file);
		path = resolve(uploadPath, filename);
		db.File.uploadsInProgress.push(rename(file.path, path)(function () {
			this.path = filename;
			this.diskSize = file.size;
			return this.onUpload();
		}.bind(this)));
	}));

	return {
		validate: function (data) {
			if (!data.id) throw new Error("Upload error: No id");
			if (!data.file) throw new Error("Upload error: Missing file");
			return data;
		},
		submit: function (data) {
			var path, dbFile, filename;

			dbFile = unserialize(data.id);
			if (dbFile._kind_ === 'descriptor') dbFile = dbFile.object._get_(dbFile._sKey_);
			filename = nameResolve(dbFile, data.file);
			path = resolve(uploadPath, filename);
			return rename(data.file.path, path, renameOpts)(function () {
				dbFile.path = filename;
				if ((dbFile.name !== data.file.name) || (normalize.call(dbFile.name) !== dbFile.name)) {
					dbFile.name = normalize.call(db.Filename.adapt(data.file.name));
				}
				if (dbFile.type !== data.file.type) dbFile.type = data.file.type;
				dbFile.diskSize = data.file.size;
				if (dbFile.constructor === db.Object) dbFile.once('turn', scheduleOnUpload.bind(dbFile));
				else scheduleOnUpload.call(dbFile);
				return true;
			});
		}
	};
};

@@ -583,16 +583,47 @@
 roperty%0A
+  , stringify = JSON.stringify%0A
   , next
@@ -2563,18 +2563,24 @@
 filename
+, desc
 ;%0A
-
 %0A%09%09%09dbFi
@@ -2645,16 +2645,41 @@
 iptor') 
+%7B%0A%09%09%09%09desc = dbFile;%0A%09%09%09%09
 dbFile =
@@ -2715,16 +2715,212 @@
 sKey_);%0A
+%09%09%09%09if (!dbFile) %7B%0A%09%09%09%09%09throw new Error(%22File instance cannot be resolved out of %22 +%0A%09%09%09%09%09%09stringify(desc.__valueId__) +%0A%09%09%09%09%09%09%22. Ensure needed model is defined on database instance%22);%0A%09%09%09%09%7D%0A%09%09%09%7D%0A
 %09%09%09filen

Improve error handling