/*  Twcouch, version 0.8
 *   - TiddlyWiki plugin for CouchDB storage engine
 *  (c) 2008 Yoshiyuki Sugimoto (opengroove.com)
 *
 *  @requires TiddlyWiki >= v2.3.0
 *  @requires jQuery >= v1.2.1
 *  @requires jqCouchDB >= v2.0.2
 *  @requires CouchDB >= 7.2
 *
 *  Twcouch is freely distributable under
 *  the terms of an MIT-style license.
 *  For details, see the web site: http://NOTE-ENTER-URL.com
 *
 *--------------------------------------------------------------------------*/

config.macros.couchdb = {
    servertype: 'couchdb',
    hostname: 'servername',
    dbname: 'database'
}

/* wrap jqcouch functions */
function jqCouch_map_error(req) {
    var res = req.responseText ? "(" + req.responseText + ")" : null;
    var results = {
        status: req.status,
        response: eval(res)
    }
    return results;
}

/* wrap TiddlyWiki functions */
TiddlyWiki.prototype.saveTiddler = function(title,newTitle,newBody,modifier,modified,tags,fields,clearChangeCount,created)
{
    var tiddler = this.fetchTiddler(title);
    if(tiddler) {
        created = created ? created : tiddler.created; // Preserve created date
        this.deleteTiddler(title);
    } else {
        created = created ? created : modified;
        tiddler = new Tiddler();
    }
    tiddler.set(newTitle,newBody,modifier,modified,tags,created,fields);
    this.addTiddler(tiddler);
    if(clearChangeCount)
        tiddler.clearChangeCount();
    else
        tiddler.incChangeCount();
    if(title != newTitle)
        this.notify(title,true);
    this.notify(newTitle,true);
    this.setDirty(true);

    // add to save CouchDB
    twc.saveTiddler(title, tiddler);

    return tiddler;
};

TiddlyWiki.prototype.removeTiddler = function(title)
{
    var tiddler = this.fetchTiddler(title);
    if(tiddler) {
        this.deleteTiddler(title);
        this.notify(title, true);
        this.setDirty(true);
        twc.deleteTiddler(twc.toJson(tiddler));
    }
}

/*
 * store is always clean.
 */
function confirmExit()
{
    store.setDirty(false);
    hadConfirmExit = true;
    if ((store && store.isDirty && store.isDirty()) || (story && story.areAnyDirty && story.areAnyDirty()))
            return config.messages.confirmExit;
}

function checkUnsavedChanges()
{
    store.setDirty(false);
    if (store && store.isDirty && store.isDirty() && window.hadConfirmExit === false) {
        if (confirm(config.messages.unsavedChangesWarning))
            if (story && story.areAnyDirty && story.areAnyDirty())
                return config.messages.confirmExit;
            saveChanges();
    }
}

/* creating messages */
config.messages.couchdb = {
    saveSuccess: "Saved to CouchDB.",
    saveFailed: "Failed to save CouchDB.",
    notSaved: "Canceled to save.",
    saveConfirm: "Document has been changed.\nOverwrite it?",
    connectionFailed: "Failed to connect to database."
}

/*
 * TWCouch core functions
 *
 */
function TWCouch() {
    this.Version = '0.8',
    this.db = $.jqCouch.connection("db");
    this.doc = $.jqCouch.connection("doc");
    this.view = $.jqCouch.connection("view");
    this.dbname = config.macros.couchdb.dbname;
    install(this.dbname, this.db, this.view);
    this.messages = config.messages.couchdb;
    function install(dbname, db, view) {
        // create views if it's not exist.
        if (view.exists(dbname, "views") === false) {
            var views = {
                _id: "views",
                views: {
                    all: function(doc) { map(null, doc) },
                }
            }
            view.save(dbname, views);
        }
    }
}

TWCouch.insertToDiv = function(tiddler, store) {
    var div = document.createElement("div");
    var modified = (tiddler.modified != "Invalid Date") ? new Date(tiddler.modified): new Date();
    var created = (tiddler.created != "Invalid Date") ? new Date(tiddler.created): new Date();
    div.setAttribute("title", tiddler.title.htmlEncode());
    div.setAttribute("modified", modified.convertToYYYYMMDDHHMM());
    div.setAttribute("created", created.convertToYYYYMMDDHHMM());
    if (tiddler.modifier != undefined) div.setAttribute("modifier", tiddler.modifier);
    if (tiddler.tags != undefined) div.setAttribute("tags", tiddler.tags.join(" "));
    if (tiddler.text != undefined) div.innerHTML = "<pre>" + tiddler.text.htmlEncode() + "</pre>";
    $.each(tiddler.fields, function(k, v){ div.setAttribute(k, v.escapeLineBreaks().htmlEncode()) });
    store.appendChild(div);
}

TWCouch.toTiddler = function(jtiddler) {
    var title = jtiddler.id;
    var _rev = jtiddler.value._rev;
    var title = jtiddler.value.Title;
    var text = jtiddler.value.Text.replace(/\r/mg, "").htmlDecode();
    var modifier = jtiddler.value.Modifier;
    var modified = new Date(jtiddler.value.Modified);
    var created = new Date(jtiddler.value.Created);
    var fields = jtiddler.value.Fields;
    fields.rev = _rev;
    var tags = jtiddler.value.Tags;
    var tiddler = new Tiddler();
    tiddler.assign(title, text, modifier, modified, tags, created, fields);
    return tiddler;
};

TWCouch.prototype.saveTiddler = function(title, tiddler) {
    var prev = this.fetchTiddler(tiddler.title);

    if (title == tiddler.title) {
        if (! this.exists(prev) || prev._rev == tiddler.fields['rev']) {
            this.save(tiddler);
        } else {
            if (window.confirm(this.messages.saveConfirm)) {
                tiddler.fields['rev'] = prev._rev;
                this.save(tiddler);
            } else {
                displayMessage(this.messages.notSaved);
            }
        }
    } else {
        var del = this.fetchTiddler(title);
        if (del._id == title) this.deleteTiddler(del);

        tiddler.fields['rev'] = (this.exists(prev)) ? prev._rev : null;
        this.save(tiddler);
    }
}

TWCouch.prototype.exists = function(tiddler) {
    return ! (tiddler.error == "not_found");
}

TWCouch.prototype.save = function(tiddler) {
    var td = this.toJson(tiddler);
    td = this.doc.save(this.dbname, td);
    if (td._id === false) {
        displayMessage(this.messages.saveFailed);
        var ret = false;
    } else {
        tiddler.fields['rev'] = td.rev;
        displayMessage(this.messages.saveSuccess);
        var ret = true;
    }
    return ret;
}

TWCouch.prototype.deleteTiddler = function(tiddler) {
    td = this.doc.del(config.macros.couchdb.dbname, tiddler);
    return td;
}

TWCouch.prototype.toJson = function(tiddler) {
    var json = {
        _id: encodeURIComponent(tiddler.title),
        _rev: (tiddler.fields.rev != undefined) ? tiddler.fields.rev : "",
        Title: tiddler.title,
        Text: tiddler.text,
        Modifier: tiddler.modifier,
        Modified: tiddler.modified.toString(),
        Created: tiddler.created.toString(),
        Fields: tiddler.fields,
        Tags: tiddler.tags
    };
    return json;
};


TWCouch.prototype.fetchTiddler = function(title) {
    return this.doc.get(this.dbname + "/" + encodeURIComponent(title));
}

TWCouch.prototype.search = function(words) {
    //
    // TODO: to be implemented.
    //
    return [];
}

TWCouch.prototype.loadFromCouch = function() {
    var tiddlers = [];
    var store = document.getElementById("storeArea");
    $.each(this.view.get(this.dbname, "views/all").rows, function() {
        tiddler = TWCouch.toTiddler(this);
        TWCouch.insertToDiv(tiddler, store);
    });
}

var twc = new TWCouch();
twc.loadFromCouch();

