/**
 * Adds a new list element, and updates numeric elements if necessary
 * when using a newline in a Markdown list
 */

action.canPerformWithContext = function(context, outError) {
	// Don't even both with this if there's a selection
	return context.selectedRanges[0].length === 0;
};

// Shared selectors
var puncSelector = new SXSelector('list.element > punctuation.definition.begin'),
	rootSelector = new SXSelector('list.element'),
	orderedSelector = new SXSelector('list.element.ordered');

function isSameZone(zone1, zone2) {
	return zone1.range.location === zone2.range.location && zone1.range.length === zone2.range.length;
}

action.performWithContext = function(context, outError) {
	/*
	Lots of branches here:
	
	- Ordered (add new number below) vs. unordered (add same symbol below)
	- Populated list element (new element) vs. empty one (delete element)
	- End of line enter (new element) vs. middle of line enter (indent the newline)
	- Mid-element (above behavior) vs. element delimiter (normal enter)
	*/
	var cursor = context.selectedRanges[0],
		zone = context.syntaxTree.zoneAtCharacterIndex(cursor.location);
	
	if (puncSelector.matches(zone)) {
		// We're somewhere in the initial punctuation, so just stick a normal enter in the document
		return context.insertTextSnippet(new CETextSnippet(context.textPreferences.lineEndingString));
	}
	
	var rootZone = zone;
	// Make sure our root zone is the whole list element
	while (!rootSelector.matches(rootZone)) {
		rootZone = rootZone.parent;
	}
	// Grab our punctuation zone
	var puncZone = rootZone.childAtIndex(0),
		// And setup our shared recipe variable
		recipe = new CETextRecipe();
	
	// Figure out if we are adding an element, or inserting an indented newline
	if (cursor.location === context.string.length || /[\r\n]+/.test(context.substringWithRange(new Range(cursor.location, 1)))) {
		// We are either creating a new element or exiting the list
		// First, make sure that we are not working with an empty list element
		var listText = context.substringWithRange(rootZone.range);
		if (/^\s*(?:\d+\.|[*+-])\s*$/.test(listText)) {
			// The current list element is empty, so exit the list by deleting the list punctuation
			recipe.replaceRange(new Range(puncZone.range.location, cursor.location - puncZone.range.location), '\n');
			return context.applyTextRecipe(recipe, CETextOptionNormalizeIndentationLevel|CETextOptionNormalizeLineEndingCharacters);
		} else if (/^\s*$/.test(context.substringWithRange(context.lineStorage.lineRangeForIndex(cursor.location)))) {
			// The current line is empty, which means they have cleared out the list marker themselves; do not enter a new marker
			return context.insertTextSnippet(new CETextSnippet(context.textPreferences.lineEndingString));
		} else {
			// We are adding a new list element
			// Figure out what our next punctuation character will be
			var listMarker = puncZone.text,
				numerical = false, position = 0;
			if (listMarker.length > 1) {
				// We are working with a numerical list
				numerical = true;
				position = parseInt(listMarker.replace(/^(\d+)\.$/, '$1')) + 1;
				listMarker = position + '.';
			}
			// Update any following zones if numerical
			if (numerical) {
				// First, parse through the parentZone's children until we locate rootZone
				var parentZone = rootZone.parent,
					zoneIndex = 0;
				while (zoneIndex < parentZone.childCount && !isSameZone(parentZone.childAtIndex(zoneIndex), rootZone)) {
					zoneIndex++;
				}
				// Bump zoneIndex one more time to get to the next sibling zone after rootZone
				zoneIndex++;
				if (zoneIndex < parentZone.childCount) {
					// We have a sibling!
					var newMarker = '';
					while (zoneIndex < parentZone.childCount && orderedSelector.matches(parentZone.childAtIndex(zoneIndex))) {
						// Increment our position, then replace the old one with the new one
						position++;
						newMarker = position + '.';
						recipe.replaceRange(parentZone.childAtIndex(zoneIndex).childAtIndex(0).range, newMarker);
						zoneIndex++;
					}
					// Apply our recipe to update the following numerical zones
					context.applyTextRecipe(recipe);
				}
			}
			// Insert our new element
			return context.insertTextSnippet(new CETextSnippet(context.textPreferences.lineEndingString + listMarker + ' $0'));
		}
	} else {
		// We are inserting an indented newline
		// First, strip out any whitespace after the cursor
		if (/\s/.test(context.substringWithRange(new Range(cursor.location, 1)))) {
			var stripIndex = cursor.location;
			while (/\s/.test(context.substringWithRange(new Range(stripIndex, 1)))) {
				stripIndex++;
			}
			recipe.deleteRange(new Range(cursor.location, stripIndex - cursor.location));
			context.applyTextRecipe(recipe);
		}
		// Setup our base leading whitespace based on the size of the punctuation zone
		var leadingString = Array(puncZone.range.length + 1).join(' '),
			leadingIndex = puncZone.range.location + puncZone.range.length,
			nextChar = context.substringWithRange(new Range(leadingIndex, 1));	
		while (/\s/.test(nextChar)) {
			leadingString += nextChar;
			leadingIndex++;
			nextChar = context.substringWithRange(new Range(leadingIndex, 1));
		}
		// Insert our snippet!
		return context.insertTextSnippet(new CETextSnippet(context.textPreferences.lineEndingString + leadingString + '$0'));
	}
};
