var moduleName = 'commentsModule';
var module;

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

angular.module(moduleName)
	.factory('Comment', ['$resource', function ($resource) {

	    var Comment = function(type, id, thread_id, offset, limit)
	    {
	        // initialize ng-resource, CRUD operations
	        this.resource = $resource(this.rest, {'id': '@id'}, {
	            'create':
	            { method: 'POST' },
	            'read':
	            { method: 'GET', params: { object_type: type, object_id: id, offset: offset, limit: limit } },
		        'thread':
		        { method: 'GET', params: { thread_id: thread_id, offset: offset, limit: limit } },
	            'update':
	            { method: 'PUT', url: "/api/comments/:id" },
	            'delete':
	            { method: 'DELETE', url: "/api/comments/:id" },
				'replies':
				{ method: 'GET', params: { action: 'replies', object_type: type, object_id: id } },
				'like':
				{ method: 'POST', params: { action: 'like'} },
				'unlike':
				{ method: 'POST', params: { action: 'unlike'} },
				'likes':
				{ method: 'GET', params: { action: 'like'} }
	        },
			{
				stripTrailingSlashes: false
			});

	        this.replies = [];
	    };

	    Comment.prototype =
	    {
	        rest: "/api/comments/:id/:action",
	        resource: null,

	        id: 0,
	        text: "",
			mentioned: [],
	        object_id: 0,
	        object_type: '',
            object_type_name: '',
	        parent_id: 0,
            object_title: '',
            object_url: '',
	        ancillary: null,

	        like_count: 0,
			have_i_liked: false,
	        reply_count: 0,
			loadingReplies: false,

	        timestamp: {},
	        user: {},
	        permissions: {},

	        replies: [],
	        new_reply: {text: "", mentioned: []},

	        files: [],

	        edit_mode: false,
	        reply_mode: false,

	        copy: null, // this holds a copy of the object for editing.


	        /**
	         * Load a Comment from REST data
	         *
	         * @param data
	         */
	        load: function(data)
	        {
	            // if any properties are not present, don't overwrite the content.
	            this.id = data.id || this.id;
	            this.text = data.text || this.text;
				this.mentioned = data.mentioned || this.mentioned;
	            this.object_id = data.object_id || this.object_id;
	            this.object_type = data.object_type || this.object_type;
	            this.object_type_name = data.object_type_name || this.object_type_name;
	            this.object_url = data.object_url || this.object_url;
	            this.parent_id = data.parent_id || this.parent_id;
                this.object_title = data.object_title || this.object_title;
	            this.ancillary = data.ancillary || this.ancillary;
	            this.user = data.user || this.user;
	            this.permissions = data.permissions || this.permissions;
	            this.timestamp = data.timestamp || this.timestamp;

	            this.new_reply = {text: "", mentioned: []};

	            this.reply_count = data.reply_count || this.reply_count;

	            //the javascript || operator is NOT a null coalescing operator. If the new values are falsey,
				// they won't be saved. Because these properties might be 0 or false, we use a way that always works.
	            this.like_count = (data.like_count !== undefined) ? data.like_count : this.like_count;
				this.have_i_liked = (data.have_i_liked !== undefined) ? data.have_i_liked : this.have_i_liked;

				this.files = data.files || this.files;
	        },

	        /**
	         * Get all comments from REST
	         *
	         * @param successCallback
	         * @param failCallback
			 * @param options
	         */
	        getAll: function (successCallback, failCallback, options)
	        {
	        	if (!options.isThreaded)
		        {
		            this.resource.read(options, function (r)
		            {
		                successCallback(r.data, r.options, r.paging);
		            }, function(r)
		            {
		            	failCallback(r);
		            });
		        } else
		        {
			        this.resource.thread(options, function (r)
			        {
				        successCallback(r.data, r.options, r.paging);
			        }, function(r)
			        {
				        failCallback(r);
			        });
		        }
	        },

			getAllReplies: function (callback, options)
			{
				var comment = this;
				this.resource.replies(options, function (r)
				{
					callback(r.comment_id, r.replies);
				}, function()
				{
					comment.loadingReplies = false;
				});
				this.loadingReplies = true;
			},

	        /**
	         * Simply determines if this comment is a reply
	         */
	        isReply: function ()
	        {
	            return this.parent_id > 0;
	        },

	        /**
	         * Determines if this comment has replies
	         */
	        hasReplies: function ()
	        {
	            return this.reply_count > 0;
	        },

	        /**
	         * Determines if this comment has attachments
	         */
	        hasFiles: function ()
	        {
	            return this.files.length > 0;
	        },

	        /**
	         * Determines number of files attached to this comment
	         */
	        numFiles: function ()
	        {
	            return this.files.length;
	        },

	        /**
	         * Gets the number of image files attached
	         */
	        numImages: function ()
	        {
	            var count = 0;
	            for( var i in this.files)
	            { if( this.files[i].image == true ) {
	                count ++;
	            }}
	            return count;
	        },

	        /**
	         * Create a clone of this comment object (for editing)
	         */
	        clone: function ()
	        {
	            this.copy = angular.copy(this);
	            return this.copy;
	        },

	        /**
	         * Resets this object from its cloned copy
	         */
	        reset: function ()
	        {
	            this.id = this.copy.id;
	            this.text = this.copy.text;
				this.mentioned = this.copy.mentioned;
	            this.object_id = this.copy.object_id;
	            this.object_type = this.copy.object_type;
	            this.object_type_name = this.copy.object_type_name;
	            this.parent_id = this.copy.parent_id;
	            this.ancillary = this.copy.ancillary;

	            this.user = this.copy.user;
	            this.permissions = this.copy.permissions;
	            this.timestamp = this.copy.timestamp;

	            this.replies = this.copy.replies;
	            this.new_reply.text = this.copy.new_reply.text;
				this.new_reply.mentioned = this.copy.new_reply.mentioned;

	            this.edit_mode = false;
	            this.reply_mode = false;

	            this.copy = null;
	        },

	        /**
	         * Determines which action to run and executes it. Save or Update
	         *
	         * @param success
	         * @param error
	         */
	        save: function (success, error)
	        {
	            var comment = this;
	            var action = this.id ? this.resource.update : this.resource.create;

	            // update or create
	            action(
	                {
	                    id: this.id || null, // NULL ID for new comments
	                    object_id: this.object_id,
	                    object_type: this.object_type,
                        object_type_name: this.object_type_name,
	                    parent_id: this.parent_id,
	                    text: this.text,
	                    attachments: this.files
	                },
	                function (data)
	                {
	                    comment.load(data);
	                    success(comment);
	                },
	                error
	            );
	        },

	        /**
	         * Deletes a comment
	         *
	         * @param success
	         * @param error
	         */
	        remove: function (success, error)
	        {
	            this.resource.delete(
	                { id: this.id },
	                success,
	                error
	            )
	        },

	        /**
	         * Add a reply to this comment instance
	         *
	         * @param reply
	         */
	        addReply: function (reply)
	        {
	            this.new_reply.text = "";
				this.new_reply.mentioned = [];
	            this.replies.push(reply);
	        },

			like: function(success, error)
			{
				var comment = this;

				this.resource.like(
					{
						id: this.id
					},
					function(data)
					{
						comment.load(data);
						if (success)
							success(comment);
					},
					function()
					{
						if (error)
							error(data);
					}
				);
			},

            unlike: function(success, error)
            {
                var comment = this;
                this.resource.unlike(
                    {
                        id: this.id
                    },
                    function(data)
                    {
                        comment.load(data);
                        if (success)
                            success(comment);
                    },
                    function()
                    {
                        if (error)
                            error(data);
                    }
                );
            },

			getLikes: function(success, error)
			{
				this.resource.likes(
					{
						id: this.id
					},
					function(data)
					{
						if (success)
							success(data);
					}
				);
			},
			getShowMoreLabel: function(){
                return lmsg('common.cla_comments.view_more_replies',this.reply_count - this.replies.length);
			}
	    };

        return Comment;
	}]);
