/*
 *  Copyright 2007 hkrn <hikarin@users.sourceforge.jp>
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 * Img0ch.App.Read
 * $Id: read.js 1496 2007-10-28 13:10:05Z hikarin $
 *
 * @require jquery.js
 * @require img0ch/app.js
 * @require img0ch/popup.js
 * @require img0ch/template/default.js
 * @require img0ch/thread.js
 * @require img0ch/util.js
 */

var Img0ch;
if ( typeof Img0ch == "undefined" ) Img0ch = {};
if ( typeof Img0ch.App == "undefined" ) Img0ch.App = {};

Img0ch.App.Read = function( app, option ) {
    var baseURL  = option.URL;
    var bbs      = option.bbs;
    var cushion  = option.cushion || "http://nun.nu/";
    var key      = option.key;
    var gz       = option.gzip;
    var opt      = option.option;
    var rootNode = option.rootNode || "threadRoot";
    var bbsURL   = baseURL + '/'    + bbs + '/';
    var getFrom  = bbsURL  + "dat/" + key + ".dat.utf8";
    var varName  = option.varName;

    if ( gz ) getFrom += ".gz";
    if ( option.reload == false ) {
        var self    = this;
        var isOpera = window.opera;
        var preventReload = function(event) {
            if ( !event ) event = window.event;
            var keyCode = event.keyCode;
            var ctrl    = event.ctrlKey;
            if ( !ctrl && keyCode == 116 ) {
                if (event.preventDefault)
                    event.preventDefault();
                return false;
            }
            else if ( !event.shiftKey && ctrl && keyCode == 82 ) {
                self.request();
                if (event.preventDefault)
                    event.preventDefault();
                return false;
            }
        }
        isOpera ? jQuery(document).keypress(preventReload)
                : jQuery(document).keydown(preventReload);
    }

    var baseURL_       = baseURL;
    var bbsURL_        = bbsURL;
    var bbs_           = bbs;
    var cgiuri_        = baseURL + "/test";
    var key_           = key;
    var length_        = 0;
    var isGzip_        = gz;
    var lastUpdate_    = new Date();
    var linkRegex_     = new RegExp(
        "(https?://[A-Za-z0-9~/._\\?&=\\-%#\\+:;,@']+)", "g" );
    var linkReplaceTo_ = "<a href='" + cushion + "$1'>$1</a>";
    var option_        = opt;
    var plugin_        = Img0ch.App.Read.Plugin;
    var popRegex_      = new RegExp(
        "(<a[^>]*>)?(&gt;|&gt;&gt;|＞|＞＞)([\\d\\-]+)(</a[^>]*>)?", "g" );
    var popReplaceTo_  = "<a href='javascript:void(0)' "
        + "onmouseover='" + ( varName || "app" )
        + ".popup( this, event, \"$3\" )'>&gt;&gt;$3</a>";
    var query_         = option.query;
    var requestURL_    = getFrom;
    var rootNode_      = rootNode;
    var static_        = option.staticURI;
    var template_      =
        Img0ch.Template[option.template] || Img0ch.Template.Default;
    var thread_        = new Img0ch.Thread(app);
    var varName_       = varName;

    return jQuery.extend( app, {
    "request": function() {
        var self = this;
        var oldSubjectValue = this.getSubject();
        thread_.request({
            "initialize": function(xml) {
                jQuery(self.getElementIsReloadLinks()).hide();
                jQuery(self.getElementIsLoading()).show();
                self.setSubject("読み込み中...");
            },
            "requestFirst": function() {
                var len = thread_.count();
                var pluginParam = query_["plugin"];
                var plugin = (typeof pluginParam == "string"
                    ? pluginParam.split(',') : pluginParam || []);
                var pluginEntries = [];
                for ( var i in plugin ) {
                    var p  = plugin[i];
                    var ns = "Img0ch.App.Read.Plugin."
                        + p.slice(0,1).toUpperCase()
                        + p.slice(1).toLowerCase();
                    var o = {};
                    o[ns] = static_
                        + "/js/img0ch/app/read/plugin/" + p + ".js";
                    pluginEntries.push(o);
                }
                self.require(
                    pluginEntries,
                    function() {
                        var option = self.getOption();
                        var range = self.parseReadRange( option, len );
                        self.render( range[0], range[1], range[2], true );
                        self.setElapsed();
                    }
                );
            },
            "requestModified": function( startFrom, len ) {
                self.triggerPlugins("reload");
                self.render( startFrom + 1,
                    startFrom + len, false, false );
            },
            "requestNotModified": function() {
                self.setSubject(oldSubjectValue);
            },
            "requestNotFound": function() {
                self.setSubject("読み込もうとしたスレッドは存在しませんでした。");
                self.setThreadSize("(N/A)");
                self.hideForm();
            },
            "requestNotSatisfied": function() {
                self.request();
            },
            "requestOtherError": function( httpStatus, xml ) {
                self.setSubject( httpStatus + ' ' + xml.statusText );
                self.setThreadSize("(N/A)");
                self.hideForm();
            },
            "requestComplete": function() {
                jQuery(self.getElementIsLoading()).hide();
                jQuery(self.getElementIsReloadLinks()).show();
            }
        });
        return;
    },
    "getBaseURL": function() { return baseURL_ },
    "getOption": function() { return option_ },
    "getQuery": function() { return query_ },
    "getRequestURL": function() { return requestURL_ },
    "getAll": function() { return thread_.getAll() },
    "getSubject": function() {
        return thread_.getSubject()
            || document.getElementById("threadSubject").innerHTML;
    },
    "getThreadInstance": function() { return thread_ },
    "getRootNodeElement": function() {
        return document.getElementById(rootNode_);
    },
    "getElementIsReloadLinks": function() {
        return document.getElementById("reloadLinks");
    },
    "getElementIsLoading": function() {
        return document.getElementById("loading");
    },
    "getElementIsReadFrom": function() {
        return document.getElementById("readFrom");
    },
    "getElementIsReadLatest": function() {
        return document.getElementById("readLatest");
    },
    "flush": function() {
        thread_.flush();
        return;
    },
    "setLength": function(length) {},
    "getLength": function() { return thread_.getSize() },
    "parseReadRange": function(range, len) {
        var from = 0, to = 0, first = true;
        if ( String(range).length == 0 ) {
            return [ 1, len, first ];
        }
        else if (/^l(\d+)(n)?/.test(range)) {
            first = RegExp.$2 ? false : true;
            return [ len - Number(RegExp.$1) + 1, len, first ];
        }
        else if (/^(\d*)-(\d*)(n)?$/.test(range)) {
            from  = this.intval(RegExp.$1) || 1;
            to    = this.intval(RegExp.$2) || len;
            first = RegExp.$3 ? false : true;
            if ( from > to ) {
                var temp = from;
                from = to;
                from = temp;
            }
        }
        else {
            /^(\d+)n?$/.test(range);
            from  = to = this.intval(RegExp.$1) || 1;
            first = false;
        }
        if ( to > len ) to = len;
        return [ from, to, first ];
    },
    "read": function(n) {
        var read = this.intval(n);
        var root = this.getRootNodeElement();
        if (read > 0) {
            var lastNum = Number(root.lastChild.id.match(/\d+$/));
            var from = lastNum;
            var to   = lastNum + read;
            this.render( from, to, true, true );
        }
        else {
            var firstNum = Number(root.firstChild.id.match(/\d+$/));
            if (firstNum == 1) {
                var item = root.childNodes.item(2);
                if (item) {
                   firstNum = Number(item.id.match(/\d+$/));
                }
            }
            var from = firstNum + read;
            var to   = firstNum;
            this.render( from, to, true, true );
        }
    },
    "readAll": function() {
        this.render( 2, thread_.count(), true, true );
    },
    "readFrom": function(readFrom, readSize) {
        this.render( readFrom, readFrom + ( readSize || 100 ), true, true );
    },
    "readRecent": function() {
        var count = thread_.count();
        this.render( count - 50, count, true, true );
    },
    "render": function(readStart, readTo, readFirst, isReload) {
        var count    = thread_.count();
        var root     = this.getRootNodeElement();
        var template = template_;

        if (isReload) jQuery(root).empty();
        if ( readFirst ) {
            var res = thread_.get(1);
            template.renderRes( this, root, 1, res );
        }

        if ( readStart < 2 ) readStart = 2;
        if ( readTo > count ) readTo = count;
        for ( var i = readStart; i <= readTo; i++ ) {
            var res = thread_.get(i);
            if ( !res || typeof res[1] == "undefined" ) {
                continue;
            }
            template.renderRes( this, root, i, res );
        }

        var rf   = this.getElementIsReadFrom();
        var loop = Math.floor( count / 100 ) + 1;
        var vn   = varName_ || "app";
        jQuery(rf).empty();
        for ( var i = 0; i < loop; i++ ) {
            var from  = ( i * 100 ) + 1;
            var to    = ( i + 1   ) * 100;
            var node  = from + '-' + to;
            var link  = document.createElement('a');
            link.href = "javascript:" + vn + ".readFrom(" + from + ",99)";
            link.appendChild(document.createTextNode(node));
            rf.appendChild(link);
            rf.appendChild(document.createTextNode(' '));
        }
        var rl = this.getElementIsReadLatest();
        rl.href = "javascript:" + vn + ".readFrom(" + count + ")";

        var subject = this.getSubject();
        this.setSubject(subject);
        this.setThreadSize();
        if ( count >= 1000 ) this.hideForm();
        document.title = "img0ch - read.html: "
            + this.escapeHTMLEntities(subject);
        this.triggerPlugins("rendered");

        return;
    },
    "rewrite": function( resno, comment ) {
        comment = String(comment).replace(
            popRegex_, popReplaceTo_ )
            .replace( linkRegex_, linkReplaceTo_);
        for ( var i in plugin_ ) {
            comment = plugin_[i].rewrite( resno, comment );
        }
        return comment;
    },
    "triggerPlugins": function(triggerName) {
        for ( var i in plugin_ ) {
            var callback = plugin_[i][triggerName];
            if (callback) callback();
        }
        return;
    },
    "setThreadSize": function(size) {
        document.getElementById("threadSize").innerHTML
            = String( size || Math.ceil( thread_.getSize() / 1024 ) ) + "KB";
    },
    "setSubject": function(subject) {
        document.getElementById("threadSubject").innerHTML
            = "■ " + this.escapeHTMLEntities(subject);
        return;
    },
    "hideForm": function() {
        jQuery("div#postform").hide().parent().append(jQuery("div#VERSION"));
        return;
    },
    "setElapsed": function() {
        var diffTime = new Date() - lastUpdate_;
        jQuery("span#renderTime").html(diffTime);
        return;
    },
    "getElementIsSearchWord": function() {
        return document.getElementById("quicksearch");
    },
    "getElementIsSearchType": function() {
        return document.getElementById("searchType");
    },
    "getElementIsSearchResult": function() {
        return document.getElementById("searchResult");
    },
    "getElementIsSearching": function() {
        return document.getElementById("searching");
    },
    "search": function() {
        var word   = this.getElementIsSearchWord().value;
        if ( word != '' ) {
            var words = this.escapeHTMLEntities(word).split(/\s+/);
            var root  = this.getRootNodeElement();
            var self  = this;
            var type  = this.getElementIsSearchType().value;
            thread_.search(
                type,
                words,
                {
                    "initialize": function() { jQuery(root).empty() },
                    "found": function( w, src ) {
                        var q  = w.replace( /(\W)/g, "\\$1" );
                        var re = new RegExp( q, 'g' );
                        var colored = "<b class='yellow'>" + w + "</b>";
                        return src.replace( re, colored );
                    },
                    "render": function( resno, result ) {
                        template_.renderRes( self, root, resno, result );
                    },
                    "complete": function(found) {
                        jQuery( self.getElementIsSearchResult() ).hide().html(
                            "「" + self.escapeHTMLEntities(word)
                            + "」の検索結果：" + String(found) + "レス発見しました"
                        ).show();
                    }
                }
            );
        }
        return;
    },
    "addSearchForm": function() {
        var timeout;
        var self       = this;
        var delay      = 1000;
        var searching  = jQuery(this.getElementIsSearching());
        var onKeyEvent = function () {
            clearTimeout(timeout);
            timeout = setTimeout( function () {
                searching.show();
                self.search();
                setTimeout(function () { searching.hide() }, delay / 2);
            }, delay / 2);
        }
        searching.hide();
        jQuery("form input#quicksearch").keyup(function(event) {
            switch(event.keyCode) {
                case 9:
                case 13:
                case 38:
                case 40:
                event.preventDefault();
                break;
                default:
                onKeyEvent();
            }
        });
        return;
    },
    "popup": function( element, event, readRange ) {
        var domTT_styleClass = "domTTClassic";
        var reses            = thread_.getAll();
        var len              = reses.length;
        var content          = "<dl class='popup'>";
        var range_array      = this.parseReadRange(readRange, len);
        var from             = range_array[0];
        var to               = range_array[1];
        var popup            = Img0ch.Popup;
        for ( var i = from; i <= to; i++ ) {
            content += popup.popupRes( this, reses, i );
        }
        content += "</dl>";
        domTT_activate(element, event, "content", content, "type", "velcro");
    },
    "getElementIsPostFormParameters": function() {
        return document.getElementById("postFormParameters");
    },
    "getElementIsWriteForm": function() {
        return document.getElementById("writeForm");
    },
    "initForm": function() {
        var self = this;
        var now  = (new Date()).getTime();
        var form = jQuery(this.getElementIsWriteForm());
        jQuery( "input[@name='bbs']", form ).val(bbs);
        jQuery( "input[@name='key']", form ).val(key);
        jQuery( "input[@name='time']", form ).val(now);
        //setInterval(function(){ app.request() }, 60000);
        jQuery( "input[@name='FROM']", form ).val(
            jQuery.cookie("img0ch_name"));
        jQuery( "input[@name='mail']", form ).val(
            jQuery.cookie("img0ch_mail"));
        jQuery("a.BackToBBSIndex").attr({ "href": self.getBBSURI() + "/" });
        jQuery("a.BackToReadCGI").attr({
            "href": cgiuri_ + "/read.cgi/" + bbs_
                + "/" + key_ + "/" + option_
        });
        jQuery("a.UploadedFileManager").attr({
            "href": cgiuri_ + "/img0ch-ufm.cgi/"
                + bbs + "/" + key + "/"
        });
        form.submit(function () {
            if ( !jQuery("textarea[@name='MESSAGE']",
                this).val() && jQuery("input[@type='submit']",
                this).val() == "書き込む" ) {
                if ( confirm(
                    "本文がありません。それでも投稿しますか？") == false ) {
                    return false;
                }
            }
            var option = { "expires": 30, "path": "/" };
            jQuery.cookie( "img0ch_name", jQuery(
                "input[@name='FROM']", this ).val(), option );
            jQuery.cookie( "img0ch_mail", jQuery(
                "input[@name='mail']", this ).val(), option );
            var submitButton = jQuery( "input[@type='submit']", this );
            submitButton.attr({ "disabled": true });
            var s = {
                "error": function( xml, type, e ) {
                    if (console) console.log([ type, e, xml ]);
                    alert(xml.status + " " + xml.statusText
                        + "\n[" + e.name + "]: " + e.message);
                    submitButton.attr({ "disabled": false });
                },
                "global": false,
                "type": "POST",
                "url": cgiuri_ + "/bbs.cgi"
            };
            if (jQuery( "input[@name='file']", this ).val()) {
                var xml = jQuery(document.createElement("input"));
                xml.attr({ "type": "hidden", "name": "xml" });
                xml.val(1);
                xml.appendTo(form);
                s.dataType   = "xml";
                s.uploadform = this;
                s.success    = function(res) {
                    if ( self.intval( jQuery( "ok", res ).text() ) ) {
                        jQuery( "input[@name='file']", form ).val('');
                        jQuery( "textarea[@name='MESSAGE']", form ).val('');
                        self.request();
                    }
                    else {
                        alert(
                            jQuery( "errstr", res ).text().replace(
                                "<br>", "\n" ).replace( "&lt;br&gt;", "\n" )
                        );
                    }
                    submitButton.attr({ "disabled": false });
                };
                jQuery.ajaxUpload(s);
                xml.remove();
            }
            else {
                var f  = this;
                var js = jQuery(document.createElement("input"));
                js.attr({ "type": "hidden", "name": "js" });
                js.val(1);
                js.appendTo(form);
                s.data     = jQuery( ":input", this ).serialize();
                s.dataType = "json";
                s.success  = function(res) {
                    if (res.ok) {
                        jQuery( "textarea[@name='MESSAGE']", form ).val('');
                        self.request();
                    }
                    else {
                        alert(
                            res.errstr.replace(
                                "<br>", "\n" ).replace( "&lt;br&gt;", "\n" )
                        );
                    }
                    submitButton.attr({ "disabled": false });
                };
                js.remove();
                jQuery.ajax(s);
            }
            return false;
        });
    },
    "syncWithGoogleGears": function(element) {
        app.syncUploadedFiles(function(){
            var message = "Google Gearsで添付ファイルの同期化(ローカルに保存)を行いました。\n"
                + "次回以降の画像及び添付ファイルの読み込みが高速化されます。";
            alert(message);
        });
        return;
    }
    });
};

Img0ch.App.Read.Plugin = {};
