<template>
  <div>
    <gz-error :error-box-message="formState.errorBoxMessage"></gz-error>

    <gz-data-table-filter
      :data-list-key="dataListKey"
      :active-filter-id="activeFilterId"
      ref="dataTableFilter"
    >
    </gz-data-table-filter>
    <gz-data-table-filter-manager
      :data-list-key="dataListKey"
      :active-filter-id="activeFilterId"
      ref="dataTableFilterManager"
    >
    </gz-data-table-filter-manager>
    <gz-data-table-mobile-filter-column-selector
      :headers="headers"
      ref="dataTableMobileFilterColumnSelector"
    >
    </gz-data-table-mobile-filter-column-selector>
    <v-card>
      <v-card-title>
        <template v-if="preFilterMode">
          <div class="my-2">
            <v-btn text @click="preFilterNav()">
              <v-icon data-cy="clickThru">{{ preFilterMode.icon }}</v-icon>
            </v-btn>
            <span @click="preFilterNav()" class="text-h5">
              {{ preFilterMode.viz }}</span
            >
            <v-btn
              v-if="preFilterMode.clearable"
              text
              @click="preFilterClear()"
            >
              <v-icon>$clear</v-icon>
            </v-btn>
          </div>

          <!-- v-if="preFilterMode.ayatype && preFilterMode.id" {icon:null,viz:null,ayatype:null,id:null,clearable:false} -->
        </template>
        <template v-else>
          <v-select
            v-model="activeFilterId"
            :items="selectLists.savedFilters"
            item-text="name"
            item-value="id"
            :label="$ay.t('Filter')"
            @input="selectedFilterChanged"
            prepend-icon="$ayiEdit"
            @click:prepend="editFilter()"
            :append-outer-icon="clearFilterIcon()"
            @click:append-outer="clearFilter()"
            data-cy="selectSavedFilter"
          >
          </v-select>
        </template>

        <v-spacer></v-spacer>
        <div class="my-5 my-sm-1">
          <v-btn text @click="refresh">
            <v-icon data-cy="refresh">$ayiSync</v-icon>
          </v-btn>

          <v-btn
            text
            v-if="$vuetify.breakpoint.xs"
            class="ml-12"
            @click="mobileColumnFilterSelect"
          >
            <v-icon data-cy="refresh">$ayiFilter</v-icon>
          </v-btn>
          <v-btn text class="ml-12" @click="editColumnView">
            <v-icon
              :large="hiddenAffectiveColumns.length > 0"
              :color="hiddenAffectiveColumns.length ? 'accent' : null"
              data-cy="filter"
              >$ayiColumns</v-icon
            >
          </v-btn>
        </div>
      </v-card-title>
      <div
        class="headline ml-3 accent--text"
        v-if="!loading && records.length < 1"
      >
        {{ $ay.t("NoData") }}
      </div>

      <!-- 
        ################################################################################################################################################
        WIDE TABLE VIEW 
        ################################################################################################################################################
        -->
      <template v-if="$vuetify.breakpoint.smAndUp">
        <v-data-table
          :headers="headers"
          :items="records"
          v-model="selected"
          :options.sync="dataTablePagingOptions"
          :server-items-length="totalRecords"
          :loading="loading"
          multi-sort
          :show-select="showSelect"
          :single-select="singleSelect"
          :footer-props="{
            showCurrentPage: true,
            showFirstLastPage: true,
            itemsPerPageOptions: rowsPerPageItems,
            itemsPerPageText: $ay.t('RowsPerPage'),
            pageText: $ay.t('PageOfPageText')
          }"
          :loading-text="$ay.t('Loading')"
          :no-data-text="$ay.t('NoData')"
          class="elevation-1"
          data-cy="datatable"
        >
          <!-- HEADER FILTERS: https://stackoverflow.com/a/58718975/8939 -->
          <template v-for="h in headers" v-slot:[`header.${h.value}`]>
            <span :key="h.text">
              <v-btn
                v-if="h.flt"
                icon
                @click.stop="filter(h)"
                class="ml-n8 mr-1"
                ><v-icon :color="filterColor(h)">$ayiFilter</v-icon></v-btn
              >
              {{ h.text }}
            </span>
          </template>

          <template v-slot:body="{ items }">
            <tbody>
              <tr v-for="item in items" :key="item.id">
                <template v-if="showSelect">
                  <td>
                    <v-checkbox
                      v-model="selected"
                      :value="item"
                      hide-details
                    ></v-checkbox>
                  </td>
                </template>
                <td
                  v-for="c in item.columns"
                  :key="c.key"
                  :style="cellStyle(c)"
                >
                  <template v-if="c.t == 1">
                    <!-- DATETIME -->
                    {{ c.v }}
                  </template>
                  <template v-else-if="c.t == 2">
                    <!-- DATE -->
                    {{ c.v }}
                  </template>
                  <template v-else-if="c.t == 3">
                    <!-- TIME -->
                    {{ c.v }}
                  </template>
                  <template v-else-if="c.t == 4">
                    <!-- TEXT (also maybe openable)-->
                    <template v-if="c.i && c.i != 0 && !c.nopen">
                      <!-- openable object with an ID -->
                      <div
                        class="subtitle-1"
                        @click="gridCellButtonClick(c.key, c.i, c.ot)"
                      >
                        <a href="javascript:"> {{ c.v }}</a>
                      </div>
                    </template>
                    <template v-else>
                      {{ c.v }}
                    </template>
                  </template>
                  <template v-else-if="c.t == 5">
                    <!-- INTEGER -->
                    <template v-if="c.i && c.i != 0 && !c.nopen">
                      <!-- openable object with an ID -->
                      <div
                        class="subtitle-1"
                        @click="gridCellButtonClick(c.key, c.i, c.ot)"
                      >
                        <a href="javascript:"> {{ c.v }}</a>
                      </div>
                    </template>
                    <template v-else>
                      {{ c.v }}
                    </template>
                  </template>
                  <template v-else-if="c.t == 6">
                    <!-- BOOL -->

                    <v-icon v-if="c.v === false" small>$ayiSquare</v-icon>
                    <v-icon v-else-if="c.v === true" small
                      >$ayiCheckSquare</v-icon
                    >
                    <v-icon v-else small>$ayiMinus-square</v-icon>
                  </template>
                  <template v-else-if="c.t == 7">
                    <!-- DECIMAL -->
                    {{ c.v }}
                  </template>
                  <template v-else-if="c.t == 8">
                    <!-- CURRENCY -->
                    {{ c.v }}
                  </template>
                  <template v-else-if="c.t == 9">
                    <!-- TAGS -->
                    {{ c.v }}
                  </template>
                  <template v-else-if="c.t == 10">
                    <!-- ENUM (translated to text on getdata) ALSO MAYBE OPENABLE -->
                    <template v-if="c.i && c.ot">
                      <!-- openable object with an ID -->
                      <div
                        class="subtitle-1"
                        @click="gridCellButtonClick(c.key, c.i, c.ot)"
                      >
                        <a href="javascript:"> {{ c.v }}</a>
                      </div>
                    </template>
                    <template v-else>
                      {{ c.v }}
                    </template>
                  </template>
                  <template v-else-if="c.t == 11">
                    <!-- EMAIL -->
                    <a :href="'mailto:' + c.v">{{ c.v }}</a>
                  </template>
                  <template v-else-if="c.t == 12">
                    <!-- URL / HTTP -->
                    <!-- Expects full url with protocol etc in c.v so might need to add to record builder -->
                    <a :href="c.v" target="_blank">{{ c.v }}</a>
                  </template>
                  <template v-else-if="c.t == 14">
                    <!-- File / memory Size -->
                    {{ c.v }}
                  </template>
                  <template v-else-if="c.t == 16">
                    <!-- PHONE NUMBER -->
                    <a :href="'tel:' + c.v">{{ c.v }}</a>
                  </template>
                  <template v-else>
                    <!-- UNKNOWN -->
                    {{ c.v }}
                  </template>
                </td>
              </tr>
            </tbody>
          </template>
        </v-data-table>
      </template>
      <!-- 
        ########################################################################
        MOBILE TABLE VIEW 
        ########################################################################
        -->
      <template v-else>
        <v-data-table
          :headers="headers"
          :items="records"
          v-model="selected"
          :options.sync="dataTablePagingOptions"
          :server-items-length="totalRecords"
          :loading="loading"
          multi-sort
          :show-select="showSelect"
          :single-select="singleSelect"
          :footer-props="{
            showCurrentPage: true,
            showFirstLastPage: true,
            itemsPerPageOptions: rowsPerPageItems,
            itemsPerPageText: $ay.t('RowsPerPage'),
            pageText: $ay.t('PageOfPageText')
          }"
          :header-props="{ sortByText: $ay.t('Sort') }"
          :loading-text="$ay.t('Loading')"
          class="elevation-1"
          data-cy="datatable"
          :no-data-text="$ay.t('NoData')"
        >
          <template v-slot:body="{ items }">
            <v-row>
              <v-col
                v-for="item in items"
                :key="item.id"
                cols="12"
                sm="6"
                md="4"
                lg="3"
              >
                <v-card elevation="4" tile>
                  <template v-if="showSelect">
                    <v-card-title class="subheading font-weight-bold">
                      <v-checkbox
                        v-model="selected"
                        :value="item"
                        hide-details
                      ></v-checkbox>
                    </v-card-title>
                    <v-divider></v-divider>
                  </template>
                  <v-list dense>
                    <v-list-item
                      two-line
                      v-for="c in item.columns"
                      :key="c.key"
                    >
                      <v-list-item-content>
                        <v-list-item-title>
                          {{ getHeaderText(c.key) }}</v-list-item-title
                        >
                        <v-list-item-subtitle :style="cellStyle(c)">
                          <template v-if="c.t == 1">
                            <!-- DATETIME -->
                            {{ c.v }}
                          </template>
                          <template v-else-if="c.t == 2">
                            <!-- DATE -->
                            {{ c.v }}
                          </template>
                          <template v-else-if="c.t == 3">
                            <!-- TIME -->
                            {{ c.v }}
                          </template>
                          <template v-else-if="c.t == 4">
                            <!-- TEXT (also maybe openable)-->
                            <template v-if="c.i && c.i != 0 && !c.nopen">
                              <!-- openable object with an ID -->
                              <div
                                class="subtitle-1"
                                @click="gridCellButtonClick(c.key, c.i, c.ot)"
                              >
                                <a href="javascript:"> {{ c.v }}</a>
                              </div>
                            </template>
                            <template v-else>
                              {{ c.v }}
                            </template>
                          </template>
                          <template v-else-if="c.t == 5">
                            <!-- INTEGER -->
                            <template v-if="c.i && c.i != 0 && !c.nopen">
                              <!-- openable object with an ID -->
                              <div
                                class="subtitle-1"
                                @click="gridCellButtonClick(c.key, c.i, c.ot)"
                              >
                                <a href="javascript:"> {{ c.v }}</a>
                              </div>
                            </template>
                            <template v-else>
                              {{ c.v }}
                            </template>
                          </template>
                          <template v-else-if="c.t == 6">
                            <!-- BOOL -->
                            <div>
                              <v-icon v-if="c.v === false" small
                                >$ayiSquare</v-icon
                              >
                              <v-icon v-else-if="c.v === true" small
                                >$ayiCheckSquare</v-icon
                              >
                              <v-icon v-else small>$ayiMinus-square</v-icon>
                            </div>
                          </template>
                          <template v-else-if="c.t == 7">
                            <!-- DECIMAL -->
                            {{ c.v }}
                          </template>
                          <template v-else-if="c.t == 8">
                            <!-- CURRENCY -->
                            <div>{{ c.v }}</div>
                          </template>
                          <template v-else-if="c.t == 9">
                            <!-- TAGS -->
                            {{ c.v }}
                          </template>
                          <template v-else-if="c.t == 10">
                            <!-- ENUM (translated to text on getdata) ALSO MAYBE OPENABLE -->
                            <template v-if="c.i && c.ot">
                              <!-- openable object with an ID -->
                              <div
                                class="subtitle-1"
                                @click="gridCellButtonClick(c.key, c.i, c.ot)"
                              >
                                <a href="javascript:"> {{ c.v }}</a>
                              </div>
                            </template>
                            <template v-else>
                              {{ c.v }}
                            </template>
                          </template>
                          <template v-else-if="c.t == 11">
                            <!-- EMAIL -->
                            <a :href="'mailto:' + c.v">{{ c.v }}</a>
                          </template>
                          <template v-else-if="c.t == 12">
                            <!-- URL / HTTP -->
                            <!-- Expects full url with protocol etc in c.v so might need to add to record builder -->
                            <a :href="c.v" target="_blank">{{ c.v }}</a>
                          </template>
                          <template v-else-if="c.t == 14">
                            <!-- File / memory Size -->
                            {{ c.v }}
                          </template>
                          <template v-else-if="c.t == 16">
                            <!-- PHONE NUMBER -->
                            <a :href="'tel:' + c.v">{{ c.v }}</a>
                          </template>
                          <template v-else>
                            <!-- UNKNOWN -->
                            {{ c.v }}
                          </template>
                        </v-list-item-subtitle>
                      </v-list-item-content>
                    </v-list-item>
                  </v-list>
                </v-card>
              </v-col>
            </v-row>
          </template>
        </v-data-table>
      </template>
    </v-card>
  </div>
</template>

<script>
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
/*  Xeslint-disable    */
////////////////////////////////////////////////////////////////////////////////////////////////////////////

const MAX_TEXT_COLUMN_LENGTH = 1024;
export default {
  data() {
    return {
      loading: true,
      dataTablePagingOptions: {},
      lastDataTablePagingOptions: {},
      activeFilterId: 0, //<--0 signifies to select default as it's uninitialized
      lastFetchFilterId: 0, //used to track change of filter and reset paging in getdata
      selectLists: {
        savedFilters: []
      },
      headers: [],
      serverColumns: [],
      serverFilter: [],
      hiddenAffectiveColumns: [],
      totalRecords: 0,
      records: [],
      rowsPerPageItems: [5, 10, 25, 50, 100],
      selected: [],
      timeZoneName: null,
      formState: {
        ready: false,
        dirty: false,
        valid: true,
        readOnly: false,
        loading: true,
        errorBoxMessage: undefined,
        appError: undefined,
        serverError: {}
      }
    };
  },
  props: {
    formKey: { type: String, default: null },
    dataListKey: { type: String, default: null },
    clientCriteria: {
      type: String,
      default: undefined
    },
    preFilterMode: {
      //{icon:null,viz:null,ayatype:null,id:null,clearable:false}
      type: Object,
      default: null
    },
    showSelect: {
      type: Boolean,
      default: false
    },
    singleSelect: {
      type: Boolean,
      default: false
    },
    reload: {
      type: Boolean,
      default: false
    },
    ridColumnOpenable: {
      type: Boolean,
      default: true
    }
  },
  watch: {
    dataTablePagingOptions: {
      async handler() {
        //{ "page": 1, "itemsPerPage": 10, "sortBy": [], "sortDesc": [], "groupBy": [], "groupDesc": [], "mustSort": false, "multiSort": false }
        //this code works around some weird bug that causes visible items to be selected in grid (only, not in actual selected array, just a visual thing)
        // when breakpoint is switched between wide and narrow either way.  No idea why it happens but this fixes that issue and also ensures that there are no

        //spurious fetches happening just because the view has changed
        //See what has changed and record it for processing
        let sortHasChanged = !(
          window.$gz.util.isEqualArraysOfPrimitives(
            this.dataTablePagingOptions.sortBy,
            this.lastDataTablePagingOptions.sortBy
          ) &&
          window.$gz.util.isEqualArraysOfPrimitives(
            this.dataTablePagingOptions.sortDesc,
            this.lastDataTablePagingOptions.sortDesc
          )
        );

        let pagingHaschanged = !(
          this.lastDataTablePagingOptions.page ==
            this.dataTablePagingOptions.page &&
          this.lastDataTablePagingOptions.itemsPerPage ==
            this.dataTablePagingOptions.itemsPerPage
        );

        if (!pagingHaschanged && !sortHasChanged) {
          return;
        }

        //something changed, persist it to last
        this.lastDataTablePagingOptions = this.dataTablePagingOptions;

        if (sortHasChanged && !this.loading) {
          //update sort at server and then allow get data

          //no return data is required here
          //because getdatafromapi will have the data to refresh the sort of the view anyway
          //so it's fire and forget
          await window.$gz.api.post("data-list-column-view/sort", {
            listKey: this.dataListKey,
            sortBy: this.keyArrayFromSortByArray(
              this.dataTablePagingOptions.sortBy
            ),
            sortDesc: this.dataTablePagingOptions.sortDesc
          });
        }

        //has changed something important so refetch
        await this.getDataFromApi();
      },
      deep: true
    },
    selected: function(newValue, oldValue) {
      if (!newValue) {
        return;
      }
      if (newValue.length != oldValue.length) {
        this.handleSelectChange();
      }
    },
    reload: function(newValue, oldValue) {
      if (newValue != oldValue) {
        this.getDataFromApi(true);
      }
    }
  },
  methods: {
    preFilterNav: function() {
      window.$gz.eventBus.$emit("openobject", {
        type: this.preFilterMode.ayatype,
        id: this.preFilterMode.id
      });
    },
    preFilterClear: function() {
      this.$emit("clear-pre-filter");
      // this.preFilterMode = null;
      // this.clientCriteria = null;
      // getDataFromApi();
    },
    //Used by narrow view to get the "header" text for a column based on the column key
    getHeaderText(key) {
      //key format is row-column e.g."500-2"
      let columnIndex = key.split("-")[1];
      let header = this.headers[columnIndex];
      if (header && header.text) {
        return header.text;
      }
      return "";
    },
    async filter(item) {
      let res = await this.$refs.dataTableFilter.open(item);
      if (res && res.refresh == true) {
        this.getDataFromApi();
      }
    },
    filterColor(item) {
      let clr = "disabled";
      if (this.serverFilter.findIndex(z => z.column == item.fk) > -1) {
        clr = "primary";
      }
      return clr;
    },
    async clearFilter() {
      //Reset back to DEFAULT filter
      setActiveFilter(this); //will not trigger refresh yet

      //Call api method to clear this filter
      let res = await window.$gz.api.remove(
        `data-list-filter/${this.activeFilterId}`
      );
      if (res.error) {
        vm.formState.serverError = res.error;
        window.$gz.form.setErrorBoxErrors(vm);
      } else {
        await fetchSavedFilterList(this);
        await this.getDataFromApi();
      }
    },
    clearFilterIcon() {
      if (this.serverFilter && this.serverFilter.length > 0) {
        return "$clear";
      }
      return null;
    },
    async editFilter() {
      let res = await this.$refs.dataTableFilterManager.open();
      if (res && res.refresh == true) {
        if (res.newFilterId) {
          //save as new filter, select it
          this.activeFilterId = res.newFilterId;
        }
        //refresh the filter picklist and get data from api
        await fetchSavedFilterList(this);
        await this.getDataFromApi();
      }
    },
    async mobileColumnFilterSelect() {
      let resHeaderItem = await this.$refs.dataTableMobileFilterColumnSelector.open(
        this.headers,
        this.filterColor
      );
      if (resHeaderItem) {
        await this.filter(resHeaderItem);
      }
    },
    keyArrayFromSortByArray(sortBy) {
      return sortBy.map(sortItem => {
        let val = this.headers.find(z => z.value == sortItem);
        if (val) {
          return val.fk;
        }
      });
    },
    setSortIndicatorsFromDataListResponse(rsort) {
      let sortBy = [];
      let sortDesc = [];
      if (rsort != null) {
        Object.keys(rsort).forEach((key, index) => {
          //Pull column header name "value" from "fk"matching "key" here  from this.headers columns.c0 etc here from this.headers see above method
          let found = this.headers.find(z => z.fk == key);
          if (found != null) {
            sortBy.push(found.value);

            //if not null then push into the sortBy array
            let sort = rsort[key];
            if (sort == "-") {
              sortDesc.push(true);
            } else {
              sortDesc.push(false);
            }
          }
        });
      }

      this.dataTablePagingOptions.sortBy = [...sortBy];
      this.dataTablePagingOptions.sortDesc = [...sortDesc];
    },
    refresh() {
      this.getDataFromApi();
    },
    getDataListSelection(ayaType) {
      let vm = this;
      //called when parent form needs the selected id's or the list view options needed to rehydrate the entire list of id's in the same order and filter
      //i.e. for reporting, batch operations etc

      let selectedRowIds = [];
      //get selected row id's if there are selections else get all row id's
      //called into from parent for things like reporting or batch ops
      //selected or records both are arrays with an id property that contains the row id conveniently for this method
      if (vm.selected.length > 0) {
        //return row id's of selected items only
        selectedRowIds = vm.selected.map(z => {
          return z.id;
        });
        selectedRowIds.reverse();
      }
      return {
        AType: ayaType,
        selectedRowIds: selectedRowIds,
        dataListKey: vm.dataListKey,
        filterId: vm.activeFilterId,
        clientCriteria: vm.clientCriteria,
        clientTimeStamp: window.$gz.locale.clientLocalZoneTimeStamp(
          vm.timeZoneName
        )
      };
    },
    handleSelectChange() {
      //SINGLESELECT MODE?
      //due to making own template for items need to handle singleselect which only affects if select all checkbox at top is visible when making own item template
      if (this.singleSelect) {
        this.selected.splice(0, this.selected.length - 1);
      }
      //emit event to parent form of selected rows

      this.$emit(
        "selection-change",
        this.selected.map(z => z.id)
      );
    },
    editColumnView() {
      this.$router.push({
        name: "ay-data-list-column-view",
        params: {
          dataListKey: this.dataListKey,
          hiddenAffectiveColumns: this.hiddenAffectiveColumns
          // ,
          // formKey: this.formKey,
          // activeFilterId: this.activeFilterId
        }
      });
    },
    selectedFilterChanged: async function() {
      await this.getDataFromApi();
    },
    gridCellButtonClick(key, i, ot) {
      let typeToOpen = null;
      //if ot is not null or undefined then *that* is the Openable Type otherwise it's in the header data
      if (ot) {
        typeToOpen = ot;
      } else {
        //translate key to actual object type from header data
        //key format is row-column e.g."500-2"
        //get the datatype of the column which matches the server columns array index
        typeToOpen = this.serverColumns[key.split("-")[1]].ay;
      }
      //i is the actual AyaNova index of vm record so we have all we need to open vm object
      window.$gz.eventBus.$emit("openobject", { type: typeToOpen, id: i });
    },
    cellStyle(c) {
      //does it have additional styling?
      //c.cst is styling added here by data builder in the form of an object with props set for compatibility with Vue
      //c.clr is a specific color coming from the server for items that support colors (reminders etc)
      if (c.cst || c.clr) {
        let clrBit = {};
        if (c.clr) {
          if (c.clr[0] != "#") {
            c.clr = "#" + c.clr;
          }
          clrBit["border-left"] = `5px solid ${c.clr}`; //warning: don't put a semicolon after any of these styles, it will then ignore the whole bit
        }
        return { ...c.cst, ...clrBit };
      } else {
        return null;
      }
    },
    async getDataFromApi(deSelectAll) {
      let vm = this;

      if (vm.loading) {
        return;
      }
      //start with defaults
      let listOptions = {
        DataListKey: vm.dataListKey,
        Limit: 5,
        Offset: 0
      };

      //has filter changed?
      if (vm.lastFetchFilterId != vm.activeFilterId) {
        //yes, go back to page one
        vm.dataTablePagingOptions.page = 1;
        vm.lastFetchFilterId = vm.activeFilterId;
      }

      //calculate paging based on settings
      const { page, itemsPerPage } = vm.dataTablePagingOptions;
      if (itemsPerPage && itemsPerPage > 0) {
        listOptions.Offset = (page - 1) * itemsPerPage;
        listOptions.Limit = itemsPerPage;
      }

      vm.loading = true;
      //Weird bug that causes grid to show all items selected after async method runs below to fetch data
      //this puts a pin in it then resets it after the fetch is completed
      let preSelected = [];
      if (!deSelectAll) {
        //preserve selections
        preSelected = [...vm.selected];
      }

      try {
        let res = await window.$gz.api.post("data-list", {
          offset: listOptions.Offset,
          limit: listOptions.Limit,
          dataListKey: vm.dataListKey,
          filterId: vm.activeFilterId,
          clientCriteria: vm.clientCriteria,
          clientTimeStamp: window.$gz.locale.clientLocalZoneTimeStamp(
            vm.timeZoneName
          )
        });

        if (res.error) {
          vm.formState.serverError = res.error;
          window.$gz.form.setErrorBoxErrors(vm);
        } else {
          //Save a copy of the server columns data for handling button clicks etc later
          vm.serverColumns = res.columns;
          //save a copy of the filter to show the filter symbol icons beside column headers
          //and know that the return list *is* filtered
          vm.serverFilter = res.filter;

          //Hidden columns that affect the query and data displayed
          vm.hiddenAffectiveColumns = res.hiddenAffectiveColumns;

          //Make sure the translation keys are fetched
          await fetchTranslatedHeaderNames(res.columns); //Note can use await here because it's wrapped inside an async function call, it will wait then resume next stuff below

          await fetchEnums(res.columns);

          //build vm.headers here
          vm.headers = buildHeaders(res.columns);

          //SET TABLE SORT HERE FROM RESPONSE
          vm.setSortIndicatorsFromDataListResponse(res.sortBy);

          //Post process data here and then set vm.records
          vm.records = buildRecords(
            res.data,
            res.columns,
            vm.ridColumnOpenable
          );

          vm.totalRecords = res.totalRecordCount;
          //Put back selected items
          vm.selected = [...preSelected];

          //persist the paging options and filter stuff so user sees same page and list on refresh or leave and return scenario
          saveFormSettings(vm);
        }
      } catch (err) {
        window.$gz.errorHandler.handleFormError(err, vm);
      } finally {
        //Note: this needs to be called in NextTick because otherwise it triggers a re-fetch when the sort indicators were set above
        vm.$nextTick(() => {
          vm.loading = false;
          vm.formState.ready = true;
        });
      }
    }
  },
  async created() {
    //get pick lists
    let vm = this;
    await initForm(vm);
    //rehydrate last form settings
    //loadFormSettings(vm);
    vm.loading = false;
    vm.getDataFromApi();
  }
};

//Called by getDataFromApi on retrieval of list with columnData
function buildHeaders(columnData) {
  //iterate columns, build headers and return
  if (!columnData) {
    return [];
  }
  let ret = [];
  //iterate the columns
  for (let i = 0; i < columnData.length; i++) {
    let cm = columnData[i];
    let h = {};
    h.text = window.$gz.translation.get(cm.cm);
    h.fk = cm.fk;
    h.value = "columns.c" + i.toString(); //+".v";
    if (i == 0) {
      h.align = "start";
    }
    //sort and filter, note server only sends a value if a column is *not* filterable or sortable to save bandwidth since most are
    //this just reverses that so that it's easier to check for
    //Not filterable?
    if (!cm.nf) {
      //yes, is filterable
      h.flt = true;
    }
    //Sortable?
    if (cm.ns) {
      h.sortable = false;
    }
    ret.push(h);
  }

  return ret;
}

/////////////////////////////////////////////////////////////////
//Called by getDataFromApi on retrieval of list with columnData
//
function buildRecords(listData, columndefinitions, ridColumnOpenable) {
  //iterate data, build each object keyed with index name and display set to correct translated filter and then return

  let ret = [];
  if (!listData) {
    return ret;
  }

  //cache display format stuff
  let timeZoneName = window.$gz.locale.getResolvedTimeZoneName();
  let languageName = window.$gz.locale.getResolvedLanguage();
  let hour12 = window.$gz.store.state.userOptions.hour12;
  let currencyName = window.$gz.locale.getCurrencyName();

  //this will cache the first time it's required (if required)
  let availableRoles = null;

  //comes as an array of arrays, needs to leave as an array of objects representing each row
  for (let iRow = 0; iRow < listData.length; iRow++) {
    let row = listData[iRow];
    //iterate row and build object representing row data keyed to index

    //container object for row
    //id will be set later when code below encounters the id column which could be in any position (or not at all) but is identified by it's rid property
    let o = { id: undefined, columns: {} };

    for (let iColumn = 0; iColumn < row.length; iColumn++) {
      let column = row[iColumn];

      //rowId?
      if (column.rid) {
        o.id = column.i;
      }

      let dataType = columndefinitions[iColumn].dt;
      let display = column.v;
      let cstStyle = null; //custom additional styling here in grid e.g. negative numbers etc

      /*
         public enum UiFieldDataType : int
    {
        NoType = 0,
        DateTime = 1,
        Date = 2,
        Time = 3,
        Text = 4,
        Integer = 5,
        Bool = 6,
        Decimal = 7,
        Currency = 8,
        Tags = 9,
        Enum = 10,
        EmailAddress = 11,
        HTTP = 12,
        InternalId = 13,
        MemorySize=14,
         TimeSpan=15,
        PhoneNumber=16//this is so client can dial directly,
        Roles=17

    }
      */
      if (display != null) {
        switch (dataType) {
          case 1: //datetime format to shortdatetime
            display = window.$gz.locale.utcDateToShortDateAndTimeLocalized(
              display,
              timeZoneName,
              languageName,
              hour12
            );
            if (!window.$gz.locale.dateIsPast(column.v)) {
              cstStyle = { "font-weight": "bold" }; //warning, don't put a semicolon after the style or it will not work
            }
            break;
          case 2: //date only
            display = window.$gz.locale.utcDateToShortDateLocalized(
              display,
              timeZoneName,
              languageName
            );
            if (!window.$gz.locale.dateIsPast(column.v)) {
              cstStyle = { "font-weight": "bold" }; //warning, don't put a semicolon after the style or it will not work
            }
            break;
          case 3: //time only
            display = window.$gz.locale.utcDateToShortTimeLocalized(
              display,
              timeZoneName,
              languageName,
              hour12
            );
            break;
          case 4: //text
            if (display.length > MAX_TEXT_COLUMN_LENGTH) {
              display = display.substring(0, MAX_TEXT_COLUMN_LENGTH) + "...";
            }
            break;
          case 5: //Integer
            //display is already set, just some styling to do
            if (window.$gz.util.isNegative(column.v)) {
              cstStyle = { color: "#FF0000" }; //warning, don't put a semicolon after the style or it will not work
            }
            break;
          case 7: //decimal
            display = window.$gz.locale.decimalLocalized(display, languageName);
            if (window.$gz.util.isNegative(column.v)) {
              cstStyle = { color: "#FF0000" }; //warning, don't put a semicolon after the style or it will not work
            }
            break;
          case 8: //currency
            display = window.$gz.locale.currencyLocalized(
              display,
              languageName,
              currencyName
            );
            if (window.$gz.util.isNegative(column.v)) {
              cstStyle = { color: "#FF0000" }; //warning, don't put a semicolon after the style or it will not work
            }
            break;
          case 9: //tags
            if (display && display.length > 0) {
              display = display.join(", ");
            } else {
              display = "";
            }

            break;
          case 10: //enum
            display = window.$gz.enums.get(
              columndefinitions[iColumn].et,
              display
            );
            break;
          case 14: //MemorySize (file size)
            display = window.$gz.locale.humanFileSize(
              display,
              languageName,
              false,
              2
            );
            break;
          case 17: //Authorization Roles
            if (availableRoles == null) {
              availableRoles = window.$gz.enums.getSelectionList(
                "AuthorizationRoles"
              );
            }
            let roles = display;
            let roleNames = [];

            if (roles != null && roles != 0) {
              for (let i = 0; i < availableRoles.length; i++) {
                let role = availableRoles[i];
                if (!!(roles & role.id)) {
                  roleNames.push(role.name);
                }
              }
            }
            display = roleNames.join(", ");

            break;
          default:
          //do nothing, allow it to stay as is (checkbox, plain text etc)
        }
      }
      //build the row column object vm will be used by the datatable
      let columnObject = {
        v: display,
        t: dataType,
        key: iRow + "-" + iColumn
      };

      //is the source dtalist field openable?  If so it will have an i property set for it's ID and we already know the types to open from the column headers data
      //so between the two we can make a clickable button in the grid vm triggers a function with the column index and the id and vm in turn will bubble up the event to open vm
      //object
      if (column.i) {
        columnObject["i"] = column.i;
      }

      if (!ridColumnOpenable && column.rid) {
        columnObject["nopen"] = true;
      }

      //Is there an overriding openable type already defined?
      //(this is used by dynamic columns with different object type and id's that are openable like attachments and event logs and Review list etc)
      if (column.ot) {
        columnObject["ot"] = column.ot;
      }

      //rgba color to decorate field with
      if (column.clr) {
        columnObject["clr"] = column.clr;
      }

      //additional styling
      if (cstStyle) {
        columnObject["cst"] = cstStyle;
      }

      o.columns["c" + iColumn.toString()] = columnObject;
    }

    ret.push(o);
  }

  return ret;
}

//////////////////////////////////////////////////////////
//
// Ensures column names are present in translation table
//
async function fetchTranslatedHeaderNames(columnData) {
  if (!columnData) {
    return;
  }
  let headerKeys = [];
  for (let i = 0; i < columnData.length; i++) {
    let cm = columnData[i];
    headerKeys.push(cm.cm);
  }
  //Now fetch all the keys
  await window.$gz.translation.cacheTranslations(headerKeys);
}

//////////////////////////////////////////////////////////
//
// Ensures column enums are present in enums list in store
//
async function fetchEnums(columnData) {
  if (!columnData) {
    return;
  }
  let headerKeys = [];
  for (let i = 0; i < columnData.length; i++) {
    let cm = columnData[i];
    if (cm.et) {
      await window.$gz.enums.fetchEnumList(cm.et);
    }
  }
}

/////////////////////////////////
//
//
async function initForm(vm) {
  vm.timeZoneName = window.$gz.locale.getResolvedTimeZoneName();
  await fetchSavedFilterList(vm);
  loadFormSettings(vm);
}

////////////////////
//
async function fetchSavedFilterList(vm) {
  //http://localhost:7575/api/v8/data-list-filter/list?ListKey=TestWidgetDataList
  let res = await window.$gz.api.get(
    "data-list-filter/list?ListKey=" + vm.dataListKey
  );
  if (res.error) {
    vm.formState.serverError = res.error;
    window.$gz.form.setErrorBoxErrors(vm);
  } else {
    vm.selectLists.savedFilters = res.data;
    //confirm we still have the current active filter id
    setActiveFilter(vm, vm.activeFilterId);
  }
}

////////////////////
//
function saveFormSettings(vm) {
  window.$gz.form.setFormSettings(vm.formKey, {
    temp: { page: vm.dataTablePagingOptions.page },
    saved: {
      itemsPerPage: vm.dataTablePagingOptions.itemsPerPage,
      dataTable: {
        activeFilterId: vm.activeFilterId
      }
    }
  });
}

////////////////////
//
//
function setActiveFilter(vm, desiredId) {
  //Handle a change of filter, ensure it exists, if not then try to select default and if not that then just put in a zero
  //if desiredId is falsey then try to pick the default

  if (desiredId) {
    if (vm.selectLists.savedFilters.find(z => z.id == desiredId)) {
      vm.activeFilterId = desiredId;
      return;
    }
  }

  //no specific id so attempt to set to default
  let dflt = vm.selectLists.savedFilters.find(z => z.default == true);
  if (dflt) {
    vm.activeFilterId = dflt.id;
    return;
  }
  vm.activeFilterId = 0;
}

////////////////////
//
function loadFormSettings(vm) {
  let formSettings = window.$gz.form.getFormSettings(vm.formKey);

  //process SAVED formsettings
  if (formSettings.saved) {
    if (formSettings.saved.itemsPerPage) {
      vm.dataTablePagingOptions.itemsPerPage = formSettings.saved.itemsPerPage;
    }
    //have a nonzero saved filter id
    if (
      formSettings.saved.dataTable.activeFilterId != null &&
      formSettings.saved.dataTable.activeFilterId != 0
    ) {
      setActiveFilter(vm, formSettings.saved.dataTable.activeFilterId);
    }
  }

  //process TEMP form settings

  if (formSettings.temp && formSettings.temp.page) {
    vm.dataTablePagingOptions.page = formSettings.temp.page;
  }
}
</script>
