(function () {
    var moduleName = 'claromentis.project.tasks_util';
    var module;

    try {
        module = angular.module(moduleName);
    } catch (err) {
        // named module does not exist, so create one
        module = angular.module(moduleName, []);
    }

    module
        .factory('Task', ['$http', taskModelFactory]);

	function taskModelFactory($http)
	{
		var Task = function()
		{
		};

        Task.prototype =
            {
                // Constant to mean 'unsorted' at the end of the list
                DISPLAY_ORDER_NONE: 1e6,
                STATUS_TODO: -1,
                STATUS_COMPLETED: -2,

                restUrl: '/api/projects/task/',

                id: 0,
                project_id: 0,
                title: '',
                description: '',
                due_date: null,
                due_date_formatted: '',
                section_id: 0,
                is_private: false,
                created_by:
                {
                    id: null,
                    name: '',
                    profile_url: '#'
                },
                assignees: [],
                priority: '0',
                is_complete: false,
                date_completed: null,
                duration: '',
                duration_units: '',
                comments_count: 0,
                has_file_storage: false,
                orders: {
                    task: this.DISPLAY_ORDER_NONE,
                    board: this.DISPLAY_ORDER_NONE,
                    board_no_list: this.DISPLAY_ORDER_NONE,
                    my_tasks: this.DISPLAY_ORDER_NONE
                },
                status_id: 0,
                status_list: [],
                source: null,

			    control: {}, // datepicker

                // Using is_complete directly for list visibility and checkbox model causes issues with click events (the model is updated, then visibility assessed and if not visible, the ng-click callback is never run).
                // So use this second copy for visibility that gets updated when the server responds.
                is_complete_server: true,

                tasks: [],
                new_task: new Task(),

			loadFromArray: function(data)
			{
				this.id = data.id;
				this.project_id = data.project_id;
				this.title = data.title;
                this.description = data.description;
                this.due_date = data.due_date;
				this.due_date_formatted = data.due_date_formatted;
				this.due_date_type = data.due_date_type;
                this.section_id = data.section_id;
                this.section_name = data.section_name;
                this.is_private = data.is_private;
				this.created_by = data.created_by;
				this.priority = parseInt(data.priority);
				this.assignees = data.assignees;
				this.is_complete = data.is_complete;
				this.is_complete_server = data.is_complete;
                this.date_completed = data.date_completed === null ? null : parseInt(data.date_completed);
				this.duration = data.duration == null ? '' : data.duration;
				this.duration_units = data.duration_units;
				this.date_created = data.date_created;
				this.comments_count = data.comments_count;
				this.perms = data.perms;
				if (data.source !== undefined)
                {
					this.source = {
						name: data.source.name,
						link: data.source.link
					};
				}
				this.has_file_storage = data.has_file_storage;

				this.orders = {
					list: data.orders.list,
					board: data.orders.board,
					board_no_list: data.orders.board_no_list,
					my_tasks: data.orders.my_tasks
				};

				this.status_id = data.status_id;
				this.status_list = data.status_list;
			},
			add: function(successCallback, errorCallback)
			{
				var data =
				{
                    project_id: this.project_id,
					title: this.title.length <= 255 ? this.title : this.title.substr(0, 255),
                    description: this.description,
					due_date: this.due_date,
					section_id: this.section_id,
					is_private: this.is_private,
                    assignees: this.assignees,
					priority: this.priority,
					duration: this.duration,
					is_complete: this.is_complete
				};
				$http(
					{
						method:'POST',
						url: this.restUrl + 'add',
						data: data,
						task: this,
						successCallback: successCallback,
						errorCallback: errorCallback
					}).then(function(response)
					{
						var task = response.config.task;
						task.id = response.data.id;
						task.project_id = response.data.project_id;
						task.title = response.data.title;
						task.description = response.data.description;
						task.due_date = response.data.due_date;
						task.due_date_formatted = response.data.due_date_formatted;
						task.due_date_type = response.data.due_date_type;
						task.section_id = response.data.section_id;
						task.is_private = response.data.is_private;
						task.created_by = response.data.created_by;
						task.assignees = response.data.assignees;
						task.perms = response.data.perms;
						task.priority = response.data.priority;
						task.is_complete = response.data.is_complete;
						task.duration = response.data.duration;
						task.duration_units = response.data.duration_units;
						task.is_complete_server = task.is_complete;

                        response.config.successCallback(response.data);
                    }, function (response) {
                        response.config.errorCallback(response.data);
					});
			},
			update: function(successCallback, errorCallback)
			{
				var data =
				{
					project_id: this.project_id,
					title: this.title.length <= 255 ? this.title : this.title.substr(0, 255),
                    description: this.description,
					due_date: this.due_date,
					is_private: this.is_private,
                    assignees: this.assignees,
                    priority: this.priority,
					duration: this.duration,
					is_complete: this.is_complete
				};

                // An array should contains only ids not user's objects
                if (Array.isArray(this.assignees) &&
                    this.assignees.length > 0  &&
                    this.assignees[0].hasOwnProperty('id')
                ) {
                    data.assignees = this.getUsersIds(this.assignees);
                }

				$http(
					{
						method:'PUT',
						url: this.restUrl + this.id,
						data: data,
						task: this,
						successCallback: successCallback,
						errorCallback: errorCallback
					}).then(function(response)
                    {
                        var task                = response.config.task;
                        task.id                 = response.data.id;
                        task.project_id         = response.data.project_id;
                        task.title              = response.data.title;
                        task.description        = response.data.description;
                        task.due_date           = response.data.due_date;
                        task.due_date_formatted = response.data.due_date_formatted;
                        task.due_date_type      = response.data.due_date_type;
                        task.is_private         = response.data.is_private;
                        task.created_by         = response.data.created_by;
                        task.assignees          = response.data.assignees;
                        task.perms              = response.data.perms;
                        task.priority           = response.data.priority;
                        task.is_complete        = response.data.is_complete;
                        task.duration           = response.data.duration;
                        task.duration_units     = response.data.duration_units;
                        task.is_complete_server = task.is_complete;

                        response.config.successCallback(task);
                    }, function (response) {
                        response.config.errorCallback(response.data);
                    });
				},
                delete: function (successCallback, errorCallback) {
                    $http(
                        {
                            method: 'DELETE',
                            url: this.restUrl + this.id,
                            section: this,
                            successCallback: successCallback,
                            errorCallback: errorCallback
                        }).then(function (response) {
                        response.config.successCallback(response.data.id);
                    }, function (response) {
                        response.config.errorCallback(response.data);
                    });
                },
                setComplete: function (successCallback, errorCallback) {
                    $http(
                        {
                            method: 'PUT',
                            url: this.restUrl + this.id + '/complete',
                            task: this,
                            successCallback: successCallback,
                            errorCallback: errorCallback
                        }).then(function (response) {
                        response.config.task.is_complete_server = true;
                        response.config.task.status_id = response.config.task.STATUS_COMPLETED;
                        response.config.successCallback(response.config.task, response.data);
                    }, function (response) {
                        response.config.errorCallback(response.config.task, response.data);
                    });
                },
                setIncomplete: function (successCallback, errorCallback) {
                    $http(
                        {
                            method: 'PUT',
                            url: this.restUrl + this.id + '/incomplete',
                            task: this,
                            successCallback: successCallback,
                            errorCallback: errorCallback
                        }).then(function (response) {
                        response.config.task.is_complete_server = false;
                        response.config.task.status_id = response.config.task.STATUS_TODO;
                        response.config.successCallback(response.config.task, response.data);
                    }, function (response) {
                        response.config.errorCallback(response.config.task, response.data);
                    });
                },
                setStatus: function (successCallback, errorCallback) {
                    if (!successCallback)
                        successCallback = function () {
                        };

                    if (!errorCallback)
                        errorCallback = function () {
                        };

                    var status      = this.status_id;
                    var is_complete = this.status_id === this.STATUS_COMPLETED;

                    $http({
                        method: "PUT",
                        url: '/api/projects/task/' + this.id + '/status',
                        task: this,
                        data: {
                            status: status
                        },
                        successCallback: successCallback,
                        errorCallback: errorCallback
                    }).then(function (response) {
                        response.config.task.is_complete_server = is_complete;
                        response.config.task.is_complete        = is_complete;
                        response.config.successCallback(response.config.task, response.data);
                    }, function (response) {
                        response.config.errorCallback(response.config.task, response.data);
                    });
                },
                moveToSectionId: function (section_id) {
                    this.section_id = section_id;
                },
                setDisplayOrder: function (display_order) {
                    this.display_order = display_order;
                },

                /**
                 * Change an array with objects contains assigned user to array with ids
                 *
                 * @param associative_array Array
                 * @returns [1,2,3,..] Array
                 */
                getUsersIds: function (associative_array) {
                    var assignees = [];

                    associative_array.map(function(assignee) {
                        if (!assignee.hasOwnProperty('id'))
                            return;

                        assignees.push(assignee.id);
                    })

                    return assignees;
                }
            };

        return Task;
    }
}());
