/*!
 * ripple.js
 * Copyright (C) 2016-2018 Morgan Bazalgette and Giuseppe Guerra
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

// this object contains tiny snippets that were deemed too small to be worth
// their own file.
var singlePageSnippets = {
  "/2fa_gateway" : function() {
    $('#telegram-code')
      .on('input', function() {
        if ($(this).val().length >= 6) {
          $.get("/2fa_gateway/verify", {
            token : $(this).val().trim().substr(0, 8),
          },
          function(resp) {
            switch (resp) {
            case "0":
              $("#telegram-code").closest(".field").addClass("success");
              redir = redir ? redir : "/";
              window.location.href = redir;
              break;
            case "1":
              $("#telegram-code").closest(".field").addClass("error");
              break;
            }
          });
        } else {
          $("#telegram-code").closest(".field").removeClass("error");
        }
      });
  },

  "/leaderboard" : function() {
    page = page === 0 ? 1 : page;

    function loadLeaderboard() {
      var wl = window.location;
      window.history.replaceState(
        '', document.title,
        wl.pathname + "?mode=" + favouriteMode + "&p=" + page +
              (country != "" ? "&country=" + encodeURI(country) : "") +
              wl.hash);
      api("leaderboard", {
        mode : favouriteMode,
        p : page,
        l : 50,
        country : country,
      },
      function(data) {
        var tb = $(".ui.table tbody");
        tb.find("tr").remove();
        if (data.users == null) {
          disableSimplepagButtons(true);
          data.users = [];
        }
        var i = 0;
        data.users.forEach(function(v) {
          tb.append($("<tr />").append(
            $("<td />").text("#" + ((page - 1) * 50 + (++i))),
            $("<td />").html("<a href='/u/" + v.id +
                                   "' title='View profile'><i class='" +
                                   v.country.toLowerCase() + " flag'></i>" +
                                   escapeHTML(v.username) + "</a>"),
            $("<td />").html(
              scoreOrPP(v.chosen_mode.ranked_score, v.chosen_mode.pp)),
            $("<td />").text(v.chosen_mode.accuracy.toFixed(2) + "%"),
            // bonus points if you get the undertale joke
            $("<td />").html(addCommas(v.chosen_mode.playcount) +
                                   " <i title='" + T("Why, LOVE, of course!") +
                                   "'>(lv. " + v.chosen_mode.level.toFixed(0) +
                                   ")</i>")));
        });
        disableSimplepagButtons(data.users.length < 50);
      });
    }
    function scoreOrPP(s, pp) {
      if (pp === 0)
        return "<b>" + addCommas(s) + "</b>";
      return "<b>" + addCommas(pp) + "pp</b> (" + addCommas(s) + ")"
    }

    // country stuff
    $("#country-chooser-modal")
      .click(function() {
        $(".ui.modal").modal("show");
      });
    $(".lb-country")
      .click(function() {
        country = $(this).data("country");
        page = 1;
        $(".ui.modal").modal("hide");
        loadLeaderboard();
      });

    loadLeaderboard();
    setupSimplepag(loadLeaderboard);
    $("#mode-menu .item")
      .click(function(e) {
        e.preventDefault();
        $("#mode-menu .active.item").removeClass("active");
        $(this).addClass("active");
        favouriteMode = $(this).data("mode");
        country = "";
        page = 1;
        loadLeaderboard();
      });
  },

  "/friends" : function() {
    $(".smalltext.button")
      .click(function() {
        var t = $(this);
        var delAdd = t.data("deleted") === "1" ? "add" : "del";
        console.log(delAdd);
        t.addClass("disabled");
        api("friends/" + delAdd, {user : +t.data("userid")}, function(data) {
          t.removeClass("disabled");
          t.data("deleted", data.friend ? "0" : "1");
          t.removeClass("green red blue");
          t.addClass(data.friend ? (data.mutual ? "red" : "green") : "blue");
          t.find(".icon")
            .removeClass("minus plus heart")
            .addClass(data.friend ? (data.mutual ? "heart" : "minus")
              : "plus");
          t.find("span").text(data.friend
            ? (data.mutual ? T("Mutual") : T("Remove"))
            : t("Add"));
        }, true);
      });
  },

  "/team" : function() {
    $("#everyone").click(function() { $(".ui.modal").modal("show"); });
  },

  "/register/verify" : function() {
    var qu = query("u");
    setInterval(function() {
      $.getJSON(hanayoConf.banchoAPI + "/api/v1/verifiedStatus?u=" + qu,
        function(data) {
          if (data.result >= 0) {
            window.location.href = "/register/welcome?u=" + qu;
          }
        })
    }, 5000)
  },

  "/settings" : function() {
    $("input[name='custom_badge.icon']")
      .on("input", function() {
        $("#badge-icon")
          .attr("class", "circular big icon " + escapeHTML($(this).val()));
      });
    $("input[name='custom_badge.name']")
      .on("input", function() {
        $("#badge-name").html(escapeHTML($(this).val()));
      });
    $("input[name='custom_badge.show']")
      .change(function() {
        if ($(this).is(":checked"))
          $("#custom-badge-fields").slideDown();
        else
          $("#custom-badge-fields").slideUp();
      });
    var isDark = $("#dark-site").is(":checked");
    $("form")
      .submit(function(e) {
        e.preventDefault();

        var darkSetting = $("#dark-site").is(":checked")
        if (darkSetting != isDark) {
          var cflags = document.cookie.replace(/(?:(?:^|.*;\s*)cflags\s*\=\s*([^;]*).*$)|^.*$/, "$1");
          cflags = darkSetting ? +cflags | 1 : +cflags & ~1;
          document.cookie = "cflags=" + cflags + ";path=/;max-age=31536000";
        }

        var obj = formToObject($(this));
        var ps = 0;
        $(this)
          .find("input[data-sv]")
          .each(function(_, el) {
            el = $(el);
            if (el.is(":checked")) {
              ps |= el.data("sv");
            }
          });
        obj.play_style = ps;
        var f = $(this);
        api("users/self/settings", obj, function(data) {
          if (darkSetting != isDark) {
            window.location.reload();
            return;
          }
          showMessage("success", "Your new settings have been saved.");
          f.removeClass("loading");
        }, true);
        return false;
      });
  },

  "/settings/userpage" : function() {
    var lastTimeout = null;
    $("textarea[name='data']")
      .on('input', function() {
        if (lastTimeout !== null) {
          clearTimeout(lastTimeout);
        }
        var v = $(this).val();
        lastTimeout = setTimeout(function() {
          $("#userpage-content").addClass("loading");
          $.post(
            "/settings/userpage/parse",
            $("textarea[name='data']").val(), function(data) {
              var e =
                      $("#userpage-content").removeClass("loading").html(data);
              if (typeof twemoji !== "undefined") {
                twemoji.parse(e[0]);
              }
            }, "text");
        }, 800);
      });
    $("form")
      .submit(function(e) {
        e.preventDefault();
        var obj = formToObject($(this));
        var f = $(this);
        api("users/self/userpage", obj, function(data) {
          showMessage("success", "Your userpage has been saved.");
          f.removeClass("loading");
        }, true);
        return false;
      });
  },

  "/donate" : function() {
    var sl = $("#months-slider")[0];
    noUiSlider.create(sl, {
      start : [ 1 ],
      step : 1,
      connect : [ true, false ],
      range : {
        min : [ 1 ],
        max : [ 24 ],
      }
    });
    var rates = {};
    var us = sl.noUiSlider;
    $.getJSON("/donate/rates", function(data) {
      rates = data;
      us.on('update', function() {
        var months = us.get();
        var priceEUR = Math.pow(months * 30 * 0.2, 0.70);
        var priceBTC = priceEUR / rates.EUR;
        var priceUSD = priceBTC * rates.USD;
        $("#cost")
          .html(T("<b>{{ months }}</b> month costs <b>€ {{ eur }}</b>", {
            count : Math.round(+months),
            months : (+months).toFixed(0),
            eur : priceEUR.toFixed(2),
          }) +
                  "<br>" + T("($ {{ usd }} / BTC {{ btc }})", {
              usd : priceUSD.toFixed(2),
              btc : priceBTC.toFixed(10),
            }));
        $("input[name='os0']")
          .attr("value",
            (+months).toFixed(0) + " month" + (months == 1 ? "" : "s"));
        $("#bitcoin-amt").text(priceBTC.toFixed(6));
        $("#paypal-amt").val(priceEUR.toFixed(2));
      });
    });
    $("#username-input").on("input", function() {
      $("#ipn-username").attr("value", "username=" + $(this).val());
    });
  },

  "/settings/avatar" : function() {
    $("#file")
      .change(function(e) {
        var f = e.target.files;
        if (f.length < 1) {
          return;
        }
        var u = window.URL.createObjectURL(f[0]);
        var i = $("#avatar-img")[0];
        i.src = u;
        i.onload = function() { window.URL.revokeObjectURL(this.src); };
      });
  },

  "/beatmaps/rank_request" : function() {
    function updateRankRequestPage(data) {
      $("#queue-info").html(data.submitted + "/" + data.queue_size);

      if (data.submitted_by_user == 0)
        $("#by-you").attr("hidden", "hidden");
      else
        $("#by-you").removeAttr("hidden");

      $("#submitted-by-user").text(data.submitted_by_user);
      $("#max-per-user").text(data.max_per_user);

      var perc = (data.submitted / data.queue_size * 100).toFixed(0);
      $("#progressbar .progress").text(perc + "%");
      $("#progressbar")
        .progress({
          percent : perc,
        });
      if (data.can_submit)
        $("#b-form .input, #b-form .button").removeClass("disabled");
      else
        $("#b-form .input, #b-form .button").addClass("disabled");
    }
    setInterval(function() {
      api("beatmaps/rank_requests/status", {}, updateRankRequestPage);
    }, 10000);
    var re = /^https?:\/\/osu.ppy.sh\/(s|b)\/(\d+)$/gi;
    $("#b-form")
      .submit(function(e) {
        e.preventDefault();
        var v = $("#beatmap").val().trim();
        var reData = re.exec(v);
        re.exec(); // apparently this is always null, idk
        console.log(v, reData);
        if (reData === null) {
          showMessage(
            "error",
            "Please provide a valid link, in the form " +
                    "of either https://osu.ppy.sh/s/&lt;ID&gt; or https://osu.ppy.sh/b/&lt;ID&gt;.");
          $(this).removeClass("loading");
          return false;
        }
        var postData = {};
        if (reData[1] == "s")
          postData.set_id = +reData[2];
        else
          postData.id = +reData[2];
        var t = $(this);
        api("beatmaps/rank_requests", postData,
          function(data) {
            t.removeClass("loading");
            showMessage("success",
              "Beatmap rank request has been submitted.");
            updateRankRequestPage(data);
          },
          function(data) {
            t.removeClass("loading");
            if (data.code == 406)
              showMessage("warning", "That beatmap is already ranked!");
          },
          true);
        return false;
      });
  },

  "/settings/profbackground" : function() {
    $("#colorpicker")
      .minicolors({
        inline : true,
      });
    $("#background-type")
      .change(function() {
        $("[data-type]:not([hidden])").attr("hidden", "hidden");
        $("[data-type=" + $(this).val() + "]").removeAttr("hidden");
      });
    $("#file")
      .change(function(e) {
        var f = e.target.files;
        if (f.length < 1) {
          return;
        }
        var u = window.URL.createObjectURL(f[0]);
        var i = document.createElement("img");
        i.src = u;
        i.onload = function() { window.URL.revokeObjectURL(this.src); };
        $("#image-background").empty().append(i);
      });
  },

  "/dev/tokens" : function() {
    $("#privileges-number")
      .on("input", function() {
        $("#privileges-text").text(privilegesToString($(this).val()));
      });
  }
};

$(document)
  .ready(function() {
    // semantic stuff
    $('.message .close').on('click', closeClosestMessage);
    $('.ui.checkbox').checkbox();
    $('.ui.dropdown').dropdown();
    $('.ui.progress').progress();
    $('.ui.form')
      .submit(function(e) {
        var t = $(this);
        if (t.hasClass("loading") || t.hasClass("disabled")) {
          e.preventDefault();
          return false;
        }
        t.addClass("loading");
        var f = t.attr("id");
        $("[form='" + f + "']").addClass("loading");
      });

    // emojis!
    if (typeof twemoji !== "undefined") {
      $(".twemoji").each(function(k, v) { twemoji.parse(v); });
    }

    // ripple stuff
    var f = singlePageSnippets[window.location.pathname];
    if (typeof f === 'function')
      f();
    if (typeof deferredToPageLoad === "function")
      deferredToPageLoad();

    // setup user search
    $("#user-search")
      .search({
        onSelect : function(val) {
          window.location.href = val.url;
          return false;
        },
        apiSettings : {
          url : "/api/v1/users/lookup?name={query}",
          onResponse : function(resp) {
            var r = {
              results : [],
            };
            $.each(resp.users, function(index, item) {
              r.results.push({
                title : item.username,
                url : "/u/" + item.id,
                image : hanayoConf.avatars + "/" + item.id,
              });
            });
            return r;
          },
        },
      });
    $("#user-search-input")
      .keypress(function(e) {
        if (e.which == 13) {
          window.location.pathname = "/u/" + $(this).val();
        }
      });

    $(document)
      .keydown(function(e) {
        var activeElement = $(document.activeElement);
        var isInput = activeElement.is(":input,[contenteditable]");
        if ((e.which === 83 || e.which === 115) && !isInput) {
          $("#user-search-input").focus();
          e.preventDefault();
        }
        if (e.which === 27 && isInput) {
          activeElement.blur();
        }
      });

    // setup timeago
    $.timeago.settings.allowFuture = true;
    $("time.timeago").timeago();

    $("#language-selector .item")
      .click(function() {
        var lang = $(this).data("lang");
        document.cookie = "language=" + lang + ";path=/;max-age=31536000";
        window.location.reload();
      });
  });

function closeClosestMessage() {
  $(this).closest('.message').fadeOut(300, function() { $(this).remove(); });
};

function showMessage(type, message) {
  var newEl =
      $('<div class="ui ' + type +
        ' message hidden"><i class="close icon"></i>' + T(message) + '</div>');
  newEl.find(".close.icon").click(closeClosestMessage);
  $("#messages-container").append(newEl);
  newEl.slideDown(300);
};

// function for all api calls
function api(endpoint, data, success, failure, post) {
  if (typeof data == "function") {
    success = data;
    data = null;
  }
  if (typeof failure == "boolean") {
    post = failure;
    failure = undefined;
  }

  var errorMessage =
      "An error occurred while contacting the Ripple API. Please report this to a Ripple developer.";

  $.ajax({
    method : (post ? "POST" : "GET"),
    dataType : "json",
    url : hanayoConf.baseAPI + "/api/v1/" + endpoint,
    data : (post ? JSON.stringify(data) : data),
    contentType : (post ? "application/json; charset=utf-8" : ""),
    success : function(data) {
      if (data.code != 200) {
        if ((data.code >= 400 && data.code < 500) &&
            typeof failure == "function") {
          failure(data);
          return;
        }
        console.warn(data);
        showMessage("error", errorMessage);
      }
      success(data);
    },
    error : function(jqXHR, textStatus, errorThrown) {
      if ((jqXHR.status >= 400 && jqXHR.status < 500) &&
          typeof failure == "function") {
        failure(jqXHR.responseJSON);
        return;
      }
      console.warn(jqXHR, textStatus, errorThrown);
      showMessage("error", errorMessage);
    },
  });
};

var modes = {
  0 : "osu! standard",
  1 : "Taiko",
  2 : "Catch the Beat",
  3 : "osu!mania",
};
var modesShort = {
  0 : "std",
  1 : "taiko",
  2 : "ctb",
  3 : "mania",
};

var entityMap = {
  "&" : "&amp;",
  "<" : "&lt;",
  ">" : "&gt;",
  '"' : '&quot;',
  "'" : '&#39;',
  "/" : '&#x2F;',
};
function escapeHTML(str) {
  return String(str).replace(/[&<>"'\/]/g,
    function(s) { return entityMap[s]; });
}

function setupSimplepag(callback) {
  var el = $(".simplepag");
  el.find(".left.floated .item").click(function() {
    if ($(this).hasClass("disabled"))
      return false;
    page--;
    callback();
  });
  el.find(".right.floated .item").click(function() {
    if ($(this).hasClass("disabled"))
      return false;
    page++;
    callback();
  });
}
function disableSimplepagButtons(right) {
  var el = $(".simplepag");

  if (page <= 1)
    el.find(".left.floated .item").addClass("disabled");
  else
    el.find(".left.floated .item").removeClass("disabled");

  if (right)
    el.find(".right.floated .item").addClass("disabled");
  else
    el.find(".right.floated .item").removeClass("disabled");
}

window.URL = window.URL || window.webkitURL;

// thank mr stackoverflow
function addCommas(nStr) {
  nStr += '';
  x = nStr.split('.');
  x1 = x[0];
  x2 = x.length > 1 ? '.' + x[1] : '';
  var rgx = /(\d+)(\d{3})/;
  while (rgx.test(x1)) {
    x1 = x1.replace(rgx, '$1' +
                             ',' +
                             '$2');
  }
  return x1 + x2;
}

// helper functions copied from user.js in old-frontend
function getScoreMods(m, noplus) {
	var r = [];
  // has nc => remove dt
  if ((m & 512) == 512)
    m = m & ~64;
  // has pf => remove sd
  if ((m & 16384) == 16384)
    m = m & ~32;
  modsString.forEach(function(v, idx) {
    var val = 1 << idx;
    if ((m & val) > 0)
      r.push(v);
  });
	if (r.length > 0) {
		return (noplus ? "" : "+ ") + r.join(", ");
	} else {
		return (noplus ? T('None') : '');
	}
}

var modsString = [
  "NF",
	"EZ",
	"NV",
	"HD",
	"HR",
	"SD",
	"DT",
	"RX",
	"HT",
	"NC",
	"FL",
	"AU", // Auto.
	"SO",
	"AP", // Autopilot.
	"PF",
	"K4",
	"K5",
	"K6",
	"K7",
	"K8",
	"K9",
	"RN", // Random
	"LM", // LastMod. Cinema?
	"K9",
	"K0",
	"K1",
	"K3",
	"K2",
];

// time format (seconds -> hh:mm:ss notation)
function timeFormat(t) {
  var h = Math.floor(t / 3600);
  t %= 3600;
  var m = Math.floor(t / 60);
  var s = t % 60;
  var c = "";
  if (h > 0) {
    c += h + ":";
    if (m < 10) {
      c += "0";
    }
    c += m + ":";
  } else {
    c += m + ":";
  }
  if (s < 10) {
    c += "0";
  }
  c += s;
  return c;
}

// http://stackoverflow.com/a/901144/5328069
function query(name, url) {
  if (!url) {
    url = window.location.href;
  }
  name = name.replace(/[\[\]]/g, "\\$&");
  var regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"),
    results = regex.exec(url);
  if (!results)
    return null;
  if (!results[2])
    return '';
  return decodeURIComponent(results[2].replace(/\+/g, " "));
}

// Useful for forms contacting the Ripple API
function formToObject(form) {
  var inputs = form.find("input, textarea, select");
  var obj = {};
  inputs.each(function(_, el) {
    el = $(el);
    if (el.attr("name") === undefined) {
      return;
    }
    var parts = el.attr("name").split(".");
    var value;
    switch (el.attr("type")) {
    case "checkbox":
      value = el.is(":checked");
      break;
    default:
      switch (el.data("cast")) {
      case "int":
        value = +el.val();
        break;
      default:
        value = el.val();
        break;
      }
      break;
    }
    obj = modifyObjectDynamically(obj, parts, value);
  });
  return obj;
}

// > modifyObjectDynamically({}, ["nice", "meme", "dude"], "lol")
// { nice: { meme: { dude: 'lol' } } }
function modifyObjectDynamically(obj, inds, set) {
  if (inds.length === 1) {
    obj[inds[0]] = set;
  } else if (inds.length > 1) {
    if (typeof obj[inds[0]] !== "object")
      obj[inds[0]] = {};
    obj[inds[0]] = modifyObjectDynamically(obj[inds[0]], inds.slice(1), set);
  }
  return obj;
}

var langWhitelist = [
  "de", "it", "ko", "es", "ru", "pl", "fr", "nl", "sv", "fi", "ro", "ko", "vi"
];
i18next.use(i18nextXHRBackend).init({
  nsSeparator : false,
  keySeparator : false,
  fallbackLng : false,
  lng : hanayoConf.language,
  whitelist : langWhitelist,
  load : "currentOnly",
  backend : {loadPath : "/static/locale/{{lng}}.json"}
});

var i18nLoaded = $.inArray(hanayoConf.language, langWhitelist) === -1;
i18next.on("loaded", function() { i18nLoaded = true });

function T(s, settings) {
  if (typeof settings !== "undefined" &&
      typeof settings.count !== "undefined" &&
      $.inArray(hanayoConf.language, langWhitelist) === -1 &&
      settings.count !== 1)
    s = keyPlurals[s];
  return i18next.t(s, settings);
}

var apiPrivileges = [
  "ReadConfidential", "Write", "ManageBadges", "BetaKeys", "ManageSettings",
  "ViewUserAdvanced", "ManageUser", "ManageRoles", "ManageAPIKeys", "Blog",
  "APIMeta", "Beatmap"
];

function privilegesToString(privs) {
  var privList = [];
  apiPrivileges.forEach(function(value, index) {
    if ((privs & (1 << (index + 1))) != 0)
      privList.push(value);
  });
  return privList.join(", ");
}
