// -*- coding: utf-8 -*-
//@require base.js

/* convention de nommage:

    funcTodo: fonction qui agit sur ou retourne une instance de Tiddler
    funcTODO: fonction qui agit sur ou affiche un élément visuel.
*/

//////////////////////////////////////////////////
// Gestion des TODOs

function getAllTodos(types, expectedTags) {
    // Lister les todos des types spécifiés ("TODO" ou "DONE"), et qui contiennent
    // tous les tags de expectedTags.
    if (!expectedTags) expectedTags = [];

    var tiddlersMatchingTags = function(tiddlers, exceptedTags, sortBy, reverse) {
        // retourner les tiddlers de tiddlers qui contiennent tous les tags de expectedTags
        var result = [];
        for(var i = 0; i < tiddlers.length; i++) {
            if (expectedTags.length) {
                var allTagsOk = true;
                for (var j = 0; j < expectedTags.length; j++) {
                    if (! tiddlers[i].tags.containsTag(expectedTags[j])) {
                        allTagsOk = false;
                        break;
                    }
                }
                if (!allTagsOk) continue;
            }

            result.push(tiddlers[i]);
        }

        if (!sortBy) sortBy = "title";
        result.sortBy(sortBy, reverse);

        return result;
    }

    var result = [];
    for (var i = 0; i < types.length; i++) {
    	var type = types[i];
		var reverse = false;
    	if (type.charAt(0) == "+") {
    		type = type.substr(1);
    	} else if (type.charAt(0) == "-") {
    		type = type.substr(1);
    		var reverse = true;
    	}
        result = result.concat(tiddlersMatchingTags(store.getTaggedTiddlers(type), expectedTags, "title", reverse));
    }

    return result;
}

function getTodoText(tiddler) {
    // Retourner "[[todoTitle]] - firstLine"
    if (!(tiddler instanceof Tiddler)) tiddler = store.tiddlers[tiddler];
    if (!(tiddler instanceof Tiddler)) return;

    var title = tiddler.title;
    var text = "[[" + title + "]]";
    var line = Tiddlers.getFirstLine(tiddler);
    if (line != "") text += " - " + line;

    return text;
}

//////////////////////////////////////////////////
// Les macros pour gérer les TODOs

// Créer un nouveau TODO
config.macros.newTODO = {label: "new TODO", prompt: "Create a new TODO tiddler", accessKey: "T"};
config.macros.newTODO.handler = function(place, macroName, params) {
    if (!readOnly) {
        createTiddlyButton(place, this.label, this.prompt, this.onClick, null, null, this.accessKey);
    }
}
config.macros.newTODO.onClick = function(e) {
    e = ClickHandler.getEvent(e);

    var now = new Date();
    var todoname = now.formatString("TODO-YYYY0MM0DD");
    var title = todoname + "-" + (store.getTaggedTiddlers(todoname).length + 1);

    clearMessage();
    displayTiddler(null, title, 2, null, null, false, false);
    var body = document.getElementById("editorBody" + title);
    if (body) body.focus();

    var tagsBox = document.getElementById("editorTags" + title);
    if(tagsBox) {
        var tags = tagsBox.value.splitTags();
        tags.removeTag("DONE").addTag("TODO").addTag(todoname);
        tagsBox.value = tags.joinTags();
    }

    return ClickHandler.end(e);
}

// Créer un nouveau bug
config.macros.newBUG = {label: "new BUG", prompt: "Create a new BUG tiddler", accessKey: "B"};
config.macros.newBUG.handler = function(place, macroName, params) {
    if (!readOnly) {
        createTiddlyButton(place, this.label, this.prompt, this.onClick, null, null, this.accessKey);
    }
}
config.macros.newBUG.onClick = function(e) {
    e = ClickHandler.getEvent(e);

    var now = new Date();
    var bugname = now.formatString("BUG-YYYY0MM0DD");
    var title = bugname + "-" + (store.getTaggedTiddlers(bugname).length + 1);

    clearMessage();
    displayTiddler(null, title, 2, null, null, false, false);
    var body = document.getElementById("editorBody" + title);
    if (body) body.focus();

    var tagsBox = document.getElementById("editorTags" + title);
    if(tagsBox) {
        var tags = tagsBox.value.splitTags();
        tags.removeTag("DONE").addTag("BUG").addTag(bugname);
        tagsBox.value = tags.joinTags();
    }

    return ClickHandler.end(e);
}

// marquer un TODO comme étant effectué
config.macros.markDONE = {label: "mark as DONE", prompt: "Mark a TODO/BUG tiddler as DONE", accessKey: "D"};
config.macros.markDONE.handler = function(place, macroName, params) {
    if (!readOnly) {
        createTiddlyButton(place, this.label, this.prompt, this.onClick, null, null, this.accessKey);
    }
}
config.macros.markDONE.onClick = function(e) {
    e = ClickHandler.getEvent(e);

    if (currentTiddler) {
        var title = currentTiddler;

        var tags = Tiddlers.valueOf(title).tags;
        if (tags.containsTag("TODO") || tags.containsTag("BUG")) {
            clearMessage();
            displayTiddler(null, title, 2, null, null, false, false);

            var tagsBox = document.getElementById("editorTags" + title);
            if(tagsBox) {
                var tags = tagsBox.value.splitTags();
                tags.removeTag("TODO").removeTag("BUG").addTag("DONE");
                tagsBox.value = tags.joinTags();

                var body = document.getElementById("editorBody" + title);
                var text = body.value;
                if (text.length > 0 && text.charAt(text.length - 1) != "\n") text += "\n";
                text += "\nFAIT le " + new Date().formatString("0DD/0MM/YYYY");
                body.value = text;
                body.focus();
            }
        }
    }

    return ClickHandler.end(e);
}

// Afficher un lien vers un TODO et une description du TODO
config.macros.todoLink = {};
config.macros.todoLink.handler = function(place, macroName, params) {
    if (!params.length) return;

    wikify(getTodoText(params[0]), place);
}

// Lister tous les TODOs, ceux à faire et ceux qui sont faits
config.macros.listTODOs = {};
config.macros.listTODOs.handler = function(place, macroName, params) {
    var todos = getAllTodos(["-TODO", "BUG", "DONE"], params);
    if (todos.length) {
        var list = document.createElement("ul");
        place.appendChild(list);

        for(var i = 0; i < todos.length; i++) {
            item = document.createElement("li");
            list.appendChild(item);

            var element = item;
            var tags = todos[i].tags;
            if (tags.containsTag("DONE")) {
                striked = createTiddlyElement(element, "strike");
                element = striked;
            }

            wikify(getTodoText(todos[i]), element);
        }
    } else {
        place.appendChild(document.createTextNode("Il n'y a pas de TODOs en cours"));
    }
}
function refreshTODOs(hint) {
    refreshTiddler("TODOs");
}
config.notifyTiddlers.push({name: null, notify: refreshTODOs});
config.shadowTiddlers.TODOs = "<<listTODOs>>";

// Créer une nouvelle release
config.macros.newRelease = {label: "new release", prompt: "Create a new release", accessKey: "R"};
config.macros.newRelease.handler = function(place, macroName, params) {
    if (!readOnly) {
        createTiddlyButton(place, this.label, this.prompt, this.onClick, null, null, this.accessKey);
    }
}
config.macros.newRelease.onClick = function(e) {
    e = ClickHandler.getEvent(e);

    var now = new Date();
    var releasename = now.formatString("Release-YYYY0MM0DD");
    var title = releasename;
    var releases = store.getTaggedTiddlers(releasename);
    if (releases.length > 0) {
        title += "-" + releases.length;
    }

    clearMessage();
    displayTiddler(null, title, 2, null, null, false, false);
    var body = document.getElementById("editorBody" + title);
    if (body && body.value == "") {
        // ne créer le contenu que la première fois.
        var text = "release du " + now.formatString("0DD/0MM/YYYY") + "\nversion: \n";

        // lister tous les TODOs faits en cours
        var todos = getAllTodos(["DONE"]);
        for (var i = 0; i < todos.length; i++) {
            if (i == 0) text += "\n";
            text += "* <<todoLink " + todos[i].title + ">>\n";
        }

        body.value = text;
        body.focus();
    }

    var tagsBox = document.getElementById("editorTags" + title);
    if(tagsBox) {
        var tags = tagsBox.value.splitTags();
        tags.addTag("release").addTag(releasename);
        tagsBox.value = tags.joinTags();
    }

    return ClickHandler.end(e);
}
TiddlyWiki.prototype.saveTiddler_patchedByTODO = TiddlyWiki.prototype.saveTiddler;
TiddlyWiki.prototype.saveTiddler = function(title, newTitle, newBody, modifier, modified, tags) {
    var tiddler = this.saveTiddler_patchedByTODO(title, newTitle, newBody, modifier, modified, tags);

    if (tiddler.tags.containsTag("release")) {
        var todos = store.getTaggedTiddlers("DONE");
        for (var i = 0; i < todos.length; i++) {
            var todo = todos[i];
            todo.tags.removeTag("DONE").addTag("RELEASED");

            store.saveTiddler(todo.title, todo.title, todo.text, todo.modifier, undefined, todo.tags);
            refreshTiddler(todos[i].title);
        }
    }

    return tiddler;
}

function getVersionText(tiddler) {
    var version = "";
    var tiddler = Tiddlers.valueOf(tiddler);
    if (tiddler) {
        var re_version = new RegExp("^version:(.*)$", "gm");
        var matched = re_version.exec(tiddler.text);
        if (matched) {
            version = matched[1].trim();
        }
    }
    return version;
}

function getReleaseText(tiddler) {
    // Retourner "[[versionText|releaseTitle]] - firstLine"
    var text = "";
    var tiddler = Tiddlers.valueOf(tiddler);
    if (tiddler) {
        var title = tiddler.title;
        var version = getVersionText(tiddler);
        if (version != "") {
            text = "[[" + version + "|" + title + "]]";
        } else {
            text = "[[" + title + "]]";
        }
        var line = Tiddlers.getFirstLine(tiddler);
        if (line != "") text += " - " + line;
    }
    return text;
}

config.macros.listReleases = {};
config.macros.listReleases.handler = function(place, macroName, params) {
    var releases = store.getTaggedTiddlers("release");
    releases.sortBy("title", true);
    if (releases.length) {
        var list = document.createElement("ul");
        place.appendChild(list);

        var len = releases.length;
        if (len > 7) len = 7;
        for(var i = 0; i < len; i++) {
            item = document.createElement("li");
            list.appendChild(item);

            wikify(getReleaseText(releases[i]), item);
        }
    } else {
        place.appendChild(document.createTextNode("Il n'y a pas de releases en cours"));
    }
}
function refreshReleases(hint) {
    refreshTiddler("Releases");
}
config.notifyTiddlers.push({name: null, notify: refreshReleases});
config.shadowTiddlers.Releases = "<<listReleases>>";

//////////////////////////////////////////////////
// Le formatter todo
// ce formatter affiche une case à cocher qu'il est possible de cocher pour signifier que l'action
// associée à la case à cocher est effectuée
// format: []  pour les actions non faites
//         [x] pour les actions faites

var lastTodoId;

var displayTiddler_patchedByTodo = window.displayTiddler;
window.displayTiddler = function(src, title, state, highlightText, highlightCaseSensitive, animate, slowly) {
    // réinitialiser le numéro du dernier todo à chaque affichage
    lastTodoId = 0;
    displayTiddler_patchedByTodo(src, title, state, highlightText, highlightCaseSensitive, animate, slowly);
}

config.formatters.push({
    name: "todo",
    match: "\\[\\]|\\[[xX\\*]\\]",
    terminator: "\\n|\\|",
    handler: function(w) {
        var formatter = this;
        var place = w.output;

        var tiddler = findContainingTiddler(place);
        if (!tiddler) return;

        var title = tiddler.id.substr(7);
        var todoId = lastTodoId++;
        var cbId = "todo" + todoId + "@" + title;
        var wrapperId = "todoWrapper" + todoId + "@:" + title;

        var matched = w.source.substr(w.matchStart, w.matchLength);
        var done = matched != "[]";
        var onClick = function(e) {
            e = ClickHandler.getEvent(e);

            if (!readOnly) {
                // lire les infos sur le tiddler
                var body = store.getTiddlerText(title);
                var tags = Tiddlers.getTags(title);

                // trouver le texte [] qui correspond à todoId
                var id = 0;
                var re_macro = new RegExp(formatter.match, "mg");
                do {
                    var mo = re_macro.exec(body);
                    if (mo) {
                        if (id++ == todoId) break;
                    }
                } while (mo);

                // changer la valeur
                if (mo) {
                    var done = mo[0] == "[]";
                    body = body.substr(0, mo.index) + (done? "[x]": "[]") + body.substr(mo.index + mo[0].length);

                    lastTodoId = 0;
                    store.saveTiddler(title, title, body, config.options.txtUserName, new Date(), tags);
                    if (config.options.chkAutoSave) saveChanges();
                    refreshTiddler(title);
                }
            }

            return ClickHandler.end(e);
        };

        var span = createTiddlyElement(place, "span");
        var cb = createTiddlyElement(span, "input");
        cb.type = "checkbox";
        cb.id = cbId;
        cb.checked = done;
        cb.onclick = onClick;
        var label = createTiddlyElement(span, "label");
        label.setAttribute("for", cbId);
        var wrapper = createTiddlyElement(label, "span", wrapperId);
        if (done) {
            wrapper.setAttribute("style", "text-decoration: line-through;");
        }
        w.subWikify(wrapper, this.terminator);
        w.nextMatch--; // ne pas manger le terminator
    }
});

//////////////////////////////////////////////////
// L'interface par défaut

config.shadowTiddlers.MainMenu = "\
<<calendar>>\n\
<<newTiddler>>\n\
----\n\
[[Releases]]\n\
<<newRelease>>\n\
----\n\
[[TODOs]]\n\
<<newTODO>>\n\
<<newBUG>>\n\
<<markDONE>>\
";
config.shadowTiddlers.DefaultTiddlers = "\
Releases\n\
TODOs\n\
EnCours\
";
config.shadowTiddlers.SiteTitle = "TODO";
config.shadowTiddlers.SiteSubtitle = "Gestion de tâches";

config.shadowTiddlers.KeyShortcuts += "\
|Alt + T|Créer un nouveau TODO|\n\
|Alt + B|Créer un nouveau BUG|\n\
|Alt + D|Terminer un TODO|\n\
|Alt + R|Créer une nouvelle release|\n\
";
