define([
    'jquery',
    'json.sortify',
    '/bower_components/nthen/index.js',
    '/common/sframe-common.js',
    '/common/sframe-app-framework.js',
    '/common/common-util.js',
    '/common/common-hash.js',
    '/common/common-interface.js',
    '/common/modes.js',
    '/customize/messages.js',
    '/kanban/jkanban.js',
    '/common/jscolor.js',
    'css!/kanban/jkanban.css',

    'less!/kanban/app-kanban.less'
], function (
    $,
    Sortify,
    nThen,
    SFCommon,
    Framework,
    Util,
    Hash,
    UI,
    Modes,
    Messages)
{

    var verbose = function (x) { console.log(x); };
    verbose = function () {}; // comment out to enable verbose logging

    var COLORS = ['yellow', 'green', 'orange', 'blue', 'red', 'purple', 'cyan', 'lightgreen', 'lightblue'];

    var addRemoveItemButton = function (framework, kanban) {
        if (!kanban) { return; }
        if (framework.isReadOnly() || framework.isLocked()) { return; }
        var $container = $(kanban.element);
        $container.find('.kanban-remove-item').remove();
        $container.find('.kanban-board .kanban-item').each(function (i, el) {
            var pos = kanban.findElementPosition(el);
            var board = kanban.options.boards.find(function (b) {
                return b.id === $(el.parentNode.parentNode).attr('data-id');
            });
            $('<button>', {
                'class': 'kanban-remove-item btn btn-default fa fa-times',
                title: Messages.kanban_removeItem
            }).click(function (e) {
                e.stopPropagation();
                UI.confirm(Messages.kanban_removeItemConfirm, function (yes) {
                    if (!yes) { return; }
                    board.item.splice(pos, 1);
                    $(el).remove();
                    kanban.onChange();
                });
            }).appendTo($(el));
        });
    };

    // Kanban code
    var initKanban = function (framework, boards) {
        var defaultBoards = [{
            "id": "todo",
            "title": Messages.kanban_todo,
            "color": "blue",
            "item": [{
                "title": Messages._getKey('kanban_item', [1])
            }, {
                "title": Messages._getKey('kanban_item', [2])
            }]
        }, {
            "id": "working",
            "title": Messages.kanban_working,
            "color": "orange",
            "item": [{
                "title": Messages._getKey('kanban_item', [3])
            }, {
                "title": Messages._getKey('kanban_item', [4])
            }]
        }, {
            "id": "done",
            "title": Messages.kanban_done,
            "color": "green",
            "item": [{
                "title": Messages._getKey('kanban_item', [5])
            }, {
                "title": Messages._getKey('kanban_item', [6])
            }]
        }];

        if (!boards) {
            verbose("Initializing with default boards content");
            boards = defaultBoards;
        } else {
            verbose("Initializing with boards content " + boards);
        }

        // Remove any existing elements
        $(".kanban-container-outer").remove();

        var getInput = function () {
            return $('<input>', {
                'type': 'text',
                'id': 'kanban-edit',
                'size': '30'
            }).click(function (e) { e.stopPropagation(); });
        };

        var kanban = new window.jKanban({
            element: '#cp-app-kanban-content',
            gutter: '5px',
            widthBoard: '300px',
            buttonContent: '❌',
            colors: COLORS,
            readOnly: framework.isReadOnly(),
            onChange: function () {
                verbose("Board object has changed");
                framework.localChange();
                if (kanban) {
                    addRemoveItemButton(framework, kanban);
                }
            },
            click: function (el) {
                if (framework.isReadOnly() || framework.isLocked()) { return; }
                if (kanban.inEditMode) {
                    $(el).focus();
                    verbose("An edit is already active");
                    //return;
                }
                kanban.inEditMode = true;
                $(el).find('button').remove();
                var name = $(el).text();
                $(el).html('');
                var $input = getInput().val(name).appendTo(el).focus();
                $input[0].select();
                var save = function () {
                    // Store the value
                    var name = $input.val();
                    // Remove the input
                    $(el).text(name);
                    // Save the value for the correct board
                    var board = $(el).closest('.kanban-board').attr("data-id");
                    var pos = kanban.findElementPosition(el.parentNode);
                    kanban.getBoardJSON(board).item[pos].title = name;
                    kanban.onChange();
                    // Unlock edit mode
                    kanban.inEditMode = false;
                };
                $input.blur(save);
                $input.keydown(function (e) {
                    if (e.which === 13) {
                        e.preventDefault();
                        e.stopPropagation();
                        save();
                        if (!$input.val()) { return; }
                        if (!$(el).closest('.kanban-item').is(':last-child')) { return; }
                        $(el).closest('.kanban-board').find('.kanban-title-button.fa-plus').click();
                        return;
                    }
                    if (e.which === 27) {
                        e.preventDefault();
                        e.stopPropagation();
                        $(el).text(name);
                        kanban.inEditMode = false;
                        addRemoveItemButton(framework, kanban);
                        return;
                    }
                });

            },
            boardTitleClick: function (el, e) {
                e.stopPropagation();
                if (framework.isReadOnly() || framework.isLocked()) { return; }
                if (kanban.inEditMode) {
                    $(el).focus();
                    verbose("An edit is already active");
                    //return;
                }
                kanban.inEditMode = true;
                var name = $(el).text();
                $(el).html('');
                var $input = getInput().val(name).appendTo(el).focus();
                $input[0].select();
                var save = function () {
                    // Store the value
                    var name = $input.val();
                    if (!name || !name.trim()) {
                        return kanban.onChange();
                    }
                    // Remove the input
                    $(el).text(name);
                    // Save the value for the correct board
                    var board = $(el.parentNode.parentNode).attr("data-id");
                    kanban.getBoardJSON(board).title = name;
                    kanban.onChange();
                    // Unlock edit mode
                    kanban.inEditMode = false;
                };
                $input.blur(save);
                $input.keydown(function (e) {
                    if (e.which === 13) {
                        e.preventDefault();
                        e.stopPropagation();
                        save();
                        return;
                    }
                    if (e.which === 27) {
                        e.preventDefault();
                        e.stopPropagation();
                        $(el).text(name);
                        kanban.inEditMode = false;
                        return;
                    }
                });
            },
            colorClick: function (el, type) {
                if (framework.isReadOnly() || framework.isLocked()) { return; }
                verbose("on color click");
                var boardJSON;
                var board;
                if (type === "board") {
                    verbose("board color click");
                    board = $(el.parentNode).attr("data-id");
                    boardJSON = kanban.getBoardJSON(board);
                } else {
                    verbose("item color click");
                    board = $(el.parentNode.parentNode).attr("data-id");
                    var pos = kanban.findElementPosition(el);
                    boardJSON = kanban.getBoardJSON(board).item[pos];
                }
                var onchange = function (colorL) {
                    var elL = el;
                    var typeL = type;
                    var boardJSONL;
                    var boardL;
                    if (typeL === "board") {
                        verbose("board color change");
                        boardL = $(elL.parentNode).attr("data-id");
                        boardJSONL = kanban.getBoardJSON(boardL);
                    } else {
                        verbose("item color change");
                        boardL = $(elL.parentNode.parentNode).attr("data-id");
                        var pos = kanban.findElementPosition(elL);
                        boardJSONL = kanban.getBoardJSON(boardL).item[pos];
                    }
                    var currentColor = boardJSONL.color;
                    verbose("Current color " + currentColor);
                    if (currentColor !== colorL.toString()) {
                        $(elL).removeClass("kanban-header-" + currentColor);
                        boardJSONL.color = colorL.toString();
                        kanban.onChange();
                    }
                };
                var jscolorL;
                el._jscLinkedInstance = undefined;
                jscolorL = new window.jscolor(el,{showOnClick: false, onFineChange: onchange, valueElement:undefined});
                jscolorL.show();
                var currentColor = boardJSON.color;
                if (currentColor === undefined) {
                    currentColor = '';
                }
                jscolorL.fromString(currentColor);
            },
            buttonClick: function (el, boardId, e) {
                e.stopPropagation();
                if (framework.isReadOnly() || framework.isLocked()) { return; }
                UI.confirm(Messages.kanban_deleteBoard, function (yes) {
                    if (!yes) { return; }
                    verbose("Delete board");
                    //var boardName = $(el.parentNode.parentNode).attr("data-id");
                    for (var index in kanban.options.boards) {
                        if (kanban.options.boards[index].id === boardId) {
                            break;
                        }
                        index++;
                    }
                    kanban.options.boards.splice(index, 1);
                    kanban.removeBoard(boardId);
                    kanban.onChange();
                });
            },
            addItemClick: function (el) {
                if (framework.isReadOnly() || framework.isLocked()) { return; }
                if (kanban.inEditMode) {
                    $(el).focus();
                    verbose("An edit is already active");
                    //return;
                }
                kanban.inEditMode = true;
                // create a form to enter element
                var boardId = $(el.parentNode.parentNode).attr("data-id");
                var $item = $('<div>', {'class': 'kanban-item new-item'});
                var $input = getInput().val(name).appendTo($item);
                kanban.addForm(boardId, $item[0]);
                $input.focus();
                var save = function () {
                    $item.remove();
                    kanban.inEditMode = false;
                    if (!$input.val()) { return; }
                    kanban.addElement(boardId, {
                        "title": $input.val(),
                    });
                };
                $input.blur(save);
                $input.keydown(function (e) {
                    if (e.which === 13) {
                        e.preventDefault();
                        e.stopPropagation();
                        save();
                        if (!$input.val()) { return; }
                        $(el).closest('.kanban-board').find('.kanban-title-button.fa-plus').click();
                        return;
                    }
                    if (e.which === 27) {
                        e.preventDefault();
                        e.stopPropagation();
                        $item.remove();
                        kanban.inEditMode = false;
                        return;
                    }
                });
            },
            addItemButton: true,
            boards: boards
        });

        var addBoardDefault = document.getElementById('kanban-addboard');
        $(addBoardDefault).attr('title', Messages.kanban_addBoard);
        addBoardDefault.addEventListener('click', function () {
            if (framework.isReadOnly()) { return; }
            var counter = 1;

            // Get the new board id
            var boardExists = function (b) { return b.id === "board" + counter; };
            while (kanban.options.boards.some(boardExists)) { counter++; }

            kanban.addBoards([{
                "id": "board" + counter,
                "title": Messages.kanban_newBoard,
                "color": COLORS[Math.floor(Math.random()*COLORS.length)], // random color
                "item": [{
                    "title": Messages._getKey('kanban_item', [1]),
                }]
            }]);
            kanban.onChange();
        });

        return kanban;
    };

    var mkHelpMenu = function (framework) {
        var $toolbarContainer = $('#cp-app-kanban-container');
        $toolbarContainer.prepend(framework._.sfCommon.getBurnAfterReadingWarning());

        var helpMenu = framework._.sfCommon.createHelpMenu(['kanban']);
        $toolbarContainer.prepend(helpMenu.menu);

        framework._.toolbar.$drawer.append(helpMenu.button);
    };

    // Start of the main loop
    var andThen2 = function (framework) {

        var kanban;
        var $container = $('#cp-app-kanban-content');

        mkHelpMenu(framework);

        if (framework.isReadOnly()) {
            $container.addClass('cp-app-readonly');
        } else {
            framework.setFileImporter({}, function (content /*, file */) {
                var parsed;
                try { parsed = JSON.parse(content); }
                catch (e) { return void console.error(e); }
                return { content: parsed };
            });
        }

        framework.setFileExporter('.json', function () {
            return new Blob([JSON.stringify(kanban.getBoardsJSON(), 0, 2)], {
                type: 'application/json',
            });
        });

        framework.onEditableChange(function (unlocked) {
            if (framework.isReadOnly()) { return; }
            if (!kanban) { return; }
            if (unlocked) {
                addRemoveItemButton(framework, kanban);
                kanban.options.readOnly = false;
                return void $container.removeClass('cp-app-readonly');
            }
            kanban.options.readOnly = true;
            $container.addClass('cp-app-readonly');
        });

        var getSelectedElement = function () {
            var node = document.getSelection().anchorNode;
            return (node.nodeType === 3 ? node.parentNode : node);
        };
        var getCursor = function () {
            if (!kanban || !kanban.inEditMode) { return; }
            try {
                var el = getSelectedElement();
                var input = $(el).is('input') ? el : $(el).find('input')[0];
                if (!input) { return; }
                var $input = $(input);

                var pos;
                var $item = $(el).closest('.kanban-item');
                if ($item.length) {
                    pos = kanban.findElementPosition($item[0]);
                }
                var board = $input.closest('.kanban-board').attr('data-id');
                var val = ($input.val && $input.val()) || '';
                var start = input.selectionStart;
                var end = input.selectionEnd;


                var boardEl = kanban.options.boards.find(function (b) {
                    return b.id === board;
                });
                var oldVal = ((pos ? boardEl.item[pos] : boardEl) || {}).title;

                return {
                    board: board,
                    pos: pos,
                    value: val,
                    start: start,
                    end: end,
                    oldValue: oldVal
                };
            } catch (e) {
                return {};
            }
        };
        var restoreCursor = function (data) {
            try {
                var boardEl = kanban.options.boards.find(function (b) {
                    return b.id === data.board;
                });
                if (!boardEl) { return; }

                var $board = $('.kanban-board[data-id="'+data.board+'"');

                // Editing a board title...
                if (!data.pos && $board.length) {
                    if (boardEl.title !== data.oldValue) { return; }
                    $board.find('.kanban-title-board').click();
                    var $boardInput = $board.find('.kanban-title-board input');
                    $boardInput.val(data.value);
                    $boardInput[0].selectionStart = data.start;
                    $boardInput[0].selectionEnd = data.end;
                    return;
                }
                // Editing a deleted board title: abort
                if (!data.pos) {
                    return;
                }

                // An item was added: add a new item
                if (!data.oldValue) {
                    $board.find('.kanban-title-button.fa-plus').click();
                    var $newInput = $board.find('.kanban-item:last-child input');
                    $newInput.val(data.value);
                    $newInput[0].selectionStart = data.start;
                    $newInput[0].selectionEnd = data.end;
                    return;
                }

                // An item was edited: click on the correct item
                var newVal = boardEl.item[data.pos];
                if (!newVal || newVal.title !== data.oldValue) { return; }
                var $el = $('.kanban-board[data-id="' + data.board + '"]')
                            .find('.kanban-item:nth-child('+(data.pos + 1)+')');
                $el.find('.kanban-item-text').click();
                var $input = $el.find('input');
                if ($input.length) {
                    $input.val(data.value);
                    $input[0].selectionStart = data.start;
                    $input[0].selectionEnd = data.end;
                }
            } catch (e) {
                return;
            }
        };

        framework.onContentUpdate(function (newContent) {
            // Init if needed
            if (!kanban) {
                kanban = initKanban(framework, (newContent || {}).content);
                addRemoveItemButton(framework, kanban);
                return;
            }

            // Need to update the content
            verbose("Content should be updated to " + newContent);
            var currentContent = kanban.getBoardsJSON();
            var remoteContent = newContent.content;

            if (Sortify(currentContent) !== Sortify(remoteContent)) {
                var cursor = getCursor();
                verbose("Content is different.. Applying content");
                kanban.setBoards(remoteContent);
                kanban.inEditMode = false;
                addRemoveItemButton(framework, kanban);
                restoreCursor(cursor);
            }
        });

        framework.setContentGetter(function () {
            if (!kanban) {
                return {
                    content: []
                };
            }
            var content = kanban.getBoardsJSON();
            verbose("Content current value is " + content);
            return {
                content: content
            };
        });

        framework.onReady(function () {
            $("#cp-app-kanban-content").focus();
        });

        framework.onDefaultContentNeeded(function () {
            kanban = initKanban(framework);
        });

        framework.start();
    };

    var main = function () {
        // var framework;
        nThen(function (waitFor) {

            // Framework initialization
            Framework.create({
                toolbarContainer: '#cme_toolbox',
                contentContainer: '#cp-app-kanban-editor',
            }, waitFor(function (framework) {
                andThen2(framework);
            }));
        });
    };
    main();
});