/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements.  See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership.  The ASF licenses this file
* to you 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.
*/

package org.apache.hadoop.yarn.webapp.view;

import com.google.common.collect.Lists;
import com.google.inject.Inject;

import java.util.List;
import java.util.Locale;
import javax.servlet.http.Cookie;

import static org.apache.commons.lang.StringEscapeUtils.*;
import static org.apache.hadoop.yarn.util.StringHelper.*;

import org.apache.hadoop.yarn.webapp.hamlet.HamletSpec.HTML;

public class JQueryUI extends HtmlBlock {
  // Render choices (mostly for dataTables)
  public enum Render {
    /** small (<~100 rows) table as html, most gracefully degradable */
    HTML,
    /** medium (<~2000 rows) table as js array */
    JS_ARRAY,
    /** large (<~10000 rows) table loading from server */
    JS_LOAD,
    /** huge (>~10000 rows) table processing from server */
    JS_SERVER
  };

  // UI params
  public static final String ACCORDION = "ui.accordion";
  public static final String ACCORDION_ID = ACCORDION +".id";
  public static final String DATATABLES = "ui.dataTables";
  public static final String DATATABLES_ID = DATATABLES +".id";
  public static final String DATATABLES_SELECTOR = DATATABLES +".selector";
  public static final String DIALOG = "ui.dialog";
  public static final String DIALOG_ID = DIALOG +".id";
  public static final String DIALOG_SELECTOR = DIALOG +".selector";
  public static final String PROGRESSBAR = "ui.progressbar";
  public static final String PROGRESSBAR_ID = PROGRESSBAR +".id";
  public static final String THEMESWITCHER = "ui.themeswitcher";
  public static final String THEMESWITCHER_ID = THEMESWITCHER +".id";
  public static final String THEME_KEY = "theme";
  public static final String COOKIE_THEME = "jquery-ui-theme";
  // common CSS classes
  public static final String _PROGRESSBAR =
      ".ui-progressbar.ui-widget.ui-widget-content.ui-corner-all";
  public static final String C_PROGRESSBAR =
      _PROGRESSBAR.replace('.', ' ').trim();
  public static final String _PROGRESSBAR_VALUE =
      ".ui-progressbar-value.ui-widget-header.ui-corner-left";
  public static final String C_PROGRESSBAR_VALUE =
      _PROGRESSBAR_VALUE.replace('.', ' ').trim();
  public static final String _INFO_WRAP =
      ".info-wrap.ui-widget-content.ui-corner-bottom";
  public static final String _TH = ".ui-state-default";
  public static final String C_TH = _TH.replace('.', ' ').trim();
  public static final String C_TABLE = "table";
  public static final String _INFO = ".info";
  public static final String _ODD = ".odd";
  public static final String _EVEN = ".even";

  @Override
  protected void render(Block html) {
    html.
      link(root_url(join("static/jquery/themes-1.8.16/",
        getTheme(), "/jquery-ui.css"))).
      link(root_url("static/dt-1.7.5/css/jui-dt.css")).
      script(root_url("static/jquery/jquery.min-1.6.4.js")).
      script(root_url("static/jquery/jquery-ui.min-1.8.16.js")).
      script(root_url("static/dt-1.7.5/js/jquery.dataTables.min.js")).
      script(root_url("static/yarn.dt.plugins.js")).
      script(root_url("static/themeswitcher.js")).
      style("#jsnotice { padding: 0.2em; text-align: center; }",
            ".ui-progressbar { height: 1em; min-width: 5em }"); // required

    List<String> list = Lists.newArrayList();
    initAccordions(list);
    initDataTables(list);
    initDialogs(list);
    initProgressBars(list);
    initThemeSwitcher(list);

    if (!list.isEmpty()) {
      html.
        script().$type("text/javascript").
          _("$(function() {")._(list.toArray())._("});")._();
    }
  }

  public static void jsnotice(HTML html) {
    html.
      div("#jsnotice.ui-state-error").
          _("This page works best with javascript enabled.")._();
    html.
      script().$type("text/javascript").
        _("$('#jsnotice').hide();")._();
  }

  protected void initAccordions(List<String> list) {
    for (String id : split($(ACCORDION_ID))) {
      if (Html.isValidId(id)) {
        String init = $(initID(ACCORDION, id));
        if (init.isEmpty()) {
          init = "{autoHeight: false}";
        }
        list.add(join("  $('#", id, "').accordion(", init, ");"));
      }
    }
  }

  protected void initDataTables(List<String> list) {
    String defaultInit = "{bJQueryUI: true, sPaginationType: 'full_numbers'}";
    for (String id : split($(DATATABLES_ID))) {
      if (Html.isValidId(id)) {
        String init = $(initID(DATATABLES, id));
        if (init.isEmpty()) {
          init = defaultInit;
        }
        list.add(join(id,"DataTable =  $('#", id, "').dataTable(", init,
                      ").fnSetFilteringDelay(188);"));
        String postInit = $(postInitID(DATATABLES, id));
        if(!postInit.isEmpty()) {
          list.add(postInit);
        }
      }
    }
    String selector = $(DATATABLES_SELECTOR);
    if (!selector.isEmpty()) {
      String init = $(initSelector(DATATABLES));
      if (init.isEmpty()) {
        init = defaultInit;
      }
      list.add(join("  $('", escapeJavaScript(selector), "').dataTable(", init,
               ").fnSetFilteringDelay(288);"));
    }
  }

  protected void initDialogs(List<String> list) {
    String defaultInit = "{autoOpen: false, show: transfer, hide: explode}";
    for (String id : split($(DIALOG_ID))) {
      if (Html.isValidId(id)) {
        String init = $(initID(DIALOG, id));
        if (init.isEmpty()) {
          init = defaultInit;
        }
        String opener = $(djoin(DIALOG, id, "opener"));
        list.add(join("  $('#", id, "').dialog(", init, ");"));
        if (!opener.isEmpty() && Html.isValidId(opener)) {
          list.add(join("  $('#", opener, "').click(function() { ",
                   "$('#", id, "').dialog('open'); return false; });"));
        }
      }
    }
    String selector = $(DIALOG_SELECTOR);
    if (!selector.isEmpty()) {
      String init = $(initSelector(DIALOG));
      if (init.isEmpty()) {
        init = defaultInit;
      }
      list.add(join("  $('", escapeJavaScript(selector),
               "').click(function() { $(this).children('.dialog').dialog(",
               init, "); return false; });"));
    }
  }

  protected void initProgressBars(List<String> list) {
    for (String id : split($(PROGRESSBAR_ID))) {
      if (Html.isValidId(id)) {
        String init = $(initID(PROGRESSBAR, id));
        list.add(join("  $('#", id, "').progressbar(", init, ");"));
      }
    }
  }

  protected void initThemeSwitcher(List<String> list) {
    for (String id : split($(THEMESWITCHER_ID))) {
      if (Html.isValidId(id)) {
        list.add(join("  $('#", id,
                      "').themeswitcher({expires:888, path:'/'});"));
        break; // one is enough
      }
    }
  }

  protected String getTheme() {
    String theme = $(THEME_KEY);
    if (!theme.isEmpty()) {
      return theme;
    }
    Cookie c = cookies().get(COOKIE_THEME);
    if (c != null) {
      return c.getValue().toLowerCase(Locale.US).replace("%20", "-");
    }
    return "base";
  }

  public static String initID(String name, String id) {
    return djoin(name, id, "init");
  }
  
  public static String postInitID(String name, String id) {
    return djoin(name, id, "postinit");
  }

  public static String initSelector(String name) {
    return djoin(name, "selector.init");
  }

  public static StringBuilder tableInit() {
    return new StringBuilder("{bJQueryUI:true, ").
        append("sPaginationType: 'full_numbers', iDisplayLength:20, ").
        append("aLengthMenu:[20, 40, 60, 80, 100]");
  }

  public static StringBuilder tableInitProgress(StringBuilder init,
                                                long numCells) {
    return init.append(", bProcessing:true, ").
        append("oLanguage:{sProcessing:'Processing ").
        append(numCells).append(" cells...").
        append("<p><img src=\"/static/busy.gif\">'}");
  }
}
