{% extends 'base.html' %}

{% block title %}Terms{% endblock %}
{% block header %}Terms{% endblock %}

{% block body %}

<p>
  <img id="showHideFilters" src="{{ url_for('static', filename='icn/plus-button.png') }}" />
  Filters
</p>
<div id="filterControls" style="display: none; margin-left: 20px;">
  <table>
    <tr>
      <td>Language</td>
      <td>
        <select id="filtLanguage">
          {% for langopt in language_options %}
          <option value={{ langopt[0] }}>{{ langopt[1] }}</option>
          {% endfor %}
        </select>
      </td>
    <tr>
      <td>Parent terms only</td>
      <td>
        <input id="filtParentsOnly" type="checkbox" />
      </td>
    </tr>
    <tr>
      <td>Age (days since created)</td>
      <td>
        <input id="filtAgeMin" style="width: 50px;" type="text" placeholder="min" />
        to
        <input id="filtAgeMax" style="width: 50px;" type="text" placeholder="max" />
      </td>
    </tr>
    <tr>
      <td>Status range</td>
      <td>
        <select id="filtStatusMin">
          {% for s in filter_statuses %}
          <option value="{{s.id}}" {%if s.id==1 %}selected{% endif %}>{{ s.text }}</option>
          {% endfor %}
        </select>
        to
        <select id="filtStatusMax">
          {% for s in filter_statuses %}
          <option value="{{s.id}}" {%if s.id==99 %}selected{% endif %}>{{ s.text }}</option>
          {% endfor %}
        </select>
      </td>
    </tr>
    <tr>
      <td>Include Ignored</td>
      <td>
        <input id="filtIncludeIgnored" type="checkbox" />
      </td>
    </tr>
    <tr id="filtTermIDs_row" style="display: none">
      <td>Term IDs</td>
      <td>
        <span id="filtTermIDs_message">(msg here)</span>
        <input type="hidden" id="filtTermIDs" />
      </td>
    </tr>
  </table>
  <button id="clearFilters">Clear all</button>
  <br />
</div>


<div id="term_actions" class="term-action-dropdown" onmouseover="activate_term_actions();">
  <button class="term-action-button">Actions</button>
  <div class="term-action-content">
    <a href="/term/new">Create new</a>
    <a id="term_action_bulk_edit" class="bulkActionLink" href="#" onclick="show_bulk_edit_form(event, this);">Bulk edit</a>
    <a id="term_action_post_anki" href="#" onclick="post_anki(event);">Create Anki cards</a>
    <a id="term_action_bulk_delete" class="bulkActionLink" href="#" onclick="bulk_delete(event);">Delete selected</a>
    <a id="term_action_export_csv" href="#" onclick="export_csv(event);">Export CSV</a>
  </div>
</div>

<div id="bulkEditDiv" class="actionDiv" style="display: none;">
  <form id="term-form" name="term_bulk_form" method="POST" action="/term/bulk_edit_from_index">
    <div id="term-bulk-form-fields">
      {% include "term/_bulk_edit_form_fields.html" %}
      <div id="term-button-container">
        <button id="btnsubmit" type="submit" class="btn btn-primary" onclick="set_bulk_wordids(); return true;">Save</button>
        <button id="btncancel" type="button" class="btn btn-secondary" onclick="hide_bulk_edit_form(); return false;">Cancel</button>
      </div>
    </div>
  </form>
</div>

<table id="termtable" class="table dataTable stripe">
  <thead>
    <tr>
      <th style="width: 20px;"><input type="checkbox" id="chkAll" onclick="handleChkAll()"></th>
      <!-- adding text-align because sometimes datatables seemed to align them to right. -->
      <th style="text-align: left">Term</th>
      <th style="text-align: left">Parents</th>
      <th style="text-align: left">Translation</th>
      <th style="text-align: left">Tags</th>
      <th style="text-align: left">Status</th>
      <th style="text-align: left">Language</th>
      <th style="text-align: left">Added</th>
      <th style="text-align: left">TermID</th>
      <th style="text-align: left">LanguageID</th>
    </tr>
  </thead>
</table>

<script type="text/javascript" src="/static/js/never_cache/lute-tagify-utils.js" charset="utf-8"></script>

<script>

  let setup_term_datatable = function(table_state_key, initial_search) {

    // On success, show checkmark near the td where post happened.
    function _show_saved_checkmark(td) {
      const tooltip = $('<div class="ajax-saved-checkmark">&#10003;</div>').hide();
      $('body').append(tooltip);
      const targetElement = $(td);
      const offset = targetElement.offset();
      const offset_left = offset.left + targetElement.outerWidth() - tooltip.outerWidth() + 5;
      tooltip.css({ top: offset.top + 5, left: offset_left });
      tooltip.fadeIn(100);
      setTimeout(() => { tooltip.fadeOut(200, function() { $(this).remove(); }); }, 800);
    }

    /**
     * Post the update, and update the term status.
     * We only need to update the status as that's the only
     * thing that might change as a side-effect of setting
     * the parent.
     */
    function post_update(td, term_id, update_type, update) {

      // Update via datatables dom manipulation, keeps the dt
      // model consistent.
      const _update_status_in_row = function(updated_status) {
        const table = $('#termtable').DataTable();
        const row = table.row(td.closest('tr'));
        const select = row.node().querySelector('.term-status-select');
        if (select)
          select.value = updated_status;
      };

      const payload = {
        term_id: term_id,
        update_type: update_type,
        values: update
      }
      const url = '/term/ajax_edit_from_index';
      $.ajax({
        url: url,
        type: 'POST',
        contentType: 'application/json',
        data: JSON.stringify(payload),
        success: function(response) {
          _show_saved_checkmark(td);
          _update_status_in_row(response.status);
        },
        error: function(xhr) {
          if (xhr.responseJSON && xhr.responseJSON.error) {
            console.error('Error:', xhr.responseJSON.error);
          } else {
            console.error('Unexpected error:', xhr.statusText);
          }
        }
      });
    }

    /**
     * Self-monitoring cell with a tagify instance that posts on lost
     * focus, if the state has changed.
     */
    const editableTagifyCell = function(
      word_id, td, tagify, post_tags_as,
    ) {
      // tagify loses focus when adding tags or clicking tag removal,
      // calling the 'blur' event when not wanted; suppress that if needed.
      let suppressBlur = false;

      $(tagify.DOM.scope).on('mousedown', '.tagify__tag__removeBtn', function () {
        suppressBlur = true;
      });

      // After add/remove, set focus on the tagify input span again,
      // so that tagify.on('blur') is fired when leaving the input box.
      tagify.on('add remove', function() {
        suppressBlur = true;
        setTimeout(function() {
          const s = $(tagify.DOM.scope);
          s.find("span.tagify__input").last().focus();
          suppressBlur = false;
        }, 0);
      });

      const add_pending_tags = function() {
        const s = $(tagify.DOM.scope).find("span.tagify__input").last();
        const pending_tag = s.text().trim();
        if (pending_tag != '')
          tagify.addTags([pending_tag], false, true);
      };

      // Save tag list state, only post on change.
      const curr_tags = () => tagify.value.map(e => e.value).filter(e => e.trim() != "");
      const curr_state = () => curr_tags().sort().join(';;');
      let old_state = curr_state();
      const post_if_changed = function() {
        if (old_state != curr_state())
          post_update(td, word_id, post_tags_as, curr_tags());
        old_state = curr_state();
      }

      tagify.on('blur', function (e) {
        if (!suppressBlur) {
          add_pending_tags();
          post_if_changed();
        }
      });
    };

    const _make_tagify_text_control = function(td, cellData) {
      const input = document.createElement('input');
      input.type = 'text';
      input.value = cellData;
      $(td).empty().append(input);
      return input;
    };

    const editableParentsCell = function (td, cellData, rowData, row, col) {
      const input = _make_tagify_text_control(td, cellData);
      const lang_id = parseInt(rowData["LgID"]);
      const lang_id_func = () => lang_id;
      const tagify = lute_tagify_utils_setup_parent_tagify(
        input, lang_id_func, rowData["WoText"], {}
      );
      editableTagifyCell(rowData["WoID"], td, tagify, 'parents');
    };

    const editableTranslationCell = function(td, cellData, rowData, row, col) {
      const div = document.createElement('div');
      $(div).addClass("translationDiv");
      div.setAttribute('contenteditable', true);
      div.innerHTML = (cellData ?? '').replace(/\r\n/g, '<br />');
      $(td).empty().append(div);

      const imgsrc = rowData["WiSource"];
      if (imgsrc) {
        const img = document.createElement('img');
        img.className = 'term-listing-image';
        img.src = `/userimages/${rowData["LgID"]}/${imgsrc}`;
        $(td).append(img);
      }

      let old_state = (cellData ?? '');
      div.addEventListener('blur', function(e) {
        // contenteditable puts each line into its own div.
        const new_content = div.innerHTML
              .replace(/<div>/g, '\r\n')
              .replace(/<\/div>/g, '')
              .replace(/<br\s*\/?>/g, '\r\n')
              .trim();
        if (old_state.trim() !== new_content) {
          const row = table.row(e.target.parentElement);
          post_update(td, rowData["WoID"], 'translation', new_content);
          old_state = new_content;  // don't repost if just entering and exiting
        }
      });
    };  // end editableTranslationCell

    var TAGS = {{ tags | safe }};
    const editableTagsCell = function (td, cellData, rowData, row, col) {
      const input = _make_tagify_text_control(td, cellData);
      const tagify = lute_tagify_utils_setup_term_tag_tagify(input, TAGS, {});
      editableTagifyCell(rowData["WoID"], td, tagify, 'term_tags');
    };

    const editableStatusCell = function (td, cellData, rowData, row, col) {
      function makeDropdown(td) {
        const select = document.createElement('select');
        select.classList.add('term-status-select');
        select.innerHTML = '';
        {% for s in update_statuses %}
          select.innerHTML += '<option value="{{s.id}}">{{ s.text }}</option>';
        {% endfor %}
        select.value = parseInt(rowData["StID"]);

        td.innerHTML = '';
        td.appendChild(select);
        return select;
      }

      const select = makeDropdown(td);
      select.addEventListener('change', function () {
        post_update(td, rowData["WoID"], 'status', select.value);
      });
    };

    table = $('#termtable').DataTable({
      layout: {
        topStart: 'pageLength',
        topEnd: 'search',
        bottomStart: ['info', 'buttons'],
        bottomEnd: 'paging'
      },
      responsive: true,
      select: false,
      lengthMenu: [ 25, 50, 100, 500, 1000 ],

      // The button is included but the div.dt-buttons is hidden,
      // so that the button actions can be trigged by a link
      // in the "Actions" list.
      buttons: [
         {
           extend: 'download',
           url: '/term/export_terms',
           text: 'Export CSV',
         },
      ],
      paging: true,
      info: true,
      searching: true,
      processing: true,
      serverSide: true,
      stateSave: true,
      stateSaveCallback: function(settings, data) {
        localStorage.setItem(table_state_key, JSON.stringify(data));
      },
      stateLoadCallback: function(settings) {
        const state = localStorage.getItem(table_state_key);
        return state ? JSON.parse(state) : null;
      },
      select: {
        style: 'multi',
        selector: 'td:first-child input[type="checkbox"]',
        className: 'row-selected'
      },
      search: { search: initial_search },
      columns: [
        { searchable: false, orderable: false, render: render_checkbox },
        { name: "WoText", render: render_text },
        { name: "ParentText", data: "ParentText", createdCell: editableParentsCell },
        { name: "WoTranslation", width: "40%", searchable: true, data: "WoTranslation", createdCell: editableTranslationCell },
        { name: "TagList", data: "TagList", createdCell: editableTagsCell },
        { name: "StID", data: "StID", createdCell: editableStatusCell },
        { name: "LgName", data: "LgName" },
        { name: "WoCreated", render: render_date_created },
        { name: "WoID", data: "WoID", visible: false },
        { name: "LgID", data: "LgID", visible: false },
      ],

      // Ajax call
      ajax: {
        url: '/term/datatables',
        type: "POST",

        // Additional filters.  func calls are required to get the
        // current filter field values.  These are included in the
        // data sent to the controller, and are used by the
        // TermRepository.
        data: {
          filtLanguage: () => $('#filtLanguage').val(),
          filtParentsOnly: () => $('#filtParentsOnly').prop('checked'),
          filtAgeMin: () => $('#filtAgeMin').val(),
          filtAgeMax: () => $('#filtAgeMax').val(),
          filtStatusMin: () => $('#filtStatusMin').val(),
          filtStatusMax: () => $('#filtStatusMax').val(),
          filtIncludeIgnored: () => $('#filtIncludeIgnored').prop('checked'),
          filtTermIDs: () => $('#filtTermIDs').val(),
        },

        dataType: "json"
      },

    });
  } // end setup_term_datatable


  let render_text = function ( data, type, row, meta ) {
    return `<a id="term_${row['WoID']}" href="/term/edit/${row['WoID']}">${row['WoText']}</a>`;
  };

  let render_checkbox = function (data, type, row, meta) {
    return `<input type="checkbox" class="chkWord" name="wordids" wordid="${row['WoID']}" langid="${row['LgID']}">`;
  };

  let render_date_created = function (data, type, row, meta) {
    const dt = row["WoCreated"];
    const datepart = dt.split(' ')[0];
    return `<span title="${dt}">${datepart}</span>`;
  };


  let handleChkAll = function() {
    const v = $('#chkAll').prop('checked');
    $('.chkWord').each(function(i) {
      $(this).prop('checked', v);
    });
  }

  /** Deactivate term Actions, activate if they're possible. */
  function activate_term_actions() {
    const checked_count = $('.chkWord:checked').length;
    const lang_id = get_lang_id();
    let should_disable = (checked_count === 0 || lang_id == null);
    $(".bulkActionLink").toggleClass("actionDisabled", should_disable);
  }


  function show_bulk_edit_form(event, el, displaydiv, focusel) {
    event.preventDefault();
    if ($(el).hasClass("actionDisabled"))
      return;
    $(`#bulkEditDiv`).css("display", "block");
  }


  function hide_bulk_edit_form() {
    const f = $("#term-form");
    f.find("input, textarea, select").val("");
    f.find("input:checkbox, input:radio").prop("checked", false);
    $(`#bulkEditDiv`).css("display", "none");
  }

  // Return the clicked checkboxes' language ID,
  // or null if the lang id isn't the same for all.
  let get_lang_id = function() {
    let langids = [];
    $('.chkWord:checked').each(function(i) {
      langids.push($(this).attr('langid'));
    });
    const unique_langids = [...new Set(langids)];
    let ret = null;
    if (unique_langids.length == 1) {
      ret = unique_langids[0];
    }
    // console.log(`for langids ${langids}, get_lang_id returned ${ret}`);
    return ret;
  }

  let _get_checked_wordids = function() {
    const wordids = [];
    $('.chkWord:checked').each(function(i) {
      wordids.push($(this).attr('wordid'));
    });
    return wordids;
  };

  function set_bulk_wordids() {
    $("#txtWordIds").val(_get_checked_wordids().join(","));
  }

  /**
   * Post the checked words to Anki service, add tooltip.
   */
  function post_anki(event) {
    event.preventDefault();
    const checked = $('.chkWord:checked');
    const word_ids = checked.toArray().map(el => parseInt($(el).attr('wordid')));
    if (word_ids.length === 0) {
      console.log("No terms selected");
      return;
    }

    function add_tooltip(term_id, results) {
      const el = $(`#term_${term_id}`);
      if (!el.length) {
        console.log(`Not found id #term_${term_id}`);
        return;
      }

      let show_results = "<b>Anki exports:</b><br />"
      show_results += results.replaceAll("\n", "<br />");
      el.attr("title", show_results);
      el.tooltip({
        content: function (callback) {
          callback($(this).prop('title'));
        }
      });
      el.tooltip("open");
    }

    // The term listing doesn't have a "current sentence" for terms,
    // so rely on lookups.
    termid_sentences = {};

    const ANKI_CONNECT_URL = LUTE_USER_SETTINGS["ankiconnect_url"];
    LuteAnki.post_anki_cards(
      ANKI_CONNECT_URL, word_ids, termid_sentences, add_tooltip
    ).catch(error => {
      console.error("ERROR:", error);
      alert(error.message);
    });
  }

  function bulk_delete(event) {
    event.preventDefault();
    if ($("#term_action_bulk_delete").hasClass("actionDisabled"))
      return;
    const checked = $('.chkWord:checked');
    const ids = checked.toArray().map(el => parseInt($(el).attr('wordid')));
    const t = (ids.length == 1) ? 'term' : 'terms';
    const resp = confirm(`Deleting ${ids.length} ${t}.  Continue?`);
    if (! resp)
      return;

    data = {
      wordids: ids
    };
    $.ajax({
      url: '/term/bulk_delete',
      method: 'POST',
      data: JSON.stringify(data),
      contentType: "application/json; charset=utf-8",
      success: function(data) {
        location.reload();
      }
    });
  }


  /** Click the export button.
   * I'm doing this so that the Datatables "export" extension button
   * works out of the box, but the actual call to export the CSV
   * is in the "Actions" list.
   */
  function export_csv(event) {
    event.preventDefault();
    /*
    // TODO export_csv: only export selected/checked terms?
    const checked = $('.chkWord:checked');
    const ids = checked.toArray().map(el => parseInt($(el).attr('wordid')));
    */
    const export_button = $('.dt-button:contains("Export CSV")');
    if (export_button.length === 0) {
      throw new Error("Export CSV button not found");
    }
    export_button.click();
  };


  let update_filter_storage = function() {
    const store = {
      filtDisplay: $('#filterControls').css('display'),
      filtLanguage: $('#filtLanguage').val(),
      filtParentsOnly: $('#filtParentsOnly').prop('checked'),
      filtAgeMin: $('#filtAgeMin').val(),
      filtAgeMax: $('#filtAgeMax').val(),
      filtStatusMin: $('#filtStatusMin').val(),
      filtStatusMax: $('#filtStatusMax').val(),
      filtIncludeIgnored: $('#filtIncludeIgnored').prop('checked'),
      filtTermIDs: $('#filtTermIDs').val(),
    };
    // console.log('saving: ' + JSON.stringify(store));
    sessionStorage.setItem('termtable_filters', JSON.stringify(store));
  };

  let wipe_filter_storage = function() {
    $('#filtLanguage').val(0);
    $('#filtParentsOnly').prop('checked', false);
    $('#filtAgeMin').val('');
    $('#filtAgeMax').val('');
    $('#filtStatusMin').val(1);
    $('#filtStatusMax').val(99);
    $('#filtIncludeIgnored').prop('checked', false);
    $('#filtTermIDs').val('');
    $('#filtTermIDs_row').css('display', 'none');
    update_filter_storage();
  };

  let handle_filter_update = function() {
    update_filter_storage();
    $('#termtable').DataTable().draw();
  };


  let update_filter_button_src = function(is_hidden) {
    // console.log(`is_hidden = ${is_hidden}`);
    const new_src = is_hidden ?
          "{{ url_for('static', filename='icn/plus-button.png') }}" :
          "{{ url_for('static', filename='icn/minus-button.png') }}";
    $('#showHideFilters').prop('src', new_src);
  }

  let load_filters_from_storage = function() {
    fs = sessionStorage.getItem('termtable_filters');
    // console.log(fs);
    if (fs == null)
      return;
    store = JSON.parse(fs);

    $('#filterControls').css('display', store.filtDisplay);
    update_filter_button_src(store.filtDisplay == "none");
    $('#filtLanguage').val(parseInt(store.filtLanguage ?? '0'));
    $('#filtParentsOnly').prop('checked', store.filtParentsOnly ?? false);
    if ((store.filtAgeMin ?? '') != '')
      $('#filtAgeMin').val(parseInt(store.filtAgeMin));
    if ((store.filtAgeMax ?? '') != '')
    $('#filtAgeMax').val(parseInt(store.filtAgeMax));
    $('#filtStatusMin').val(parseInt(store.filtStatusMin ?? '1'));
    $('#filtStatusMax').val(parseInt(store.filtStatusMax ?? '99'));
    $('#filtIncludeIgnored').prop('checked', store.filtIncludeIgnored);
    if ((store.filtTermIDs ?? '') != '') {
      $('#filtTermIDs').val(store.filtTermIDs);
      $('#filtTermIDs_row').css('display', 'table-row');
      $('#filtTermIDs_message').text('(Filtered from reading pane)');
    }
  };

  let handle_show_hide_filter_click = function() {
    const fc = $('#filterControls');
    wipe_filter_storage();
    const new_display = (fc.css('display') == 'none' ? 'block' : 'none');
    fc.css('display', new_display);
    update_filter_button_src(new_display == 'none');
    update_filter_storage();  // to store new display setting.
    $('#termtable').DataTable().draw();
  };

  let handle_clear_filters = function() {
    wipe_filter_storage();
    $('#termtable').DataTable().draw();
  };

  // Return the termids and params if those were passed, else null.
  let get_termids_list_params_from_query_string = function() {
    const queryString = window.location.search;
    const params = new URLSearchParams(queryString);
    if (!params.has('termids'))
      return null;

    let termids = params.get('termids');
    // The termids are passed as "+" separated values,
    // but the browser might replace them with spaces.
    // Restore '+' for standard handling.
    termids = termids.replaceAll(' ', '+');

    return {
      termids_string: termids.split('+').join(','),
      bookid: params.get('bookid'),
      pagenum: params.get('pagenum'),
    };
  };

  $(document).ready(function () {
    // Handlers.
    $('#showHideFilters').click(handle_show_hide_filter_click);
    $('#filtLanguage').change(handle_filter_update);
    $('#filtParentsOnly').change(handle_filter_update);
    $('#filtAgeMin').keyup(handle_filter_update);
    $('#filtAgeMax').keyup(handle_filter_update);
    $('#filtStatusMin').change(handle_filter_update);
    $('#filtStatusMax').change(handle_filter_update);
    $('#filtIncludeIgnored').change(handle_filter_update);
    // filtTermIDs are not interactive, no handler needed.
    $('#clearFilters').click(handle_clear_filters);

    // Datatables state needs to be different for regular vs term
    // list.  If they were the same, then setting the page number
    // on the filtered term id list would also affect the default
    // term listing.
    let dt_saved_state_key = "saved_state_default";

    // If term ids were passed in, override any storage values.
    // Filters are fast to apply, not concerned about this.
    const hsh = get_termids_list_params_from_query_string();
    if (hsh != null) {
      wipe_filter_storage();
      $('#filterControls').css('display', 'block');
      $('#filtStatusMin').val(0);
      $('#filtTermIDs').val(hsh.termids_string);
      update_filter_storage();
      // Each book and page gets its own state.
      dt_saved_state_key = `saved_state_${hsh.bookid}_${hsh.pagenum}`;
    }

    load_filters_from_storage();

    // Setting up the datatable now, so the filters are
    // taken into account.
    setup_term_datatable(dt_saved_state_key, "{{ initial_search or '' }}");

    if (LUTE_USER_SETTINGS["use_ankiconnect"] == false) {
      $("#term_action_post_anki").css('display', 'none');
    }
  });


</script>

{% endblock %}
