/**
 * @requires jQuery
 * @requires nicoru
 * @author kouta_saito
 */
(function($){

  var NICO_URL_REGEXP = /^http:\/\/[^\/]*\.(nicovideo\.jp|niconico\.com)\//;
  var LIVE_URL_REGEXP = /^http:\/\/live\./;
  var MAX_RES_LENGTH  = 140;
  var WARN_RES_LENGTH = 130;

  $.fn.initNicorepoPage = function () {
    $(this).each(initNicorepoPage);
  };

  $(document).ready(function() {
    // IE8以下にtime要素の存在を認識させる
    document.createElement("time");

    // ニコるボタン初期化
    (typeof nicoru !== "undefined") && $("#nicoru-token").each(function () { nicoru.init($(this).attr("data-nicoru-token"), $(this).attr("data-nicoru-language")); });

    // ページ初期化
    $(".nicorepo").each(initNicorepoPage);
  });

  function initNicorepoPage() {
    var $page = $(this);

    // タイムラインのログに各種 bind
    $page.find(".log").not(".log .log").each(initLog);

    // 次のページ
    $page.find(".next-page-link").click(onClickNextPage);

    // ニコるボタン読み込み
    (typeof nicoru !== "undefined") && nicoru.refresh();

    // 画像遅延読み込み
    $page.each(laziness);

    // 日時の相対表示
    $page.each(prettyDate);
  }

  function initLog() {
    var $log = $(this);

    // マウスオーバー時
    $log.hover(
      function () { $log.addClass("active"); },
      function () { $log.removeClass("active"); });

    // ニコレポ削除フォーム
    $log.find(".log-deleteform").submit(function () {
      submitForm(this);
      return false;
    });

    // コメント折り畳み
    $log.find(".log-comment-expand-link").click(function () {
      var $clog = $(this).closest(".log");
      $clog.find(".log-comment-summary").hide();
      $clog.find(".log-comment-full").show();
      return false;
    });

    // ニコる折り畳み
    $log.find(".log-nicoru-expand-link").click(function () {
      var $cnicoru = $(this).closest(".log-nicoru");
      $cnicoru.find(".log-nicoru-msg-summary").hide();
      $cnicoru.find(".log-nicoru-msg-full").show();
      return false;
    });

    // コメントするリンク
    $log.find(".log-res-open-link").click(function () {
      var $clog = $(this).closest(".log");
      $clog.find(".log-reslist").show();
      $clog.find(".log-res-input").focus();
      return false;
    });

    // レス一覧を初期化
    $log.find(".log-reslist").each(initResList);

    // トラッキング
    $log.each(tracking);
  }

  function initResList() {
    var $reslist = $(this);

    // レス削除フォーム
    $reslist.find(".log-res-deleteform").submit(function () {
      submitForm(this);
      return false;
    });

    // レス書き込みフォーム
    $reslist.find(".log-res-writeform").submit(function () {
      var $form = $(this), $clog = $form.closest(".log");

      var message = $form.find("input[name='message']").val();
      if (message === "") {
        return false;
      }
      if (message.length > MAX_RES_LENGTH) {
        showError("too_long_res");
        return false;
      }
      submitForm(this, function (response) {
        $form.each(function () { this.reset(); });
        setTimeout(function () {
          var reslistUrl = $form.attr("data-reslist-url");
          if (reslistUrl) {
            reloadResList(reslistUrl, $form.closest(".log-reslist"));
          } else {
            location.reload(true);
          }
        }, 2000);
      });
      return false;
    });

    // レス書き込み欄
    $reslist.find(".log-res-input").
      keyup(function () {
        var $input = $(this), len = $input.val().length, $field = $input.closest(".log-res-inner");
        if (len > MAX_RES_LENGTH) {
          $field.addClass("error").removeClass("warn");
        } else if (len > WARN_RES_LENGTH) {
          $field.addClass("warn").removeClass("error");
        } else {
          $field.removeClass("warn error");
        }
        $field.find(".log-res-counter").text(MAX_RES_LENGTH - len);
      }).
      change(function () {
        $(this).trigger("keyup");
      }).
      focus(function () {
        $(this).closest("form").addClass("focused");
      });

    // 最大長設定
    $reslist.find(".log-res-counter").text(MAX_RES_LENGTH);
  }

  function tracking() {
    var $this = $(this), $nicorepo = $this.closest(".nicorepo");
    var trackingName = $nicorepo.attr("data-tracking-name");
    var liveRefName = $nicorepo.attr("data-live-ref-name");
    if (trackingName) {
      trackingName = encodeURIComponent(trackingName);
      if (liveRefName) {
        liveRefName = encodeURIComponent(liveRefName);
      }
      $(this).find("a").each(function () {
        if (this.href && !/#$/.test(this.href) && NICO_URL_REGEXP.test(this.href)) {
          var tmp = this.innerHTML;
          if (liveRefName && LIVE_URL_REGEXP.test(this.href)) {
            this.href += (this.href.indexOf("?") >= 0 ? "&" : "?") + "ref=" + liveRefName;
          } else {
            this.href += (this.href.indexOf("?") >= 0 ? "&" : "?") + trackingName;
          }
          this.innerHTML = tmp; /* fix for IE */
        }
      });
    }
  }

  function laziness() {
    NicorepoLazyImage.add($(this).find("img.nicorepo-lazyimage").removeClass("nicorepo-lazyimage"));
  }

  function prettyDate() {
    $.fn.prettyDate && $(this).find("time.relative").prettyDate({
        value: function () { return $(this).attr("datetime") },
      inDays: false
    });
  }

  function submitForm(form, onSuccess, onError) {
    var $form = $(form), conf;
    if (conf = $form.attr("data-confirm")) {
      if (!confirm(conf)) {
        return null;
      }
    }

    $form.addClass("loading");
    var ajax = $.ajax({
      type:     $form.attr("method"),
      url:      $form.attr("action"),
      data:     $form.serialize(),
      dataType: "json",
      success:  function (response) {
        $form.removeClass("loading");
        if (response.status == "ok") {
          $form.addClass("completed");
          onSuccess && onSuccess.call($form, response);
        } else {
          var code = response.code || "unknown";
          onError && onError.call($form, code);
          showError(code);
        }
      },
      error:    function () {
        $form.removeClass("loading");
        onError && onError.call($form, "ajax-failed");
        showError("ajax-failed");
      }
    });
    return ajax;
  }

  function reloadResList(url, reslist) {
    if (url) {
      var $tmp = $('<div/>');
      $tmp.load(url, function (response, status, xhr) {
        if (status == "success") {
          var $newResList = $tmp.children();
          $newResList.replaceAll(reslist);
          $newResList.each(initResList).each(tracking).each(laziness).each(prettyDate);
        } else {
          showError("ajax-failed");
        }
      });
    }
  }

  function onClickNextPage() {
    var $link = $(this),
        $container = $link.closest(".next-page"),
        $currentPage = $container.closest(".nicorepo-page");

    $container.addClass("loading");
    $.ajax({
      type:     "GET",
      url:      $link.attr("href"),
      dataType: "html",
      success:  function (response) {
        var $newPage = $('<div/>').html(response).find(".nicorepo-page");
        $newPage.addClass("nicorepo-next-page");
        $currentPage.parent().append($newPage);
        $newPage.each(initNicorepoPage);
        $container.remove();
      },
      error:    function () {
        $container.removeClass("loading");
        showError("ajax-failed");
      }
    });
    return false;
  }

  function showError(code) {
    code = String(code).replace(/_/g, "-");
    var messages = $(".nicorepo .nicorepo_error_messages");
    var msg = messages.attr("data-" + code) || messages.attr("data-unkown");
    if (!msg) { throw new Error("Failed to get nicorepo_error_messages"); }
    alert(msg);
  }

  var NicorepoLazyImage = (function () {
    var exports = {};

    var waitImages = [];
    var PRELOAD_HEIGHT = 200;

    exports.add = function ($imgs) {
      var edge = pageBottom() + PRELOAD_HEIGHT;
      $imgs.each(function () {
        var $img = $(this), src = $img.attr("data-src"), top = $img.offset().top;
        if ($img.hasClass("adjust")) {
          adjustSizeAndLoad($img, src);
        } else if (top <= edge) {
          $img.attr("src", src);
        } else {
          waitImages.push({ img: this, src: src, top: top });
        }
      });
      waitImages.sort(function (a, b) { return a.top - b.top; });
    };

    var updateImagesCounter = 0;
    setInterval(function () {
      if (waitImages.length == 0) return;
      if (++updateImagesCounter >= 4) {
        updateImagesCounter = 0;
        updateImages();
      }
      var edge = pageBottom() + PRELOAD_HEIGHT;
      var i = 0, len = waitImages.length;
      while (i < len) {
        if (waitImages[i].top >= edge) { break; }
        waitImages[i].img.src = waitImages[i].src;
        i++;
      }
      if (i > 0) {
        waitImages.splice(0, i);
      }
    }, 500);

    function updateImages() {
      var body = document.body, anyDeleted = false, i, l, changed = false, top;
      for (i = 0, l = waitImages.length; i < l; i++) {
        img = waitImages[i].img;
        if ($.contains(body, img)) {
          top = $(img).offset().top;
          if (waitImages[i].top != top) {
            waitImages[i].top = top;
            changed = true;
          }
        } else {
          anyDeleted = true;
          waitImages[i].img = null;
        }
      }
      if (anyDeleted) {
        var validImages = [];
        for (i = 0, l = waitImages.length; i < l; i++) {
          if (waitImages[i].img) {
            validImages.push(waitImages[i]);
          }
        }
        waitImages = validImages;
      }
      if (changed) {
        waitImages.sort(function (a, b) { return a.top - b.top; });
      }
    }

    function pageBottom() {
      var y = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop;
      var h = window.innerHeight || document.documentElement.clientHeight || document.body.offsetHeight;
      return y + h;
    }

    function adjustSizeAndLoad($img, src) {
      var tmpImage = new Image();
      tmpImage.onload = function () {
        var frameWidth = $img.width(), frameHeight = $img.height(),
            origWidth = tmpImage.width, origHeight = tmpImage.height,
            newWidth, newHeight, marginWidth, marginHeight;
        if (origWidth >= origHeight) {
          newWidth = frameWidth;
          newHeight = origHeight * (frameHeight / origWidth);
        } else {
          newWidth = origWidth * (frameWidth / origHeight);
          newHeight = frameHeight;
        }
        $img.css({
          "width":  newWidth + "px",
          "height": newHeight + "px"
        });
        $img.attr("src", src);
        try { tmpImage.onload = null; tmpImage = undefined; } catch (e) {}
      };
      tmpImage.src = src;
    }

    return exports;
  })();

})(jQuery);
