/*
 *  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 657 2007-05-19 12:55:55Z hikarin $
 *
 * @require jquery.js
 * @require domLib.js
 * @require domTT.js
 * @require img0ch/template/*.js
 * @require img0ch/util.js
 */

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

Img0ch.App.Read = function(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 res_           = [ [ "", "", "", "", "" ] ];
    var rootNode_      = rootNode;
    var static_        = option.staticURI;
    var template_      =
        Img0ch.Template[option.template] || Img0ch.Template.Default;
    var varName_       = varName;

    return {
    "request": function() {
        var app = this;
        var oldSubjectValue = app.getSubject();
        jQuery.ajax({
            "url": requestURL_,
            "ifModified": true,
            "global": false,
            "beforeSend": function(xml) {
                var len = app.getLength();
                if ( !app.isGzip && len > 0 ) {
                    var from = "bytes=" + String(len) + "-";
                    xml.setRequestHeader( "Range", from );
                }
                jQuery("div#reloadLinks").hide();
                jQuery("#loading").show();
                app.setSubject("読み込み中...");
            },
            "complete": function(xml) {
                var httpStatus = xml.status;
                try {
                    var len = xml.getResponseHeader("Content-Length");
                    isNaN(len)
                        ? app.setLength(xml.responseText.length)
                        : app.setLength(len);
                    } catch(e) {
                    }
                if ( httpStatus == 200 ) {
                    app.flush();
                    var dat = xml.responseText;
                    var res = dat.split("\n");
                    var len = res.length - 1;
                    res.length = len;
                    for ( var i in res ) {
                        if (isNaN(i)) continue;
                        app.set(res[i].split("<>"));
                    }
                    var pluginParam = query_["plugin"];
                    var plugin = (typeof pluginParam == "string"
                        ? pluginParam.split(',') : pluginParam || []);
                    for ( var i in plugin ) {
                        var p = plugin[i];
                        app.require("img0ch/app/read/plugin/" + p + ".js");
                    }
                    var option = app.getOption();
                    var range = Img0ch.Util.parseReadRange( option, len );
                    app.render( range[0], range[1], range[2], true );
                    app.setElapsed();
                }
                else if ( httpStatus == 206 ) {
                    var dat = xml.responseText;
                    var res = dat.split("\n");
                    var startFrom = app.getCount();
                    var len = res.length - 1;
                    res.length = len;
                    for ( var i in res ) {
                        if (isNaN(i)) continue;
                        app.set(res[i].split("<>"));
                    }
                    app.render( startFrom + 1,
                        startFrom + len, false, false );
                }
                else if ( httpStatus == 304 ) {
                    app.setSubject(oldSubjectValue);
                }
                else if ( httpStatus == 404 ) {
                    app.setSubject("読み込もうとしたスレッドは存在しませんでした。");
                    app.setThreadSize("(N/A)");
                    app.hideForm();
                }
                else if ( httpStatus == 416 ) {
                    app.request();
                    return;
                }
                else {
                    app.setSubject( httpStatus + ' ' + xml.statusText );
                    app.setThreadSize("(N/A)");
                    app.hideForm();
                }
                jQuery("#loading").hide();
                jQuery("div#reloadLinks").show();
            },
            "error": function( xml, error ) {
                app.setSubject(error);
                app.hideForm();
                jQuery("#loading").hide();
                jQuery("div#reloadLinks").show();
            }
            });
        return;
    },
    "getBaseURL": function() {
        return baseURL_;
    },
    "getBBSURL": function() {
        return bbsURL_;
    },
    "getBBS": function() {
        return bbs_;
    },
    "getCGIURI": function() {
        return cgiuri_;
    },
    "getKey": function() {
        return key_;
    },
    "getOption": function() {
        return option_;
    },
    "getQuery": function() {
        return query_;
    },
    "getRequestURL": function() {
        return requestURL_;
    },
    "getStaticURI": function() {
        return static_;
    },
    "getThread": function() {
        return reses_;
    },
    "set": function(res) {
        res_.push(res);
        return;
    },
    "get": function(i) {
        if ( i < 1 || res_.length > i ) {
            return [];
        }
        return res_[i];
    },
    "getAll": function() {
        return res_;
    },
    "getCount": function() {
        return res_.length;
    },
    "getSubject": function() {
        return res_[0][4]
            || document.getElementById("threadSubject").innerHTML;
    },
    "flush": function() {
        res_.length = 0;
        return;
    },
    "setLength": function(length) {
        length_ += Number(length);
        return;
    },
    "getLength": function() {
        return length_;
    },
    "read": function(n) {
        var read = Img0ch.Util.intval(n);
        var root = document.getElementById(rootNode_);
        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, this.getCount(), true, true );
    },
    "readFrom": function(readFrom, readSize) {
        this.render( readFrom, readFrom + (readSize || 100), true, true );
    },
    "readRecent": function() {
        var count = this.getCount() + 1;
        this.render( count - 50, count, true, true );
    },
    "render": function(readStart, readTo, readFirst, isReload) {
        var reses    = res_;
        var count    = res_.length;
        var count2   = count + 1;
        var root     = document.getElementById(rootNode_);
        var template = template_;

        if (isReload) jQuery(root).empty();
        if ( readFirst ) {
            var res = reses[0];
            template.renderRes( this, root, 1, res );
        }

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

        var rf   = document.getElementById("readFrom");
        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 = document.getElementById("readLatest");
        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: " + subject;

        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;
    },
    "setThreadSize": function(size) {
        document.getElementById("threadSize").innerHTML
            = String( size || Math.ceil(this.getLength() / 1024) ) + "KB";
    },
    "setSubject": function(subj) {
        document.getElementById(
            "threadSubject").innerHTML = "■ " + String(subj);
        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;
    },
    "search": function() {
        var word   = jQuery("form input#quicksearch").val();
        var type   = Img0ch.Util.intval(jQuery("select#searchType").val());
        if ( type < 0 || type > 3 ) type = 3;
        if ( word != '' ) {
            var found    = 0;
            var reses    = res_;
            var words    = Img0ch.Util.escapeHTMLEntities(word).split(/\s+/);
            var wlen     = words.length;
            var template = template_;
            var root     = document.getElementById(rootNode_);
            jQuery(root).empty();
            for ( var i in reses ) {
                var res     = reses[i];
                if ( typeof res[3] == "undefined" ) continue;
                var hit    = 0;
                var resno  = Number(i) + 1;
                var result = [ res[0], res[1], res[2], res[3] ];
                var src    = res[type];
                for ( var j in words ) {
                    var w = words[j];
                    if ( src.indexOf(w) >= 0 ) {
                        var regex   = new RegExp(w, 'g');
                        var colored = "<b class='yellow'>" + w + "</b>";
                        src = src.replace(regex, colored);
                        hit++;
                   }
                }
                if ( hit === wlen ) {
                    found++;
                    result[type] = src;
                    template.renderRes( this, root, resno, result );
                }
            }
            jQuery("span#searchResult").hide().html(
                 "「" + Img0ch.Util.escapeHTMLEntities(word)
                 + "」の検索結果：" + String(found) + "レス発見しました"
            ).show();
        }
        return;
    },
    "addSearchForm": function() {
        var timeout;
        var app        = this;
        var delay      = 1000;
        var searching  = jQuery("span#searching");
        var onKeyEvent = function () {
            clearTimeout(timeout);
            timeout = setTimeout( function () {
                searching.show();
                app.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;
    },
    "require": function(path) {
        if (!path || !/^[\w\\/]+\w+\.js$/.test(path)) return;
        Img0ch.Util.require(static_ + "/js/" + path);
    },
    "popup": function( element, event, readRange ) {
        var domTT_styleClass = "domTTClassic";
        var reses            = this.getAll();
        var len              = reses.length;
        var content          = "<dl class='popup'>";
        var range_array      = Img0ch.Util.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");
    }
    };
};

Img0ch.App.Read.Plugin = {};
