<template>
  <template v-if="!hide_title">
    <h2>{{ object_name }}管理</h2>
    <hr>
  </template>
  <div v-if="error">
    <div class="alert alert-danger">
      API错误：
      {{ error }}
    </div>
  </div>
  <div v-else-if="loading" class="alert alert-info">
    正在加载数据...
    <div class="my-3">
      <CSpinner/>
    </div>
  </div>
  <div v-else class="fade-in">
    <slot></slot>
    <div>
      <CInputGroup v-if="show_search" class="search-bar">
        <CFormInput type="text" v-model="search_query" @keyup.enter="reload_data" id="search-input"/>
        <CButton class="btn btn-primary" @click="do_search" id="search-btn">
        <font-awesome-icon icon="magnifying-glass" />
        查找
        </CButton>
      </CInputGroup>
      <div class="alert alert-success" v-if="current_query">
        查找关键字：{{ current_query }}
        <button class="btn btn-sm btn-secondary mx-3" @click="clear_query" id="clear-input">清除</button>
      </div>
      <GenericEditModal
        :fields="create_fields_" :object_name="object_name"
        :create="true"
        @close="create_closed"
        :uri="uri"
        ref="create_modal"
        :pre_save="pre_save"
      />
      <GenericDetailModal
        :fields="visible_fields_" :object_name="object_name"
        ref="detail_modal"
        :uri="uri"
      />
      <GenericEditModal
        :fields="editable_fields_" :object_name="object_name"
        :create="false"
        ref="edit_modal"
        @close="edit_closed"
        :uri="edit_object_uri"
        :pre_save="pre_save"
      />
      <Modal
        title="删除"
        body="确定要删除这条数据吗？"
        :verify_input="deleting_object_verify_input"
        ref="delete_modal"
        ></Modal>
      <Modal
        title="批量删除"
        body="确定要删除所有选中的数据吗？"
        ref="delete_selected_modal"
        ></Modal>
    </div>
    <div v-if="rows.length > 0">
      <div class="my-3">
        <button class="btn btn-secondary btn-sm" @click="reload" id="refresh-btn">
          <font-awesome-icon icon="refresh" />
        </button>
        <template v-if="!no_actions">
          <button v-if="!no_create" class="btn btn-success btn-sm" @click="show_create" id="add-btn">
            <font-awesome-icon icon="plus"/>
              新建
          </button>
          <button v-if="!no_delete" class="btn btn-primary btn-sm" @click="select_all" id="sel-all-btn">
            <font-awesome-icon icon="check-double"/>
              全选
          </button>
          <button v-if="!no_delete" class="btn btn-outline-danger btn-sm" @click="delete_selected" id="batch-del-btn">
            <font-awesome-icon icon="trash-can"/>
              批量删除
          </button>
        </template>
      </div>
      <CTable small hover striped>
        <CTableHead>
          <CTableRow>
            <th v-if="!no_actions && !no_edit" class="selectors">
            </th>
            <th v-for="f, i in list_fields_" :key="i">
              {{ f.metadata.verbose_name }}
            </th>
            <th v-for="a, i in custom_fields" :key="i">
              {{ a.title }}
            </th>
            <th v-if="!no_actions">操作</th>
          </CTableRow>
        </CTableHead>
        <CTableBody>
          <CTableRow v-for="r, i in rows" :key="i">
            <td v-if="!no_actions && !no_edit" class="selectors"><input type="checkbox" v-model="selected_rows[i]"></td>
            <td class="value" v-for="f, j in list_fields_" :key="i + '+' + j" >
              <span :title="display_field(r[f.name], f)" :id="f.name+i">
                {{ display_field(r[f.name], f) }}
              </span>
              <CBadge class="public-badge" color="info" v-if="r.public">(公共)</CBadge>
            </td>
            <td v-for="a, i in custom_fields" :key="i">
              <div v-html="a.get_html(r)"></div>
            </td>
            <td class="row-actions" v-if="!no_actions">
              <button class="btn btn-light btn-sm" title="查看详情" v-if="!no_details" @click="row_detail(r)">
                <font-awesome-icon icon="eye" />
              </button>
              <template v-if="actions">
                <button class="btn btn-sm"
                    :class="a.classes || 'btn-light'"
                    :title="a.title"
                    v-for="a, j in actions"
                    :disabled="a.is_write && !can_edit(r)"
                    @click="a.handler(r)" :key="j" :id="get_handle_btn(i,j)">
                  <font-awesome-icon v-if="a.icon" :title="a.title" :icon="a.icon" />
                  {{ a.get_name ? a.get_name(r) : a.name }}
                </button>
              </template>
              <button class="btn btn-light btn-sm" title="修改" v-if="!no_edit && can_edit(r)" @click="edit_row(r)" :id="get_edit_btn(i)">
                <font-awesome-icon icon="pencil" />
              </button>
              <button v-if="!no_delete" class="btn btn-outline-danger btn-sm" title="删除" @click="delete_row(r)" :id="get_del_btn(i)">
                <font-awesome-icon icon="trash-can" />
              </button>
            </td>
          </CTableRow>
          <CTableRow v-for="x, i in filler_rows" :key="'filler-row-' + i">
            <td v-if="!no_actions && !no_edit"></td>
            <td v-for="j in x" :key="'filler-cell-' + j">&nbsp;</td>
            <td v-for="a, i in custom_fields" :key="i"></td>
            <td v-if="!no_actions"></td>
          </CTableRow>
        </CTableBody>
      </CTable>
      <div v-if="!no_actions" class="my-3">
        <button v-if="!no_create" class="btn btn-success btn-sm" @click="show_create" id="add-btn">
          <font-awesome-icon icon="plus"/>
            新建
        </button>
        <button class="btn btn-primary btn-sm" @click="select_all" id="sel-all-btn">
          <font-awesome-icon icon="check-double"/>
            全选
        </button>
        <button v-if="!no_delete" class="btn btn-outline-danger btn-sm" @click="delete_selected" id="batch-del-btn">
          <font-awesome-icon icon="trash-can"/>
            批量删除
        </button>
      </div>
      <CPagination class="mt-3" size="sm">
        <CPaginationItem href="javascript:void(0)" :disabled="offset == 0" @click="goto_page(-this.meta.limit)">上一页</CPaginationItem>
        <CPaginationItem href="javascript:void(0)" :disabled="meta.total_count <= offset + meta.limit" @click="goto_page(this.meta.limit)">下一页</CPaginationItem>
        <CPaginationItem href="javascript:void(0)" :disabled="offset == 0" @click="goto_page(-this.offset)">第一页</CPaginationItem>
        <div class="page-jump">
          跳转到第 <input type="number" v-model="jump_page" min="1" :max="total_pages" class="page-input" @keyup.enter="jump_to_page"> 页
          (共 {{ total_pages }} 页)
        </div>
        <button class="btn btn-primary btn-sm mx-3" @click="jump_to_page" id="jump-btn">跳转</button>
      </CPagination>
      <div>
        <span>当前显示第 {{ offset + 1 }} - {{ Math.min(offset + meta.limit, meta.total_count) }} 条数据，共 {{ meta.total_count }} 条数据</span>
      </div>
    </div>
    <div v-else>
      <div class="alert alert-dark">
        共 0 条数据
      </div>
      <button v-if="!no_create" class="btn btn-success btn-sm" @click="show_create" id="add-btn">
        <font-awesome-icon icon="plus"/>
          新建
      </button>
    </div>
  </div>
</template>

<script>
import GenericEditModal from './generic-edit-modal'
import GenericDetailModal from './generic-detail-modal'

export default {
  name: 'GenericManager',
  props: ['uri', 'list_fields', 'visible_fields', 'editable_fields', 'object_name', 'show_search',
          'actions', 'delete_confirm_field_name', 'no_details', 'no_edit', 'default_values',
          'no_create', 'no_actions', 'no_delete',
          'pre_save', 'hide_title', 'patch_schema', 'custom_fields'],
  components: {
    GenericEditModal,
    GenericDetailModal,
  },
  data: function () {
    return {
      loading: true,
      error: null,
      schema: null,
      offset: 0,
      rows: [],
      meta: null,
      selected_rows: {},
      edit_object: null,
      deleting_object: null,
      detail_object: null,
      search_query: '',
      current_query: '',
      jump_page: 1,
    };
  },
  computed: {
    filler_rows: function() {
      var ret = [];
      for (var i = this.rows.length; i < 20; i++) {
        var r = [];
        for (var j in this.list_fields_) {
          r.push(j);
        }
        ret.push(r);
      }
      return ret;
    },
    deleting_object_verify_input: function() {
      var fn = this.delete_confirm_field_name;
      if (!fn || !this.deleting_object) {
        return null;
      }
      return this.deleting_object[fn];
    },
    visible_fields_: function() {
      return this.filter_fields(this.visible_fields);
    },
    list_fields_: function() {
      var fs = this.list_fields || this.visible_fields;
      return this.filter_fields(fs);
    },
    create_fields_: function() {
      var fs = this.filter_fields(this.editable_fields);
      for (var f of fs) {
        if (this.default_values && this.default_values[f.name]) {
          f.default_value = this.default_values[f.name];
        }
      }
      return fs;
    },
    editable_fields_: function() {
      return this.filter_fields(this.editable_fields);
    },
    edit_object_uri: function() {
      if (this.edit_object) return this.edit_object.resource_uri;
      return '';
    },
    base_uri: function() {
      return this.uri.replace(/\?.*/, '');
    },
    schema_uri: function() {
      return this.base_uri + "schema/";
    },
    query_uri: function() {
      var uri = this.uri + 
        (this.uri.includes('?') ? '&' : '?') +
        'offset=' + this.offset;
      if (this.search_query) {
        uri = uri + "&q=" + this.search_query;
      }
      return uri;
    },
    total_pages: function() {
      return Math.ceil(this.meta?.total_count / this.meta?.limit) || 1;
    },
  },
  methods: {
    do_search: function() {
      this.offset = 0;
      this.reload_data();
    },
    clear_query: function() {
      this.search_query = '';
      this.reload_data();
    },
    goto_page: function(step) {
      var new_val = this.offset + step;
      if (new_val >= 0 && new_val < this.meta.total_count) {
        this.offset = new_val;
        this.reload_data();
      }
      this.update_jump_page();
    },
    update_jump_page: function() {
      this.jump_page = Math.ceil((this.offset + 1) / this.meta.limit);
    },
    select_all: function() {
      for (var x in this.rows) {
        this.selected_rows[x] = true;
      }
    },
    show_create: function() {
      this.$refs.create_modal.initialize();
      this.$refs.create_modal.show();
    },
    create_closed: function(success) {
      if (success) this.reload();
    },
    reload: async function() {
      this.schema = null;
      this.data = null;
      this.reload_schema();
      this.reload_data();
      this.selected_rows = {};
    },
    filter_fields: function(allowed) {
      if (!allowed) {
        return [];
      }
      var fields = [];
      for (var f of allowed) {
        var x = this.schema.fields[f];
        if (!x) continue;
        fields.push({
          name: f,
          metadata: x
        });
      }
      return fields;
    },
    reload_schema: async function() {
      var r = await this.$root.api_get(this.schema_uri);
      var schema = r.data;
      if (this.patch_schema)
        schema = this.patch_schema(schema);
      this.schema = schema;
      console.log(schema);
    },
    reload_data: async function() {
      this.loading = true;
      try {
        var r = await this.$root.api_get(this.query_uri);
        this.current_query = this.search_query;
        this.meta = r.data.meta;
        this.rows = r.data.objects;
        this.loading = false;
        this.error = null;
      } catch (e) {
        this.loading = false;
        this.error = e;
      }
    },
    row_detail: function(row) {
      this.detail_object = row;
      this.$refs.detail_modal.set_obj(row);
      this.$refs.detail_modal.show();
    },
    can_edit: function(row) {
      if (row.public && !this.$root.is_admin) return false;
      return true;
    },
    edit_row: function(row) {
      this.edit_object = row;
      this.$refs.edit_modal.set_obj(row);
      this.$refs.edit_modal.show();
    },
    edit_closed: function(success) {
      if (success) this.reload();
    },
    delete_row: function(row) {
      this.deleting_object = row;
      this.$refs.delete_modal.show(async (ok) => {
        if (!ok) return;
        await this.$root.api_delete(this.base_uri + this.deleting_object.id + '/');
        this.reload();
      });
    },
    delete_selected: function() {
      this.$refs.delete_selected_modal.show(async (ok) => {
        if (!ok) return;
        for (var x in this.selected_rows) {
          await this.$root.api_delete(this.base_uri + this.rows[x].id + '/');
        }
        this.reload();
      });
    },
    get_edit_btn: function(index){
      return "edit-btn-"+index
    },
    get_del_btn: function(index){
      return "del-btn-"+index
    },
    get_handle_btn: function(i,j){
      return "handle-btn-"+i+"-"+j
    },
    jump_to_page: function() {
      if (!this.jump_page) return;
      const page = parseInt(this.jump_page);
      if (page < 1 || page > this.total_pages) return;
      const new_offset = (page - 1) * this.meta.limit;
      this.goto_page(new_offset - this.offset);
    },
  },
  mounted() {
    this.reload();
  },
}
</script>

<style scoped>
button {
  margin-right: 0.2rem;
}

.row-actions button {
  margin: 0.1rem;
}

.search-bar {
  max-width: 500px;
  margin: 1rem 0;
}

td.value {
  text-overflow: ellipsis;
  overflow: hidden;
  white-space: nowrap;
  max-width: 100px;
}
td {
  vertical-align:middle
}
.selectors {
  width: 3rem;
  padding-left: 1rem;
}
.public-badge {
  margin-left: 1rem;
}
.page-jump {
  display: inline-block;
  margin-left: 1rem;
}

.page-input {
  width: 60px;
  margin: 0 0.5rem;
  padding: 0.1rem 0.3rem;
}
</style>

