/**
 * Code for drag&drop and/or editing of SCRUM-like cards from Bugzilla
 * License: Dual-license GPL 3.0+ or MPL 1.1+
 * Author: (c) Vitaliy Filippov 2010-2011
 */
var pressedButton;
var pasteMode = false;
var ctrl = false;
var highlitCard;
var selectedcards = {};
var cuttedcards = [];
var cuttedids = [];
// Добавить карточки для багов с ID'шниками из поля addbugs
function addNewCards()
{
    for (var i = idlist.length-1; i >= 0; i--)
    {
        if (idlist[i] === '')
            idlist.pop();
        else
            break;
    }
    var val = document.getElementById('addbugs').value;
    var re = /(\d+)/g;
    var m;
    while (m = re.exec(val))
        idlist.push(m[1]);
    document.getElementById('idlist_value').value = idlist.join(',');
    document.getElementById('scrumform').submit();
}
// Добавить пустую страницу
function addEmptyPage()
{
    for (var i = 0; i < nr*nc; i++)
        idlist.push('');
    document.getElementById('idlist_value').value = idlist.join(',');
    document.getElementById('scrumform').submit();
}
// Обработчик нажатия Enter на поле addbugs
function addNewIfEnter(ev)
{
    if (ev.keyCode == 10 || ev.keyCode == 13)
    {
        addNewCards();
        return true;
    }
    return false;
}
// Удалить все карточки
function deleteAllCards()
{
    idlist = [];
    document.getElementById('idlist_value').value = idlist.join(',');
    document.getElementById('pages').innerHTML = '';
    document.getElementById('scrumform').submit();
}
// Сбросить состояние кнопок, выделение карточек, режим вставки
function resetAll()
{
    if (pressedButton)
    {
        buttonHandler(null, pressedButton);
        pressedButton = null;
    }
    deselectAll();
    stopPaste();
    return true;
}
// Снять выделение
function deselectAll()
{
    for (var i in selectedcards)
    {
        var e = document.getElementById('cardtd_'+i);
        e.className = 'cardtd';
    }
    selectedcards = {};
}
// Обработчик, пытающийся по иерархии найти кнопку (id=btn_*),
// и вызвать на ней buttonHandler
function guessButton(ev)
{
    ev = DragMaster.fixEvent(ev);
    var t;
    if (t = searchButtonTarget(ev))
        return buttonHandler(ev, t);
    return true;
}
// Обработчик mousedown и mouseup на кнопках (id=btn_*)
function buttonHandler(ev, target)
{
    if (!pressedButton)
    {
        pressedButton = target;
        target.style.borderStyle = 'inset';
        target.style.padding = '4px 2px 2px 4px';
    }
    else if (pressedButton == target)
    {
        target.style.borderStyle = 'outset';
        target.style.padding = '3px';
        pressedButton = null;
        if (target.id == 'btn_cut')
            deleteSelectedCards(true);
        else if (target.id == 'btn_delete')
            deleteSelectedCards(false);
        else if (target.id == 'btn_paste')
            pasteCards();
        else if (target.id == 'btn_paste_beg')
            doPasteCards(0);
        return true;
    }
    return false;
}
// Обработчик клика-выделения по карточке
function selectCard(ev, target)
{
    var empty = isEmptyHash(selectedcards, target.id.substr(7));
    if (pasteMode)
    {
        doPasteCards(id_to_coord(target.id)+1);
        return true;
    }
    else if (ctrl || !selectedcards[target.id.substr(7)])
    {
        if (!ctrl)
            deselectAll();
        var issel = target.className == 'cardtd selected';
        target.className = issel ? 'cardtd' : 'cardtd selected';
        if (issel)
            delete selectedcards[target.id.substr(7)];
        else
            selectedcards[target.id.substr(7)] = true;
        return true;
    }
    return false;
}
// Подсветка карточки в режиме вставки
function highlightCard(ev)
{
    if (pasteMode)
    {
        ev = DragMaster.fixEvent(ev);
        var t = searchCardTarget(ev);
        t.className = 'cardtd highlight';
        highlitCard = t;
    }
}
// Снятие подсветки карточки в режиме вставки
function unlightCard(ev)
{
    if (pasteMode)
    {
        ev = DragMaster.fixEvent(ev);
        var t = searchCardTarget(ev);
        t.className = 'cardtd';
    }
}
// Преобразование номера в координаты [лист, строка, столбец]
function to_coord(i)
{
    var to_k = Math.floor(i / nr / nc);
    var to_i = Math.floor((i / nc) % nr);
    var to_j = Math.floor(i % nc);
    return [to_k, to_i, to_j];
}
// Преобразование номера в строку с координатами лист_строка_столбец
function to_coord_id(i)
{
    return to_coord(i).join('_');
}
// Преобразование id элемента (.*лист_строка_столбец.*) в номер
function id_to_coord(id)
{
    var m = /(\d+)_(\d+)_(\d+)/.exec(id.substr(7));
    return (parseInt(m[1])*nr+parseInt(m[2]))*nc+parseInt(m[3]);
}
// Удалить(cut=0)/вырезать(cut=1) выделенные карточки
function deleteSelectedCards(cut)
{
    var shift = 0;
    var coord = 0;
    var n = nr * nc * np;
    if (cut)
    {
        cuttedcards = [];
        cuttedids = [];
    }
    for (var k = 0; k < np; k++)
    {
        for (var i = 0; i < nr; i++)
        {
            for (var j = 0; j < nc; j++, coord++)
            {
                var s = selectedcards[k+'_'+i+'_'+j];
                var e = document.getElementById('cardtd_'+k+'_'+i+'_'+j);
                if (!e)
                    continue;
                if (s)
                {
                    if (cut)
                    {
                        cuttedcards.push(e.innerHTML);
                        cuttedids.push(idlist[coord]);
                    }
                    shift++;
                }
                else if (shift > 0)
                {
                    var to = to_coord_id(coord-shift);
                    document.getElementById('cardtd_'+to).innerHTML = e.innerHTML;
                    idlist[coord-shift] = idlist[coord];
                }
                else if (coord + shift < n)
                    continue;
                // во всех трёх случаях - если выделена для
                // удаления, если перемещена в другую, или
                // если находится в конце - очищаем
                e.innerHTML = emptycell;
                idlist[coord] = '';
            }
        }
    }
    if (cut && cuttedids.length)
        document.getElementById('cut_status').innerHTML = 'Вырезано '+cuttedids.length+' карточек.';
    deselectAll();
    document.getElementById('idlist_value').value = idlist.join(',');
}
// Перейти в режим выбора места вставки вырезанных карточек
function pasteCards()
{
    if (!pasteMode)
    {
        if (!cuttedids.length)
        {
            alert('Сначала выделите и вырежьте какие-нибудь карточки!');
            return;
        }
        alert('Кликните на карточку, после которой нужно вставить вырезанное, либо на кнопку "В начало".');
        deselectAll();
        pasteMode = true;
        document.getElementById('btn_paste_beg').style.display = '';
    }
}
// Вставить вырезанные карточки в позицию coord
function doPasteCards(coord)
{
    stopPaste();
    var nx = cuttedids.length;
    if (nx <= 0)
        return;
    if (!document.getElementById('cardtd_'+to_coord_id(coord)))
    {
        alert('Некуда вставлять');
        return;
    }
    var n = nr * nc * np;
    var from, to;
    for (var i = n-nx-1; i >= coord; i--)
    {
        from = to_coord_id(i);
        to = to_coord_id(i+nx);
        if (document.getElementById('cardtd_'+from) &&
            document.getElementById('cardtd_'+to))
        {
            document.getElementById('cardtd_'+to).innerHTML =
                document.getElementById('cardtd_'+from).innerHTML;
            idlist[i+nx] = idlist[i];
        }
    }
    for (var i = 0; i < nx; i++)
    {
        to = to_coord_id(i+coord);
        document.getElementById('cardtd_'+to).innerHTML = cuttedcards[i];
        document.getElementById('cardtd_'+to).className = 'cardtd selected';
        idlist[i+coord] = cuttedids[i];
        selectedcards[to] = true;
    }
    cuttedids = [];
    cuttedcards = [];
    document.getElementById('cut_status').innerHTML = '';
    document.getElementById('idlist_value').value = idlist.join(',');
}
// Выйти из режима вставки
function stopPaste()
{
    if (highlitCard)
        highlitCard.className = 'cardtd';
    document.getElementById('btn_paste_beg').style.display = 'none';
    pasteMode = false;
}
// Обработчик keydown/keyup, записывает состояние ctrl
function ctrlDown(ev)
{
    if (ev.keyCode == 17)
    {
        ctrl = ev.type == 'keydown';
        return true;
    }
    return false;
}
// Попробовать найти по иерархии карточку (id=cardtd_*)
function searchCardTarget(e)
{
    var nt = e._target, i;
    while (nt && (!nt.attributes ||
        !(i = nt.attributes.getNamedItem('id')) ||
        i.value.substr(0, 7) != 'cardtd_'))
        nt = nt.parentNode;
    return nt;
}
// Попробовать найти по иерархии кнопку (id=btn_*)
function searchButtonTarget(e)
{
    var nt = e._target, i;
    while (nt && (!nt.attributes ||
        !(i = nt.attributes.getNamedItem('id')) ||
        i.value.substr(0, 4) != 'btn_'))
        nt = nt.parentNode;
    return nt;
}
// Попробовать найти по иерархии что-нибудь кроме карточек и кнопок,
// по сути это только <input> и <textarea>, клик по чему не сбрасывает всё нахрен
function searchNonResetTarget(e)
{
    var nt = e._target, i;
    while (nt && (!(i = nt.nodeName.toLowerCase()) ||
        !(i == 'input' || i == 'textarea')))
        nt = nt.parentNode;
    return nt;
}
// Глобальный обработчик события mouseup
function mouseUpHandler(e)
{
    e = DragMaster.fixEvent(e);
    var t;
    if (searchNonResetTarget(e)) {}
    else if (t = searchCardTarget(e))
        selectCard(e, t);
    else if (t = searchButtonTarget(e))
        buttonHandler(e, t);
    else
        resetAll();
    return true;
}
// Класс "Перетаскиваемая карточка", унаследованный от "Перетаскиваемого объекта"
// Логика - при начале перетаскивания удаляется из родительского элемента,
// который есть строка таблицы, и вместо себя вставляет пустую ячейку.
// При окончании перетаскивания сначала возвращается на место, а потом дёргает
// обработчики вырезания и вставки карточек.
var CardDragObject = function(e)
{
    DragObject.call(this, e);
    this.n = id_to_coord(e.id);
};
CardDragObject.prototype = new DragObject();
CardDragObject.prototype.onDragStart = function() {
    this.tmp = document.createElement('td');
    this.element.style.opacity = 0.5;
    this.element.parentNode.insertBefore(this.tmp, this.element);
    this.element.parentNode.parentNode.parentNode.appendChild(this.element);
};
CardDragObject.prototype.onDragSuccess = function(target, pos) {
    this.tmp.parentNode.insertBefore(this.element, this.tmp);
    this.tmp.parentNode.removeChild(this.tmp);
    if (!selectedcards[this.element.id.substr(7)])
    {
        deselectAll();
        selectedcards[this.element.id.substr(7)] = true;
    }
    var to = id_to_coord(target.element.id)+1;
    var w = target.element.scrollWidth;
    if (pos.x < w/2)
        to--;
    var decr = 0;
    for (var i = 0; i < to; i++)
        if (selectedcards[to_coord_id(i)])
            decr++;
    to -= decr;
    deleteSelectedCards(true);
    doPasteCards(to);
};
CardDragObject.prototype.onDragFail = function() {
    this.tmp.parentNode.insertBefore(this.element, this.tmp);
    this.tmp.parentNode.removeChild(this.tmp);
};
// Каждая карточка также является целью перетаскивания
// Тут вся логика - подсветить левый/правый край в зависимости
// от положения мышки
var CardDropTarget = function(e)
{
    DropTarget.call(this, e);
    this.n = id_to_coord(e.id);
};
CardDropTarget.prototype = new DropTarget();
CardDropTarget.prototype.onLeave = function()
{
    this.element.style.border = '1px dashed black';
};
CardDropTarget.prototype.onMove = function(pos)
{
    var w = this.element.scrollWidth;
    this.element.style.border = '1px dashed black';
    if (pos.x < w/2)
        this.element.style.borderLeft = '5px solid red';
    else
        this.element.style.borderRight = '5px solid red';
};
// 2 если hash пуст
// 1 если в нём один ключ key
// 0 если в нём есть ключ, не равный key
function isEmptyHash(hash, key)
{
    var empty = 2;
    for (var i in hash)
    {
        if (key !== undefined && i == key)
            empty = 1;
        else
        {
            empty = 0;
            break;
        }
    }
    return empty;
}
// Установка обработчиков событий:
for (var k = 0; k < np; k++)
{
    for (var i = 0; i < nr; i++)
    {
        for (var j = 0; j < nc; j++)
        {
            var e = document.getElementById('cardtd_'+k+'_'+i+'_'+j);
            if (e)
            {
                addListener(e, 'mouseover', highlightCard);
                addListener(e, 'mouseout', unlightCard);
                new CardDragObject(e);
                new CardDropTarget(e);
            }
        }
    }
}
addListener(document, "mousedown", guessButton);
addListener(document, "mouseup", mouseUpHandler);
addListener(document, "keydown", ctrlDown);
addListener(document.getElementById('addbugs'), "keypress", addNewIfEnter);
addListener(document, "keyup", ctrlDown);
