/**
 * jqCouchDB - jQuery plugin for couchdb connections
 * @requires jQuery > v1.0.3
 *
 * http://protoblogr.net
 *
 * Copyright (c) 2007 Jerry Jalava (protoblogr.net)
 * Dual licensed under the MIT and GPL licenses:
 * http://www.opensource.org/licenses/mit-license.php
 * http://www.gnu.org/licenses/gpl.html
 *
 * Version: 1.0.4
 *
 * 
 * Example usages:
 *
var db_exists = false;
var jqcouch_db = $.jqcouch.db;
var info = null;
jqcouch_db.on_error = function(request, caller) { db_exists = false; };
jqcouch_db.on_success = function(data, caller) { db_exists = data.exists; };
jqcouch_db.exists(/some_database);

if (! db_exists)
{
    $.jqcouch.db.create(/some_database);
}

jqcouch_db.on_success = function(data, caller)
{
    info = data;
}
jqcouch_db.info(/some_database);

---
 *
 * You don't have to override the on_error and on_success methods if you don't wan't to...
 *

$.jqcouch.doc.put('/some_database/note1', {
    name: 'Test',
    title: 'Test title'
});
$.jqcouch.doc.on_success = function(data, caller) {
    alert(data.value);
    $.jqcouch.doc.on_success = $.jqcouch.doc._original_on_success;
}
$.jqcouch.doc.get('/some_database/note1');

 *
 * You could also override the global function:
 *
$.jqcouch.on_error = function(request,caller)
{
    some_other_error_func(request, caller);
};
$.jqcouch.on_success = function(data,caller)
{
    some_other_success_func(data, caller);
}
 */

(function($){

    $.jqcouch = $.jqcouch || {};
    
    $.jqcouch.parseJSON = function (json_str)
    {
    	try
    	{
            return !(/[^,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]/.test(
                    json_str.replace(/"(\\.|[^"\\])*"/g, ''))) &&
                eval('(' + json_str + ')');
        }
        catch (e)
        {
            return false;
        }
    }
    $.jqcouch.toJSON = function (item,item_type)
    {
    	var m = {
                '\b': '\\b',
                '\t': '\\t',
                '\n': '\\n',
                '\f': '\\f',
                '\r': '\\r',
                '"' : '\\"',
                '\\': '\\\\'
            },
            s = {
                arr: function (x) {
                    var a = ['['], b, f, i, l = x.length, v;
                    for (i = 0; i < l; i += 1) {
                        v = x[i];
                        v = conv(v);
                        if (typeof v == 'string') {
                            if (b) {
                                a[a.length] = ',';
                            }
                            a[a.length] = v;
                            b = true;
                        }
                    }
                    a[a.length] = ']';
                    return a.join('');
                },
                bool: function (x) {
                    return String(x);
                },
                nul: function (x) {
                    return "null";
                },
                num: function (x) {
                    return isFinite(x) ? String(x) : 'null';
                },
                obj: function (x) {
                    if (x) {
                        if (x instanceof Array) {
                            return s.arr(x);
                        }
                        var a = ['{'], b, f, i, v;
                        for (i in x) {
                            v = x[i];
                            v = conv(v);
                            if (typeof v == 'string') {
                                if (b) {
                                    a[a.length] = ',';
                                }
                                a.push(s.str(i), ':', v);
                                b = true;
                            }
                        }
                        a[a.length] = '}';
                        return a.join('');
                    }
                    return 'null';
                },
                str: function (x) {
                    if (/["\\\x00-\x1f]/.test(x)) {
                        x = x.replace(/([\x00-\x1f\\"])/g, function(a, b) {
                            var c = m[b];
                            if (c) {
                                return c;
                            }
                            c = b.charCodeAt();
                            return '\\u00' +
                                Math.floor(c / 16).toString(16) +
                                (c % 16).toString(16);
                        });
                    }
                    return '"' + x + '"';
                }
            };
            conv = function (x) {
                var itemtype = typeof x;
            	switch(itemtype) {
            		case "array":
            		  return s.arr(x);
            		  break;
            		case "object":
            		  return s.obj(x);
            		  break;
            		case "string":
            		  return s.str(x);
            		  break;
            		case "number":
            		  return s.num(x);
            		  break;
            		case "null":
            		  return s.nul(x);
            		  break;
            		case "boolean":
            		  return s.bool(x);
            		  break;
            	}
            }

    	var itemtype = item_type || typeof item;
    	switch(itemtype) {
    		case "array":
    		  return s.arr(item);
    		  break;
    		case "object":
    		  return s.obj(item);
    		  break;
    		case "string":
    		  return s.str(item);
    		  break;
    		case "number":
    		  return s.num(item);
    		  break;
    		case "null":
    		  return s.nul(item);
    		  break;
    		case "boolean":
    		  return s.bool(item);
    		  break;				
    		default:
    		  throw("Unknown type for $.jqcouch.toJSON");
    		}
    }
    
    $.jqcouch.on_error = function(request, caller)
    {
        var error = "Error occured while talking with server!\n";
        error += "request.status: "+request.status+"\n";
        
        if (caller != undefined)
        {
            error += "caller.method: "+caller.method+"\n";
            error += "caller.action: "+caller.action+"\n";
            if (caller.args != undefined)
            {
                error += "caller.args: "+caller.args.toString()+"\n";                
            }
        }

        //error += "request.responseText: "+request.responseText+"\n";

        var response = eval("(" + request.responseText + ")");
        error += "response.error.reason: "+response.error.reason+"\n";
    };
    $.jqcouch.on_success = function(data, caller)
    {
        alert("on_success method is not specified!");
    };
    
    $.jqcouch.generate_query_str = function(query_args)
    {
        var query_string = '';
        if (query_args != undefined)
        {
            query_string += '?';
            $.each(query_args, function(key,value){
                query_string += key + '=' + value + '&';
            });
            query_string = query_string.substr(0,query_string.length-1);
        }
        
        return query_string;
    };
    
    $.jqcouch.server_url = '/';
    
    $.jqcouch.ajax_setup = {
        type: "GET",
        global: false,
        cache: false,
        async: false,
        dataType: "json",
        contentType: 'application/json'
    };
    
    $.jqcouch.db = {
        exists: function(path)
        {
            var exists = false;
            var server_url = $.jqcouch.server_url;
            $.ajax({
                url: server_url + '_all_dbs',
                type: "GET",
                global: false,
                cache: false,
                async: false,
                dataType: "json",
                contentType: 'application/json',
                error: function(request){
                    $.jqcouch.db.on_error(request, {
                        method: 'jqcouch.db.exists',
                        action: '_all_dbs',
                        args: {path: path}
                    });
                    return false;
                },
                success: function(data){
                    $.each(data, function(i,n){
                        if (n == path)
                        {
                            exists = true;
                        }
                    });
                    $.jqcouch.db.on_success({exists: exists}, {
                        method: 'jqcouch.db.exists',
                        action: '_all_dbs',
                        args: {path: path}
                    });
                    return exists;
                }
            });
        },
        get_all: function()
        {            
            var server_url = $.jqcouch.server_url;
            $.ajax({
                url: server_url + '_all_dbs',
                type: "GET",
                global: false,
                cache: false,
                async: false,
                dataType: "json",
                contentType: 'application/json',
                error: function(request){
                    $.jqcouch.db.on_error(request, {
                        method: 'jqcouch.db.exists',
                        action: '_all_dbs',
                        args: {path: path}
                    });
                    return false;
                },
                success: function(data){
                    $.jqcouch.db.on_success({exists: exists}, {
                        method: 'jqcouch.db.info',
                        action: '',
                        args: {path: path}
                    });
                    return true;
                }
            });
        },
        create: function(path)
        {
            if (path.substr(path.length-1,1) != '/')
            {
                path += '/';
            }
            
            var server_url = $.jqcouch.server_url;
            $.ajax({
                url: server_url + path,
                type: "PUT",
                global: false,
                cache: false,
                async: false,
                dataType: "json",
                contentType: 'application/json',
                error: function(request){
                    $.jqcouch.db.on_error(request, {
                        method: 'jqcouch.db.create',
                        action: '',
                        args: {path: path}
                    });
                    return false;
                },
                success: function(data){
                    $.jqcouch.db.on_success(data, {
                        method: 'jqcouch.db.create',
                        action: '',
                        args: {path: path}
                    });
                    return data.ok;
                }
            });
        },
        remove: function(path)
        {
            if (path.substr(path.length-1,1) != '/')
            {
                path += '/';
            }
            
            var server_url = $.jqcouch.server_url;
            $.ajax({
                url: server_url + path,
                type: "DELETE",
                global: false,
                cache: false,
                async: false,
                dataType: "json",
                contentType: 'application/json',
                error: function(request){
                    $.jqcouch.db.on_error(request, {
                        method: 'jqcouch.db.remove',
                        action: '',
                        args: {path: path}
                    });
                    return false;
                },
                success: function(data){
                    $.jqcouch.db.on_success(data, {
                        method: 'jqcouch.db.remove',
                        action: '',
                        args: {path: path}
                    });
                    return data.ok;
                }
            });
        },
        info: function(path)
        {   
            var server_url = $.jqcouch.server_url;
            $.ajax({
                url: server_url + path,
                type: "GET",
                global: false,
                cache: false,
                async: false,
                dataType: "json",
                contentType: 'application/json',
                error: function(request){
                    $.jqcouch.db.on_error(request, {
                        method: 'jqcouch.db.info',
                        action: '',
                        args: {path: path}
                    });
                    return false;
                },
                success: function(data){
                    $.jqcouch.db.on_success(data, {
                        method: 'jqcouch.db.info',
                        action: '',
                        args: {path: path}
                    });
                    return true;
                }
            });
        },
        
        on_error: function(request, caller)
        {
            $.jqcouch.on_error(request, caller);            
        },
        on_success: function(data, caller)
        {
            $.jqcouch.on_success(data, caller);
        },
        _original_on_error: this.on_error,
        _original_on_success: this.on_success
    };
    
    $.jqcouch.doc = {
        get: function(path, query_args)
        {
            path += $.jqcouch.generate_query_str(query_args);
            
            var server_url = $.jqcouch.server_url;
            $.ajax({
                url: server_url + path,
                type: "GET",
                global: false,
                cache: false,
                async: false,
                dataType: "json",
                contentType: 'application/json',
                error: function(request){
                    $.jqcouch.doc.on_error(request, {
                        method: 'jqcouch.doc.get',
                        action: '',
                        args: {path: path}
                    });
                    return false;
                },
                success: function(data){
                    $.jqcouch.doc.on_success(data, {
                        method: 'jqcouch.doc.get',
                        action: '',
                        args: {path: path}
                    });
                    return true;
                }
            });
        },
        get_all: function(path, query_args)
        {
            if (path.substr(path.length-1,1) != '/')
            {
                path += '/';
            }            
            path += '_all_docs';            
            path += $.jqcouch.generate_query_str(query_args);
            
            var server_url = $.jqcouch.server_url;
            $.ajax({
                url: server_url + path,
                type: "GET",
                global: false,
                cache: false,
                async: false,
                dataType: "json",
                contentType: 'application/json',
                error: function(request){
                    $.jqcouch.doc.on_error(request, {
                        method: 'jqcouch.doc.get_all',
                        action: '_all_docs',
                        args: {query_args: query_args}
                    });
                    return false;
                },
                success: function(data){
                    $.jqcouch.doc.on_success(data, {
                        method: 'jqcouch.doc.get_all',
                        action: '_all_docs',
                        args: {query_args: query_args}
                    });
                    return true;
                }
            });
        },
        save: function(path, data)
        {
            if (   data._id == undefined
                || data._id == '')
            {
                if (path.substr(path.length-1,1) != '/')
                {
                    path += '/';
                }
                $.jqcouch.doc.post(path, data);
            }
            else
            {
                data._rev = Number(data._rev);
                if (path.substr(path.length-1,1) != '/')
                {
                    path += '/';
                }
                $.jqcouch.doc.put(path + data._id, data);
            }
        },
        put: function(path, data)
        {            
            var server_url = $.jqcouch.server_url;
            $.ajax({
                url: server_url + path,
                type: "PUT",
                data: $.jqcouch.toJSON(data),
                processData: false,
                global: false,
                cache: false,
                async: false,
                dataType: "json",
                contentType: 'application/json',
                error: function(request){
                    $.jqcouch.doc.on_error(request, {
                        method: 'jqcouch.doc.put',
                        action: '',
                        args: {path: path}
                    });
                    return false;
                },
                success: function(data){
                    $.jqcouch.doc.on_success(data, {
                        method: 'jqcouch.doc.put',
                        action: '',
                        args: {path: path}
                    });
                    return true;
                }
            });
        },
        post: function(path, data)
        {            
            var server_url = $.jqcouch.server_url;
            $.ajax({
                url: server_url + path,
                type: "POST",
                global: false,
                cache: false,
                async: false,
                dataType: "json",
                contentType: 'application/json',
                data: $.jqcouch.toJSON(data),
                processData: false,
                error: function(request){
                    $.jqcouch.doc.on_error(request, {
                        method: 'jqcouch.doc.post',
                        action: '',
                        args: {path: path, data: data}
                    });
                    return false;
                },
                success: function(data){
                    $.jqcouch.doc.on_success(data, {
                        method: 'jqcouch.doc.post',
                        action: '',
                        args: {path: path, data: data}
                    });
                    return true;
                }
            });
        },
        del: function(path, revision)
        {
            if (revision != undefined)
            {
                path += '?rev=' + revision;                
            }
            
            var server_url = $.jqcouch.server_url;
            $.ajax({
                url: server_url + path,
                type: "DELETE",
                global: false,
                cache: false,
                async: false,
                dataType: "json",
                contentType: 'application/json',
                error: function(request){
                    $.jqcouch.doc.on_error(request, {
                        method: 'jqcouch.doc.del',
                        action: '',
                        args: {path: path}
                    });
                    return false;
                },
                success: function(data){
                    $.jqcouch.doc.on_success(data, {
                        method: 'jqcouch.doc.del',
                        action: '',
                        args: {path: path}
                    });
                    return true;
                }
            });
        },
        
        on_error: function(request, caller)
        {
            $.jqcouch.on_error(request, caller);            
        },
        on_success: function(data, caller)
        {
            $.jqcouch.on_success(data, caller);
        },
        _original_on_error: this.on_error,
        _original_on_success: this.on_success
    };
    
    $.jqcouch.view = {
        exists: function(path, name)
        {
            var exists = false;
            var rev = null;
            
            if (path.substr(path.length-1,1) != '/')
            {
                path += '/';
            }
            path += '_all_docs';
            
            var server_url = $.jqcouch.server_url;
            $.ajax({
                url: server_url + path,
                type: "GET",
                global: false,
                cache: false,
                async: false,
                dataType: "json",
                contentType: 'application/json',
                error: function(request){
                    $.jqcouch.view.on_error(request, {
                        method: 'jqcouch.view.exists',
                        action: '_all_docs',
                        args: {path: path}
                    });
                    return false;
                },
                success: function(data){
                    $.each(data.rows, function(i,n){
                        if (n._id == name)
                        {
                            exists = true;
                            rev = n._rev;
                        }
                    });
                    $.jqcouch.view.on_success({exists: exists, rev: rev}, {
                        method: 'jqcouch.view.exists',
                        action: '_all_docs',
                        args: {path: path}
                    });
                    return exists;
                }
            });
        },
        get: function(path, query_args)
        {
            $.jqcouch.doc.on_error = $.jqcouch.view.on_error;
            $.jqcouch.doc.on_success = $.jqcouch.view.on_success;
            $.jqcouch.doc.get(path, query_args);
        },
        save: function(path, data)
        {            
            if (   data._id == undefined
                || data._id == '')
            {
                return false;
            }
            
            if (   data._rev == undefined
                || data._rev == '')
            {
                if (path.substr(path.length-1,1) != '/')
                {
                    path += '/';
                }
                $.jqcouch.view.post(path, data);
            }
            else
            {
                data._rev = Number(data._rev);
                if (path.substr(path.length-1,1) != '/')
                {
                    path += '/';
                }
                $.jqcouch.view.put(path + data._id, data);
            }
        },
        post: function(path, data)
        {            
            var server_url = $.jqcouch.server_url;
            $.ajax({
                url: server_url + path,
                type: "POST",
                global: false,
                cache: false,
                async: false,
                dataType: "json",
                contentType: 'application/json',
                data: $.jqcouch.toJSON(data),
                processData: false,
                error: function(request){
                    $.jqcouch.view.on_error(request, {
                        method: 'jqcouch.doc.post',
                        action: '',
                        args: {path: path}
                    });
                    return false;
                },
                success: function(data){
                    $.jqcouch.view.on_success(data, {
                        method: 'jqcouch.doc.post',
                        action: '',
                        args: {path: path}
                    });
                    return true;
                }
            });
        },
        put: function(path, data)
        {
            $.jqcouch.doc.on_error = $.jqcouch.view.on_error;
            $.jqcouch.doc.on_success = $.jqcouch.view.on_success;
            $.jqcouch.doc.put(path, data);
        },
        del: function(path)
        {
            $.jqcouch.doc.on_error = $.jqcouch.view.on_error;
            $.jqcouch.doc.on_success = $.jqcouch.view.on_success;
            $.jqcouch.doc.del(path);
        },
        temp: function(path, query_args, map)
        {            
            if (path.substr(path.length-1,1) != '/')
            {
                path += '/';
            }            
            path += '_temp_view';            
            path += $.jqcouch.generate_query_str(query_args);
            
            var server_url = $.jqcouch.server_url;
            $.ajax({
                url: server_url + path,
                type: "POST",
                global: false,
                cache: false,
                async: false,
                dataType: "json",
                contentType: 'application/json',
                data: map.toSource(),
                contentType: 'application/javascript',
                processData: false,
                error: function(request){
                    $.jqcouch.view.on_error(request, {
                        method: 'jqcouch.doc.temp',
                        action: '_temp_view',
                        args: {query_args: query_args}
                    });
                    return false;
                },
                success: function(data){
                    $.jqcouch.view.on_success(data, {
                        method: 'jqcouch.doc.temp',
                        action: '_temp_view',
                        args: {query_args: query_args}
                    });
                    return true;
                }
            });
        },
        
        on_error: function(request, caller)
        {
            $.jqcouch.on_error(request, caller);            
        },
        on_success: function(data, caller)
        {
            $.jqcouch.on_success(data, caller);
        },
        _original_on_error: this.on_error,
        _original_on_success: this.on_success
    };

})(jQuery);