<template>
  <div
    class="field is-grouped is-grouped-multiline is-gap-4 px-2"
    v-if="filters?.length > 0"
  >
    <div class="control">
      <a
        class="button is-primary is-light fs-14"
        @click="updateData({ filters: [] })"
      >
        Xóa lọc
      </a>
    </div>
    <div class="control">
      <span class="icon-text">
        <span class="icon">
          <Icon
            name="mdi:sigma"
            :size="20"
            class="has-text-primary"
          />
        </span>
        <span class="fs-16 font-semibold has-text-primary">{{ totalRows }}</span>
      </span>
    </div>
    <div
      class="control"
      v-for="(v, i) in filters"
      :key="i"
    >
      <div class="tags has-addons is-marginless">
        <a
          class="tag is-primary is-light is-marginless"
          @click="showCondition(v)"
          >{{ v.label.indexOf(">") >= 0 ? $stripHtml(v.label, 30) : v.label }}</a
        >
        <a
          class="tag is-delete is-marginless"
          @click="removeFilter(i)"
        ></a>
      </div>
      <span class="help">
        {{
          v.sort
            ? v.sort
            : v.select
              ? "[" + (v.select.length > 0 ? $stripHtml(v.select[0], 20) : "") + "...&#931;" + v.select.length + "]"
              : v.condition
        }}</span
      >
    </div>
  </div>
  <div
    class="table-container mb-0"
    ref="container"
    id="docid"
  >
    <table
      class="table is-fullwidth is-bordered is-narrow is-hoverable"
      :style="tableStyle"
    >
      <thead>
        <tr>
          <th
            v-for="(field, i) in displayFields"
            :key="i"
            :style="field.headerStyle"
            class="is-clickable header"
            @click="showField(field)"
          >
            <div :style="field.dropStyle">
              <a
                v-if="field.label.indexOf('<') < 0"
                class="font-semibold"
                >{{ field.label }}</a
              >
              <a v-else>
                <component
                  :is="dynamicComponent(field.label)"
                  :row="v"
                  @clickevent="clickEvent($event, v, field)"
                />
              </a>
            </div>
          </th>
        </tr>
      </thead>
      <tbody>
        <tr
          v-for="(v, i) in displayData"
          :key="i"
        >
          <td
            v-for="(field, j) in displayFields"
            :key="j"
            :id="field.name"
            :style="v[`${field.name}color`]"
            style="overflow: hidden; text-overflow: ellipsis"
            @dblclick="doubleClick(field, v)"
          >
            <component
              :is="dynamicComponent(field.template)"
              :row="v"
              v-if="field.template"
              @clickevent="clickEvent($event, v, field)"
            />
            <span v-else>{{ v[field.name] }}</span>
          </td>
        </tr>
      </tbody>
    </table>
    <DatatablePagination
      v-bind="{ data: data, perPage: perPage }"
      @changepage="changePage"
      v-if="showPaging"
    ></DatatablePagination>
  </div>
  <Modal
    @close="close"
    @selected="doSelect"
    @confirm="confirmRemove"
    v-bind="showmodal"
    v-if="showmodal"
  />
</template>
<script setup>
import { createApp } from "vue/dist/vue.esm-bundler.js";
import { ref, defineComponent } from "vue";
import { useStore } from "~/stores/index";
const emit = defineEmits(["edit", "insert", "dataevent"]);
const {
  $calc,
  $calculate,
  $calculateData,
  $copy,
  $deleterow,
  $empty,
  $find,
  $getdata,
  $getEditRights,
  $formatNumber,
  $multiSort,
  $remove,
  $stripHtml,
  $unique,
} = useNuxtApp();
const store = useStore();
const props = defineProps({
  pagename: String,
});

function dynamicComponent(htmlString) {
  return defineComponent({
    template: htmlString,
    props: {
      row: {
        type: Object,
        default: () => ({}),
      },
    },
  });
}
var timer = undefined;
var showPaging = ref(false);
var totalRows = ref(0);
var currentPage = 1;
var displayFields = ref([]);
var displayData = [];
var pagedata = store[props.pagename];
let tablesetting = $copy(pagedata.tablesetting || store.tablesetting);
if (!Array.isArray(tablesetting)) {
  tablesetting = Object.values(tablesetting);
}
// console.log("props.pagename", props.pagename);
// console.log("pagedata.tablesetting", pagedata.tablesetting);
// console.log("store.tablesetting", store.tablesetting);
// console.log("tablesetting", tablesetting);
// var tablesetting = $copy(store.tablesetting);
// const tablesettingObj = Object.fromEntries(tablesetting.map((v) => [v.code, v]));
var perPage = Number($find(tablesetting, { code: "per-page" }, "detail")) || 20;
var filters = $copy(pagedata.filters || []);
var currentField;
var filterData = [];
var currentsetting;
var scrollbar;
var fields;
var currentRow;
var data = $copy(pagedata.data);
var showmodal = ref();
watch(
  () => store[props.pagename],
  () => {
    updateChange();
  },
);
function updateChange() {
  pagedata = store[props.pagename];
  if (!pagedata.update) return;
  if (pagedata.update.data) data = $copy(pagedata.update.data);
  if (pagedata.update.filters) {
    doFilter(pagedata.update.filters);
    updateShow();
    return; //exit
  }
  if (filters.length > 0) doFilter(filters);
  if (pagedata.update.fields || pagedata.update.data) updateShow();
}
const updateShow = function (full_data) {
  // allowed JS expressions - should return a boolean
  const allowedFns = {
    "$getEditRights()": $getEditRights,
  };
  const shownFields = pagedata.fields.filter(({ show }) => {
    if (typeof show === "boolean") return show;
    else {
      // show is a string
      if (show === "true") return true;
      if (show === "false") return false;
      return allowedFns[show]?.() || false;
    }
  });

  if (full_data === false) {
    displayData = $copy(data);
  } else {
    displayData = data.filter((ele, index) => index >= (currentPage - 1) * perPage && index < currentPage * perPage);
  }
  displayData.forEach((v) => {
    shownFields.forEach((x) => (v[`${x.name}color`] = getStyle(x, v)));
  });
  shownFields.forEach((v) => {
    v.headerStyle = getSettingStyle("header", v);
    v.dropStyle = getSettingStyle("dropdown", v);
  });
  displayFields.value = shownFields;
  showPagination();
};
function confirmRemove() {
  $deleterow(pagedata.api.name, currentRow.id, props.pagename, true);
}
const clickEvent = function (event, row, field) {
  let name = typeof event === "string" ? event : event.name;
  let data = typeof event === "string" ? event : event.data;
  if (name === "remove") {
    currentRow = row;
    showmodal.value = {
      component: "dialog/Confirm",
      vbind: { content: "Bạn có muốn xóa bản ghi này không?", duration: 10 },
      title: "Xác nhận",
      width: "500px",
      height: "auto",
    };
  }
  emit(name, row, field, data);
};
const showField = async function (field) {
  if (pagedata.contextMenu === false || field.menu === "no") return;
  currentField = field;
  filterData = $unique(pagedata.data, [field.name]);
  //let doc = this.$refs[`th${field.name}`]
  //let width = (doc? doc.length>0 : false)? doc[0].getBoundingClientRect().width : 100
  let width = 100;
  if (pagedata.setting) currentsetting = $copy(pagedata.setting);
  showmodal.value = {
    vbind: {
      pagename: props.pagename,
      field: field,
      filters: filters,
      filterData: filterData,
      width: width,
    },
    component: "datatable/ContextMenu",
    title: field.name,
    width: "650px",
    height: "500px",
  }; //$stripHtml(field.label)
};
const getStyle = function (field, record) {
  let stop = false;
  let val = tablesetting.find((v) => v.code === "td-border")
    ? tablesetting.find((v) => v.code === "td-border").detail
    : "border: solid 1px rgb(44, 44, 44); ";
  val = val.indexOf(";") >= 0 ? val : val + ";";
  if (field.bgcolor ? !Array.isArray(field.bgcolor) : false) {
    val += ` background-color:${field.bgcolor}; `;
  } else if (field.bgcolor ? Array.isArray(field.bgcolor) : false) {
    field.bgcolor.map((v) => {
      if (v.type === "search") {
        if (
          record[field.name] && !stop ? record[field.name].toLowerCase().indexOf(v.keyword.toLowerCase()) >= 0 : false
        ) {
          val += ` background-color:${v.color}; `;
          stop = true;
        }
      } else {
        let res = $calculate(record, v.tags, v.expression);
        if (res.success && res.value && !stop) {
          val += ` background-color:${v.color}; `;
          stop = true;
        }
      }
    });
  }
  stop = false;
  if (field.color ? !Array.isArray(field.color) : false) {
    val += ` color:${field.color}; `;
  } else if (field.color ? Array.isArray(field.color) : false) {
    field.color.map((v) => {
      if (v.type === "search") {
        if (
          record[field.name] && !stop ? record[field.name].toLowerCase().indexOf(v.keyword.toLowerCase()) >= 0 : false
        ) {
          val += ` color:${v.color}; `;
          stop = true;
        }
      } else {
        let res = $calculate(record, v.tags, v.expression);
        if (res.success && res.value && !stop) {
          val += ` color:${v.color}; `;
          stop = true;
        }
      }
    });
  }
  stop = false;
  if (field.textsize ? !Array.isArray(field.textsize) : false) {
    val += ` font-size:${field.textsize}px; `;
  } else if (field.textsize ? Array.isArray(field.textsize) : false) {
    field.textsize.map((v) => {
      if (v.type === "search") {
        if (
          record[field.name] && !stop ? record[field.name].toLowerCase().indexOf(v.keyword.toLowerCase()) >= 0 : false
        ) {
          val += ` font-size:${v.size}px; `;
          stop = true;
        }
      } else {
        let res = $calculate(record, v.tags, v.expression);
        if (res.success && res.value && !stop) {
          val += ` font-size:${v.size}px; `;
          stop = true;
        }
      }
    });
  } else val += ` font-size:${tablesetting.find((v) => v.code === "table-font-size").detail}px;`;
  if (field.textalign) val += ` text-align:${field.textalign}; `;
  if (field.minwidth) val += ` min-width:${field.minwidth}px; `;
  if (field.maxwidth) val += ` max-width:${field.maxwidth}px; `;
  return val;
};
const getSettingStyle = function (name, field) {
  if (!tablesetting || tablesetting.length === 0) {
    // manual temp fix
    tablesetting = [
      {
        id: 4,
        code: "per-page",
        name: "Số dòng trong 1 trang",
        detail: "20",
      },
      {
        id: 8,
        code: "header-filter-color",
        name: "Màu chữ khi áp dụng Filter",
        detail: "#00cc66",
      },
      {
        id: 10,
        code: "background",
        name: "Màu nền background",
        detail: "#363636",
      },
      {
        id: 11,
        code: "table-background",
        name: "Màu nền của bảng",
        detail: "#ffffff",
      },
      {
        id: 12,
        code: "table-font-color",
        name: "Mầu chữ trong bảng",
        detail: "hsl(0, 0%, 14%)",
      },
      {
        id: 13,
        code: "table-font-size",
        name: "Cỡ chữ trong bảng",
        detail: "12",
      },
      {
        id: 14,
        code: "header-background",
        name: "Màu nền tiêu đề",
        detail: "var(--bulma-primary-soft)",
      },
      {
        id: 15,
        code: "header-font-color",
        name: "Màu chữ tiêu đề",
        detail: "var(--bulma-primary-40)",
      },
      {
        id: 16,
        code: "header-font-size",
        name: "Cỡ chữ tiêu đề",
        detail: "12",
      },
      {
        id: 17,
        code: "container-height",
        name: "Chiều cao container",
        detail: "38",
      },
      {
        id: 18,
        code: "header-arrow",
        name: "Mũi tên trỏ xuống",
        detail: "no",
      },
      {
        id: 19,
        code: "menu-width",
        name: "Chiều rộng menu",
        detail: "20.6",
      },
      {
        id: 20,
        code: "menu-min-height",
        name: "Chiều cao menu (nhỏ nhất)",
        detail: "32",
      },
      {
        id: 21,
        code: "menu-max-height",
        name: "Chiều cao menu (lớn nhất)",
        detail: "37",
      },
      {
        id: 22,
        code: "show-menu",
        name: "Hiển thị menu",
        detail: "yes",
      },
      {
        id: 23,
        code: "note",
        name: "Ghi chú",
        detail: "@",
      },
      {
        id: 24,
        code: "td-border",
        name: "Đường viền",
        detail: "border: 1px solid #dbdbdb;",
      },
    ];
  }

  let value = "";
  if (name === "container") {
    value = "min-height:" + tablesetting.find((v) => v.code === "container-height").detail + "rem; ";
  } else if (name === "table") {
    value += "background-color:" + tablesetting.find((v) => v.code === "table-background").detail + "; ";
    value += "font-size:" + tablesetting.find((v) => v.code === "table-font-size").detail + "px;";
    value += "color:" + tablesetting.find((v) => v.code === "table-font-color").detail + "; ";
  } else if (name === "header") {
    value += "background-color:" + tablesetting.find((v) => v.code === "header-background").detail + "; ";
    if (field.minwidth) value += " min-width: " + field.minwidth + "px; ";
    if (field.maxwidth) value += " max-width: " + field.maxwidth + "px; ";
  } else if (name === "menu") {
    let arg = tablesetting.find((v) => v.code === "menu-width").detail;
    arg = field ? (field.menuwidth ? field.menuwidth : arg) : arg;
    value += "width:" + arg + "rem; ";
    value += "min-height:" + tablesetting.find((v) => v.code === "menu-min-height").detail + "rem; ";
    value += "max-height:" + tablesetting.find((v) => v.code === "menu-max-height").detail + "rem; ";
    value += "overflow:auto; ";
  } else if (name === "dropdown") {
    value += "font-size:" + tablesetting.find((v) => v.code === "header-font-size").detail + "px; ";
    let found = filters.find((v) => v.name === field.name);
    found
      ? (value += "color:" + tablesetting.find((v) => v.code === "header-filter-color").detail + "; ")
      : (value += "color:" + tablesetting.find((v) => v.code === "header-font-color").detail + "; ");
  }
  return value;
};
function changePage(page) {
  currentPage = page;
  updateShow();
}
const showPagination = function () {
  showPaging.value = pagedata.pagination === false ? false : true;
  totalRows.value = data.length;
  if (showPaging.value && pagedata.api) {
    if (pagedata.api.full_data === false) totalRows.value = pagedata.api.total_rows;
    showPaging.value = totalRows.value > perPage;
  }
};
const close = function () {
  showmodal.value = undefined;
};
const frontendFilter = function (newVal) {
  let checkValid = function (name, x, filter) {
    if ($empty(x[name])) return false;
    else {
      let text = "";
      filter.map((y, k) => {
        text += `${k > 0 ? (filter[k - 1].operator === "and" ? " &&" : " ||") : ""} ${$formatNumber(x[name])}
              ${y.condition === "=" ? "==" : y.condition === "<>" ? "!==" : y.condition} ${$formatNumber(y.value)}`;
      });
      return $calc(text);
    }
  };
  newVal = $copy(newVal);
  var data = $copy(pagedata.data);
  newVal
    .filter((m) => m.select || m.filter)
    .map((v) => {
      if (v.select) {
        data = data.filter((x) => v.select.findIndex((y) => ($empty(y) ? $empty(x[v.name]) : y === x[v.name])) > -1);
      } else if (v.filter) {
        data = data.filter((x) => checkValid(v.name, x, v.filter));
      }
    });
  let sort = {};
  let format = {};
  let list = filters.filter((x) => x.sort);
  list.map((v) => {
    sort[v.name] = v.sort === "az" ? "asc" : "desc";
    format[v.name] = v.format;
  });
  return list.length > 0 ? $multiSort(data, sort, format) : data;
};
const backendFilter = function (newVal) {};
const doFilter = function (newVal, nonset) {
  if (currentPage > 1 && nonset !== true) currentPage = 1;
  if (pagedata.api.full_data) {
    data = frontendFilter(newVal);
    pagedata.dataFilter = $copy(data);
    store.commit(props.pagename, pagedata);
    emit("changedata", newVal);
  } else {
    if (timer) clearTimeout(timer);
    timer = setTimeout(() => backendFilter(newVal), 200);
  }
  pagedata.filters = newVal;
  store.commit(props.pagename, pagedata);
  emit("changefilter", newVal ? newVal.length > 0 : false);
};
const doSelect = function (value) {
  showmodal.value = undefined;
  let field = currentField;
  let found = filters.find((v) => v.name === field.name);
  if (found) {
    !found.select ? (found.select = []) : false;
    let idx = found.select.findIndex((x) => x === value);
    idx >= 0 ? $remove(found.select, idx) : found.select.push(value);
    if (found.select.length === 0) {
      idx = filters.findIndex((v) => v.name === field.name);
      if (idx >= 0) $remove(filters, idx);
    }
  } else {
    filters.push({
      name: field.name,
      label: field.label,
      select: [value],
      format: field.format,
    });
  }
  doFilter(filters);
  updateShow();
};
const doubleScroll = function (element) {
  var _scrollbar = document.createElement("div");
  _scrollbar.appendChild(document.createElement("div"));
  _scrollbar.style.overflow = "auto";
  _scrollbar.style.overflowY = "hidden";
  _scrollbar.firstChild.style.width = element.scrollWidth + "px";
  _scrollbar.firstChild.style.height = "1px";
  _scrollbar.firstChild.appendChild(document.createTextNode("\xA0"));
  var running = false;
  _scrollbar.onscroll = function () {
    if (running) {
      running = false;
      return;
    }
    running = true;
    element.scrollLeft = _scrollbar.scrollLeft;
  };
  element.onscroll = function () {
    if (running) {
      running = false;
      return;
    }
    running = true;
    _scrollbar.scrollLeft = element.scrollLeft;
  };
  element.parentNode.insertBefore(scrollbar, element);
  _scrollbar.scrollLeft = element.scrollLeft;
  scrollbar = _scrollbar;
};
const removeFilter = function (i) {
  $remove(filters, i);
  doFilter(filters);
  updateShow();
};
const scrollbarVisible = function () {
  let element = this.$refs["container"];
  if (!element) return;
  let result = element.scrollWidth > element.clientWidth ? true : false;
  if (scrollbar) {
    element.parentNode.removeChild(scrollbar);
    scrollbar = undefined;
  }
  if (result) doubleScroll(element);
};
const updateData = async function (newVal) {
  if (newVal.columns) {
    //change attribute
    fields = $copy(newVal.columns);
    let _fields = fields.filter((v) => v.show);
    data.map((v) => {
      _fields.map((x) => (v[`${x.name}color`] = getStyle(x, v)));
    });
    return updateShow();
  }
  if (newVal.tablesetting) {
    tablesetting = newVal.tablesetting;
    perPage = $formatNumber(tablesetting.find((v) => v.code == "per-page").detail);
    currentPage = 1;
  }
  tablesetting = $copy(pagedata.tablesetting || gridsetting);
  if (tablesetting) {
    perPage = pagedata.perPage ? pagedata.perPage : Number(tablesetting.find((v) => v.code === "per-page").detail);
  }
  if (newVal.fields) {
    fields = $copy(newVal.fields);
  } else fields = $copy(pagedata.fields);
  if (newVal.data || newVal.fields) {
    let copy = $copy(newVal.data || data);
    this.data = $calculateData(copy, fields);
    let fields = fields.filter((v) => v.show);
    data.map((v) => {
      fields.map((x) => (v[`${x.name}color`] = getStyle(x, v)));
    });
  }
  if (newVal.filters) filters = $copy(newVal.filters);
  else if (pagedata.filters) filters = $copy(pagedata.filters);
  if (newVal.data || newVal.fields || newVal.filters) {
    let copy = $copy(filters);
    filters.map((v, i) => {
      let idx = $findIndex(fields, { name: v.name });
      let index = $findIndex(copy, { name: v.name });
      if (idx < 0 && index >= 0) $delete(copy, index);
      else if (idx >= 0 && index >= 0) copy[index].label = fields[idx].label;
    });
    filters = copy;
    doFilter(filters);
  }
  if (newVal.data || newVal.fields || newVal.filters || newVal.tablesetting) updateShow();
  if (newVal.data || newVal.fields) setTimeout(() => scrollbarVisible(), 100);
  if (newVal.highlight) setTimeout(() => highlight(newVal.highlight), 50);
};
const doubleClick = function (field, v) {
  currentField = field;
  doSelect(v[field.name]);
};
const tableStyle = getSettingStyle("table");
setTimeout(() => updateShow(), 200);
</script>
<style scoped>
:deep(.table tbody tr:hover td, .table tbody tr:hover th) {
  --bulma-table-row-hover-background-color: var(--bulma-scheme-main-ter);
  background-color: var(--bulma-table-row-hover-background-color);
}
.header:hover a {
  opacity: 0.75;
}
</style>
