var _nextTaskId = -1;

function SaveScheduler() {
    
    var _updateTimers = new Array();
    
    this.IsSaveScheduled = function(itemID) {
        if (_updateTimers[itemID] == null) {
            return false;
        }
        else {
            return true;
        }
    };
    
    this.ScheduleSave = function(itemID, listItem) {
        // Check for a timer, if there is none
        if (this.IsSaveScheduled(itemID) == false) {
            // set a timer to save this item in 3 seconds
            _updateTimers[itemID] = setTimeout(function() {
                _updateTimers[itemID] = null;
                saveListItem(itemID);				
            },
            3000);
        }
    };
    
    this.CancelSave = function(itemID) {
        if (this.IsSaveScheduled(itemID)){
            clearTimeout(_updateTimers[itemID]);
            _updateTimers[itemID] = null;
        }
    };
    
    // Saves the list item
    var saveListItem = function(itemID) {
        var listItem = $("li#task_" + itemID);
        var cell = listItem.children("input.Title");
        var position = listItem.children("input.Position");
        
        // Build data packet
        var data = {
            title: cell.attr("value")
        };
        
        if (position.length > 0) {
            data.position = position.attr("value");			
        }
    
        var url = "/api/insert/";
    
        // Is this an insert or a delete? Build the appropriate postback url
        if (false == isListItemNew(listItem)) {
            url = "/api/update/" + getListItemID(listItem) + "/";
        }
            
        // post to the server	
        jQuery.post(url, data, function(responseData, status) {
            if (status == "success") {
            
                // Remove the saving gif
                listItem.children("img.saving").remove();	
                
                // If this was an insert, get the ID of the inserted task and set that as the name of the input box
                if (url == "/api/insert/") {
                    var dataObj = JSON.parse(responseData);
                    cell.attr("name", "task_" + dataObj.taskId);
                    cell.parent().attr("id", "task_" + dataObj.taskId);
                }
            }
            else {
                // If something went wrong, keep trying
                scheduler.ScheduleSave(getListItemID(listItem));				
            };
        });
    };
}

var scheduler = new SaveScheduler();

// Helper method to set the focus on an element, uses a timer to get round IE's threading issues
function setFocus(input){
    setTimeout(function() {
        input.focus();
        input.select();
    }, 1);
    return input;
}

// Called whenever the user presses a key inside a standard item
function listItemKeyDown(eventObject) {

    var retval = true;
    var thisCell = $(this);
    var thisItem = thisCell.parent();
    var thisList = thisItem.parent();

    switch (eventObject.keyCode)
    {
    case 46: // Delete
        if (eventObject.ctrlKey == true) {
            deleteListItem(thisItem);
            retval = false;
        }
        else {
            listItemEdited(thisItem);
        }
        break;
    
    case 9: // Tab
        if (eventObject.shiftKey == true) {
            moveToPreviousListItem(thisCell);
        }
        else {
            moveToNextListItem(thisCell);
        }
        retval = false;
        break;

    case 13: // Return
		if (eventObject.ctrlKey == true) {
            if (eventObject.shiftKey == true) {
				// Insert a new item beneath this one
				insertNewRow(thisItem.next(), "");
			}
			else{
				// TODO: Mark item as done
				deleteListItem(thisItem);
			}
		}
		else {
			moveToNextListItem(thisCell);
			retval = false;
		}
        break;

    case 38: // Up		
        if (eventObject.ctrlKey == true) {
            if (eventObject.shiftKey == true) {
                moveListItemToTop(thisItem);
            }
            else {	
                moveListItemUp(thisItem);
            }
        }
        else {
            moveToPreviousListItem(thisCell);
        }		
        break;

    case 40: // Down
        if (eventObject.ctrlKey == true) {
            if (eventObject.shiftKey == true) {
                moveListItemToBottom(thisItem);
            }
            else {
                moveListItemDown(thisItem);
            }
        }
        else {
            moveToNextListItem(thisCell);	
        }		
        break;

    default:
        // if the keypress will actually do something
        if (eventObject.keyCode < 16 || eventObject.keyCode > 45) {
            listItemEdited(thisItem);
        }
        break;
    }

    return retval;
}

function moveListItemUp(thisItem) {
    var prevItem = thisItem.prev();
    
    if (prevItem.length > 0) {	
        var thisCell = thisItem.children("input.Title");
        var position = thisItem.prevAll().length + 1;
    
        var clonedItem = thisItem.clone(true);
        
        // Swap the items round
        clonedItem.insertBefore(prevItem);
        thisItem.remove();
    
        // Add a new position value for the save to post back
        setPositionValue(clonedItem, position - 1);
        setPositionValue(prevItem, position);
    
        // Trigger a save of the items				
        listItemEdited(clonedItem);
        listItemEdited(prevItem);
    
        setFocus(clonedItem.children("input.Title"));
    }
}

function moveListItemToTop(thisItem) {
    var prevItems = thisItem.prevAll();
    if (prevItems.length > 0) {	
        var thisCell = thisItem.children("input.Title");
        var clonedItem = thisItem.clone(true);

        thisItem.parent().prepend(clonedItem);
        thisItem.remove();
    
        setPositionValue(clonedItem, 1);
        
        prevItems.each(function (i) {
            var prevItem = $(this);
            setPositionValue(prevItem, prevItem.prevAll().length + 1);
            listItemEdited(prevItem);
        });
        
        listItemEdited(clonedItem);

        setFocus(clonedItem.children("input.Title"));
    }
}

function moveListItemDown(thisItem) {
    var thisCell = thisItem.children("input.Title");
    var position = thisItem.prevAll().length + 1;

    var nextItem = thisItem.next();
    
    if (nextItem.length != 0) {	
        if (nextItem.find("input.Extender").length != 0) {
            moveToNextListItem(thisCell);
        }
        else {
            var clonedItem = thisItem.clone(true);

            // Swap the items round
            clonedItem.insertAfter(nextItem);
            thisItem.remove();		

            // Add a new position value for the save to post back
            setPositionValue(clonedItem, position + 1);
            setPositionValue(nextItem, position);

            // Trigger a save of the items				
            listItemEdited(clonedItem);
            listItemEdited(nextItem);

            setFocus(clonedItem.children("input.Title"));
        }
    }
    return clonedItem;
}

function moveListItemToBottom(thisItem) {
    var nextItems = thisItem.nextAll(":not(:last)");
    if (nextItems.length > 0) {	
        var thisCell = thisItem.children("input.Title");
        var clonedItem = thisItem.clone(true);

        clonedItem.insertBefore(thisItem.nextAll(":last"));
        thisItem.remove();
    
        setPositionValue(clonedItem, nextItems.length + 2);
        
        nextItems.each(function (i) {
            var nextItem = $(this);
            setPositionValue(nextItem, nextItem.prevAll().length + 1);
            listItemEdited(nextItem);
        });
        
        listItemEdited(clonedItem);

        setFocus(clonedItem.children("input.Title"));
    }
}

function setPositionValue(listItem, position) {
    var positionHolder = listItem.find("input.Position");
    if (positionHolder.length == 0) {
        listItem.prepend($("<input type='hidden' class='Position' />"));
        positionHolder = listItem.find("input.Position");
    }
    positionHolder.attr("value", position);
}

function listItemEdited(listItem) {
    showLoadingImage(listItem);
    scheduler.ScheduleSave(getListItemID(listItem));
}

function getListItemID(listItem) {
    if (listItem.length > 0) {
        var cell = listItem.find("input.Title");
        if (cell.length > 0) {
            var nameParts = cell.attr("name").split("_");
            if (nameParts.length > 1) {
                return nameParts[1];
            }
        }
    }
    return 0;
}

function isListItemNew(listItem) {
    return getListItemID(listItem) < 0;
}

function deleteListItem(listItem) {
        
    scheduler.CancelSave(getListItemID(listItem));
    var thisCell = listItem.children("input.Title");
    moveToNextListItem(thisCell);
        
    if (false == isListItemNew(listItem)) {
        // Remove the input, but leave the <li> with a loading icon until the
        // server has configmed the delete
        showLoadingImage(listItem);
        
        taskId = getListItemID(listItem);
        
        url = "/api/delete/" + taskId + "/";
        jQuery.post(url, { }, function(responseData, status) {
            listItem.remove();
        });
        
        thisCell.remove();
    }
    else {
        // If the item was never saved to the server, cancel the save and remove
        scheduler.CancelSave(getListItemID(listItem));
        listItem.remove();
    }	
}

function showLoadingImage(listItem) {
    if (listItem.children("img.saving").length == 0) {
        listItem.append("<img class='saving' src='static/loading.gif' alt='saving' />");
    }
}

function moveToNextListItem(cell) {
    var nextCell = cell.parent().next().children("input.Cell");
    setFocus(nextCell);
}

function moveToPreviousListItem(cell) {
    var prevCell = cell.parent().prev().children("input.Cell");
    setFocus(prevCell);
}

function insertNewRow(nextItem, initialValue) {
	// Create a new item in the list
    var listItemId = "task_" + _nextTaskId--;

    var newItem = createNewRow(listItemId);

    newItem.row.insertBefore(nextItem);

    listItemEdited(newItem.row);
	
	newItem.cell.focus().val(initialValue);
}

// Called whenever the user presses a key inside the extender
function extenderKeyDown(eventObject) {
    // alpha-num
    if ((eventObject.keyCode >= 48 && eventObject.keyCode <= 90) ||
    // number pad
    (eventObject.keyCode >= 96 && eventObject.keyCode <= 111) ||
    // punctuation
    (eventObject.keyCode >= 186))
    {
        insertNewRow($(this).parent(), this.value);
        this.value = "";
    }
    else if (eventObject.keyCode == 38) // Up
    {
        moveToPreviousListItem($(this));	
    }
}

function createNewRow(listItemId) {
    var newRow = $("<li id="+listItemId+"></li>");

    var newCell = $("<input type='text' class='Title Cell'></input>");
    newCell.attr("name", listItemId);
    newCell.keydown(listItemKeyDown);

    var newDoneButton = $("<img class='itemCommand doneCommand' alt='mark item as done' src='static/done.png'/>");
    newDoneButton.click(function() {
        // TODO: Change to use seperate API, done is not the same as delete
        deleteListItem($(this).parent());
    });
    
    var newUpButton = $("<img class='itemCommand upCommand' alt='move item up' src='static/up.png'/>");
    newUpButton.click(function() {
        moveListItemUp($(this).parent());
    });
    
    var newDownButton = $("<img class='itemCommand downCommand' alt='move item down' src='static/down.png'/>");
    newDownButton.click(function() {
        moveListItemDown($(this).parent());
    });

    var newDeleteButton = $("<img class='itemCommand deleteCommand' alt='delete item' src='static/delete.png'/>");
    newDeleteButton.click(function() {
        deleteListItem($(this).parent());
    });
    
    newRow.append(newCell);
    newRow.append(newDoneButton);
    newRow.append(newUpButton);
    newRow.append(newDownButton);
    newRow.append(newDeleteButton);
    
    return { row: newRow, cell: newCell };
}

jQuery(function() {

    // Bind to the keydown event on all input normal boxes
    $("#tasklist > li:not(:last) > input").keydown(listItemKeyDown);

    $("#tasklist > li:not(:last) > img.doneCommand").click(function() {
        deleteListItem($(this).parent());
    });

    $("#tasklist > li:not(:last) > img.deleteCommand").click(function() {
        deleteListItem($(this).parent());
    });
    
    $("#tasklist > li:not(:last) > img.upCommand").click(function() {
        moveListItemUp($(this).parent());
    });
    
    $("#tasklist > li:not(:last) > img.downCommand").click(function() {
        moveListItemDown($(this).parent());
    });

    // Find the extender
    var extender = $("#tasklist > li:last > input.Extender");

    // Bind to the keydown event on the last 'extender' input box
    extender.keyup(extenderKeyDown);

    // Set focus on the extender to begin with
    setFocus(extender);
});
