/* eslint-env amd */
define([],
function() {
	/**
	 * @fileoverview The event generator for comments.
	 * @author Toru Nagashima
	 */

	"use strict";

	//------------------------------------------------------------------------------
	// Helpers
	//------------------------------------------------------------------------------

	/**
	 * Check collection of comments to prevent double event for comment as
	 * leading and trailing, then emit event if passing
	 * @param {ASTNode[]} comments - Collection of comment nodes
	 * @param {EventEmitter} emitter - The event emitter which is the destination of events.
	 * @param {Object[]} locs - List of locations of previous comment nodes
	 * @param {string} eventName - Event name postfix
	 * @returns {void}
	 */
	function emitComments(comments, emitter, locs, eventName) {
		if (comments.length > 0) {
			comments.forEach(function(node) {
				var index = locs.indexOf(node.loc);

				if (index >= 0) {
					locs.splice(index, 1);
				} else {
					locs.push(node.loc);
					emitter.emit(node.type + eventName, node);
				}
			});
		}
	}

	/**
	 * Shortcut to check and emit enter of comment nodes
	 * @param {CommentEventGenerator} generator - A generator to emit.
	 * @param {ASTNode[]} comments - Collection of comment nodes
	 * @returns {void}
	 */
	function emitCommentsEnter(generator, comments) {
		emitComments(
			comments,
			generator.emitter,
			generator.commentLocsEnter,
			"Comment");
	}

	/**
	 * Shortcut to check and emit exit of comment nodes
	 * @param {CommentEventGenerator} generator - A generator to emit.
	 * @param {ASTNode[]} comments Collection of comment nodes
	 * @returns {void}
	 */
	function emitCommentsExit(generator, comments) {
		emitComments(
			comments,
			generator.emitter,
			generator.commentLocsExit,
			"Comment:exit");
	}

	//------------------------------------------------------------------------------
	// Public Interface
	//------------------------------------------------------------------------------

	/**
	 * The event generator for comments.
	 * This is the decorator pattern.
	 * This generates events of comments before/after events which are generated the original generator.
	 *
	 * @param {EventGenerator} originalEventGenerator - An event generator which is the decoration target.
	 * @param {SourceCode} sourceCode - A source code which has comments.
	 * @returns {CommentEventGenerator} new instance.
	 */
	function CommentEventGenerator(originalEventGenerator, sourceCode) {
		this.original = originalEventGenerator;
		this.emitter = originalEventGenerator.emitter;
		this.sourceCode = sourceCode;
		this.commentLocsEnter = [];
		this.commentLocsExit = [];
	}

	CommentEventGenerator.prototype = {
		constructor: CommentEventGenerator,

		/**
		 * Emits an event of entering comments.
		 * @param {ASTNode} node - A node which was entered.
		 * @returns {void}
		 */
		enterNode: function enterNode(node) {
			var comments = this.sourceCode.getComments(node);

			emitCommentsEnter(this, comments.leading);
			this.original.enterNode(node);
			emitCommentsEnter(this, comments.trailing);
		},

		/**
		 * Emits an event of leaving comments.
		 * @param {ASTNode} node - A node which was left.
		 * @returns {void}
		 */
		leaveNode: function leaveNode(node) {
			var comments = this.sourceCode.getComments(node);

			emitCommentsExit(this, comments.trailing);
			this.original.leaveNode(node);
			emitCommentsExit(this, comments.leading);
		}
	};

	return CommentEventGenerator;

});