<template>
  <div class="file-list-container">
    <el-dialog
      :title="$t('New Directory')"
      :visible.sync="dirDialogVisible"
      width="30%">
      <el-form>
        <el-form-item :label="$t('Enter new directory name')">
          <el-input v-model="name" :placeholder="$t('New directory name')"></el-input>
        </el-form-item>
      </el-form>
      <span slot="footer" class="dialog-footer">
        <el-button @click="dirDialogVisible = false">{{$t('Cancel')}}</el-button>
        <el-button type="primary" @click="onAddDir">{{$t('Confirm')}}</el-button>
      </span>
    </el-dialog>

    <el-dialog
      :title="$t('New File')"
      :visible.sync="fileDialogVisible"
      width="30%">
      <el-form>
        <el-form-item :label="$t('Enter new file name')">
          <el-input v-model="name" :placeholder="$t('New file name')"></el-input>
        </el-form-item>
      </el-form>
      <span slot="footer" class="dialog-footer">
        <el-button size="small" @click="fileDialogVisible = false">{{$t('Cancel')}}</el-button>
        <el-button size="small" type="primary" @click="onAddFile">{{$t('Confirm')}}</el-button>
      </span>
    </el-dialog>

    <div
      class="file-tree-wrapper"
    >
      <el-tree
        class="tree"
        :data="computedFileTree"
        ref="tree"
        node-key="path"
        :highlight-current="true"
        :default-expanded-keys="expandedPaths"
        @node-contextmenu="onFileRightClick"
        @node-click="onFileClick"
        @node-expand="onDirClick"
        @node-collapse="onDirClick"
      >
        <span class="custom-tree-node" slot-scope="{ node, data }">
          <el-popover v-model="isShowCreatePopoverDict[data.path]" trigger="manual" placement="right"
                      popper-class="create-item-popover" :visible-arrow="false" @hide="onHideCreate(data)">
            <ul class="action-item-list">
              <li class="action-item" @click="fileDialogVisible = true">
                <font-awesome-icon icon="file-alt" color="rgba(3,47,98,.5)"/>
                <span class="action-item-text">{{$t('Create File')}}</span>
              </li>
              <li class="action-item" @click="dirDialogVisible = true">
                <font-awesome-icon :icon="['fa', 'folder']" color="rgba(3,47,98,.5)"/>
                <span class="action-item-text">{{$t('Create Directory')}}</span>
              </li>
            </ul>
            <ul class="action-item-list">
              <li class="action-item" @click="onClickRemoveNav(data)">
                <font-awesome-icon :icon="['fa', 'trash']" color="rgba(3,47,98,.5)"/>
                <span class="action-item-text">{{$t('Remove')}}</span>
              </li>
            </ul>
            <template slot="reference">
              <div>
                <span class="item-icon">
                  <font-awesome-icon v-if="data.is_dir" :icon="['fa', 'folder']" color="rgba(3,47,98,.5)"/>
                  <font-awesome-icon v-else-if="data.path.match(/\.py$/)" :icon="['fab','python']"
                                     color="rgba(3,47,98,.5)"/>
                  <font-awesome-icon v-else-if="data.path.match(/\.js$/)" :icon="['fab','node-js']"
                                     color="rgba(3,47,98,.5)"/>
                  <font-awesome-icon v-else-if="data.path.match(/\.(java|jar|class)$/)" :icon="['fab','java']"
                                     color="rgba(3,47,98,.5)"/>
                  <font-awesome-icon v-else-if="data.path.match(/\.go$/)" :icon="['fab','go']"
                                     color="rgba(3,47,98,.5)"/>
                  <font-awesome-icon v-else-if="data.path.match(/\.zip$/)" :icon="['fa','file-archive']"
                                     color="rgba(3,47,98,.5)"/>
                  <font-awesome-icon v-else icon="file-alt" color="rgba(3,47,98,.5)"/>
                </span>
                <span class="item-name" :class="isActiveFile(data) ? 'active' : ''">
                  {{data.name}}
                </span>
              </div>
            </template>
          </el-popover>
        </span>
      </el-tree>
      <div
        class="add-btn-wrapper"
      >
        <el-popover trigger="click" placement="right"
                    popper-class="create-item-popover" :visible-arrow="false">
          <ul class="action-item-list">
            <li class="action-item" @click="fileDialogVisible = true">
              <font-awesome-icon icon="file-alt" color="rgba(3,47,98,.5)"/>
              <span class="action-item-text">{{$t('Create File')}}</span>
            </li>
            <li class="action-item" @click="dirDialogVisible = true">
              <font-awesome-icon :icon="['fa', 'folder']" color="rgba(3,47,98,.5)"/>
              <span class="action-item-text">{{$t('Create Directory')}}</span>
            </li>
          </ul>
          <el-button
            class="add-btn"
            size="small"
            type="primary"
            icon="el-icon-plus"
            slot="reference"
            :disabled="isDisabled"
            @click="onEmptyClick"
          >
            {{$t('Add')}}
          </el-button>
        </el-popover>
      </div>
    </div>

    <div class="main-content">
      <div v-if="!showFile" class="file-list">
        {{$t('Please select a file or click the add button on the left.')}}
      </div>
      <template v-else>
        <div class="top-part">
          <!--back-->
          <div class="action-container">
            <el-popover v-model="isShowDelete" trigger="click">
              <el-button size="small" type="default" @click="() => this.isShowDelete = false">
                {{$t('Cancel')}}
              </el-button>
              <el-button size="small" type="danger" @click="onFileDelete">
                {{$t('Confirm')}}
              </el-button>
              <template slot="reference">
                <el-button type="danger" size="small" style="margin-right: 10px;" :disabled="isDisabled">
                  <font-awesome-icon :icon="['fa', 'trash']"/>
                  {{$t('Remove')}}
                </el-button>
              </template>
            </el-popover>
            <el-popover v-model="isShowRename" trigger="click">
              <el-input v-model="name" :placeholder="$t('Name')" style="margin-bottom: 10px"/>
              <div style="text-align: right">
                <el-button size="small" type="warning" @click="onRenameFile">
                  {{$t('Confirm')}}
                </el-button>
              </div>
              <template slot="reference">
                <div>
                  <el-button type="warning" size="small" style="margin-right: 10px;" :disabled="isDisabled" @click="onOpenRename">
                    <font-awesome-icon :icon="['fa', 'redo']"/>
                    {{$t('Rename')}}
                  </el-button>
                </div>
              </template>
            </el-popover>
            <el-button type="success" size="small" style="margin-right: 10px;" :disabled="isDisabled" @click="onFileSave">
              <font-awesome-icon :icon="['fa', 'save']"/>
              {{$t('Save')}}
            </el-button>
          </div>
          <!--./back-->

          <!--file path-->
          <div class="file-path-container">
            <div class="file-path">{{currentPath}}</div>
          </div>
          <!--./file path-->
        </div>
        <file-detail/>
      </template>
    </div>
  </div>
</template>

<script>
import {
  mapState,
  mapGetters
} from 'vuex'
import FileDetail from './FileDetail'

export default {
  name: 'FileList',
  components: { FileDetail },
  data () {
    return {
      isEdit: false,
      showFile: false,
      name: '',
      isShowAdd: false,
      isShowDelete: false,
      isShowRename: false,
      isShowCreatePopoverDict: {},
      currentFilePath: '.',
      ignoreFileRegexList: [
        '__pycache__',
        'md5.txt',
        '.pyc',
        '.git'
      ],
      activeFileNode: {},
      dirDialogVisible: false,
      fileDialogVisible: false,
      nodeExpandedDict: {},
      isShowDeleteNav: false
    }
  },
  computed: {
    ...mapState('spider', [
      'fileTree',
      'spiderForm'
    ]),
    ...mapState('file', [
      'fileList'
    ]),
    ...mapGetters('user', [
      'userInfo'
    ]),
    currentPath: {
      set (value) {
        this.$store.commit('file/SET_CURRENT_PATH', value)
      },
      get () {
        return this.$store.state.file.currentPath
      }
    },
    computedFileTree () {
      if (!this.fileTree || !this.fileTree.children) return []
      let nodes = this.sortFiles(this.fileTree.children)
      nodes = this.filterFiles(nodes)
      return nodes
    },
    expandedPaths () {
      return Object.keys(this.nodeExpandedDict)
        .map(path => {
          return {
            path,
            expanded: this.nodeExpandedDict[path]
          }
        })
        .filter(d => d.expanded)
        .map(d => d.path)
    },
    isDisabled () {
      return this.spiderForm.is_public && this.spiderForm.username !== this.userInfo.username && this.userInfo.role !== 'admin'
    }
  },
  methods: {
    onEdit () {
      this.isEdit = true
    },
    onItemClick (item) {
      if (item.is_dir) {
        // 目录
        this.$store.dispatch('file/getFileList', { path: item.path })
      } else {
        // 文件
        this.showFile = true
        this.$store.commit('file/SET_FILE_CONTENT', '')
        this.$store.commit('file/SET_CURRENT_PATH', item.path)
        this.$store.dispatch('file/getFileContent', { path: item.path })
      }
      this.$st.sendEv('爬虫详情', '文件', '点击')
    },
    async onFileSave () {
      await this.$store.dispatch('file/saveFileContent', { path: this.currentPath })
      this.$message.success(this.$t('Saved file successfully'))
      this.$st.sendEv('爬虫详情', '文件', '保存')
    },
    async onAddFile () {
      if (!this.name) {
        this.$message.error(this.$t('Name cannot be empty'))
        return
      }
      const arr = this.activeFileNode.path.split('/')
      if (this.activeFileNode.is_dir) {
        arr.push(this.name)
      } else {
        arr[arr.length - 1] = this.name
      }
      const path = arr.join('/')
      await this.$store.dispatch('file/addFile', { path })
      await this.$store.dispatch('spider/getFileTree')
      this.isShowAdd = false
      this.fileDialogVisible = false
      this.showFile = true
      this.$store.commit('file/SET_FILE_CONTENT', '')
      this.$store.commit('file/SET_CURRENT_PATH', path)
      await this.$store.dispatch('file/getFileContent', { path })
      this.$st.sendEv('爬虫详情', '文件', '添加')
    },
    async onAddDir () {
      if (!this.name) {
        this.$message.error(this.$t('Name cannot be empty'))
        return
      }
      const arr = this.activeFileNode.path.split('/')
      if (this.activeFileNode.is_dir) {
        arr.push(this.name)
      } else {
        arr[arr.length - 1] = this.name
      }
      const path = arr.join('/')
      await this.$store.dispatch('file/addDir', { path })
      await this.$store.dispatch('spider/getFileTree')
      this.isShowAdd = false
      this.dirDialogVisible = false
      this.$st.sendEv('爬虫详情', '文件', '添加')
    },
    async onFileDelete () {
      await this.$store.dispatch('file/deleteFile', { path: this.currentFilePath })
      await this.$store.dispatch('spider/getFileTree')
      this.$message.success(this.$t('Deleted successfully'))
      this.isShowDelete = false
      this.showFile = false
      this.$st.sendEv('爬虫详情', '文件', '删除')
    },
    onOpenRename () {
      const arr = this.currentFilePath.split('/')
      this.name = arr[arr.length - 1]
    },
    async onRenameFile () {
      await this.$store.dispatch('file/renameFile', { path: this.currentFilePath, newPath: this.name })
      await this.$store.dispatch('spider/getFileTree')
      const arr = this.currentFilePath.split('/')
      arr[arr.length - 1] = this.name
      this.currentFilePath = arr.join('/')
      this.$store.commit('file/SET_CURRENT_PATH', this.currentFilePath)
      this.$message.success(this.$t('Renamed successfully'))
      this.isShowRename = false
      this.$st.sendEv('爬虫详情', '文件', '重命名')
    },
    async getFileTree () {
      const arr = this.$route.path.split('/')
      const id = arr[arr.length - 1]
      await this.$store.dispatch('spider/getFileTree', { id })
    },
    async onFileClick (data) {
      if (data.is_dir) {
        return
      }
      this.currentFilePath = data.path
      this.onItemClick(data)
    },
    onDirClick (data, node) {
      const vm = this
      setTimeout(() => {
        vm.$set(vm.nodeExpandedDict, data.path, node.expanded)
      }, 0)
    },
    sortFiles (nodes) {
      nodes.forEach(node => {
        if (node.is_dir) {
          if (!node.children) node.children = []
          node.children = this.sortFiles(node.children)
        }
      })
      return nodes.sort((a, b) => {
        if ((a.is_dir && b.is_dir) || (!a.is_dir && !b.is_dir)) {
          return a.name > b.name ? 1 : -1
        } else {
          return a.is_dir ? -1 : 1
        }
      })
    },
    filterFiles (nodes) {
      return nodes.filter(node => {
        if (node.is_dir) {
          node.children = this.filterFiles(node.children)
        }
        for (let i = 0; i < this.ignoreFileRegexList.length; i++) {
          const regex = this.ignoreFileRegexList[i]
          if (node.name.match(regex)) {
            return false
          }
        }
        return true
      })
    },
    isActiveFile (node) {
      return node.path === this.currentFilePath
    },
    onFileRightClick (ev, data) {
      this.isShowCreatePopoverDict = {}
      this.$set(this.isShowCreatePopoverDict, data.path, true)
      this.activeFileNode = data
      this.$st.sendEv('爬虫详情', '文件', '右键点击导航栏')
    },
    onEmptyClick () {
      const data = { path: '' }
      this.isShowCreatePopoverDict = {}
      this.$set(this.isShowCreatePopoverDict, data.path, true)
      this.activeFileNode = data
      this.$st.sendEv('爬虫详情', '文件', '空白点击添加')
    },
    onHideCreate (data) {
      this.$set(this.isShowCreatePopoverDict, data.path, false)
      this.name = ''
    },
    onClickRemoveNav (data) {
      this.$confirm(this.$t('Are you sure to delete this file/directory?'), this.$t('Notification'), {
        confirmButtonText: this.$t('Confirm'),
        cancelButtonText: this.$t('Cancel'),
        confirmButtonClass: 'danger',
        type: 'warning'
      }).then(() => {
        this.onFileDeleteNav(data.path)
      })
    },
    async onFileDeleteNav (path) {
      await this.$store.dispatch('file/deleteFile', { path })
      await this.$store.dispatch('spider/getFileTree')
      this.$message.success(this.$t('Deleted successfully'))
      this.isShowDelete = false
      this.showFile = false
      this.$st.sendEv('爬虫详情', '文件', '删除')
    },
    clickSpider (filepath) {
      const node = this.$refs['tree'].getNode(filepath)
      const data = node.data
      this.onFileClick(data)
      node.parent.expanded = true
      this.$set(this.nodeExpandedDict, node.parent.data.path, true)
      node.parent.parent.expanded = true
      this.$set(this.nodeExpandedDict, node.parent.parent.data.path, true)
    },
    clickPipeline () {
      const filename = 'pipelines.py'
      for (let i = 0; i < this.computedFileTree.length; i++) {
        const dataLv1 = this.computedFileTree[i]
        const nodeLv1 = this.$refs['tree'].getNode(dataLv1.path)
        if (dataLv1.is_dir) {
          for (let j = 0; j < dataLv1.children.length; j++) {
            const dataLv2 = dataLv1.children[j]
            if (dataLv2.path.match(filename)) {
              this.onFileClick(dataLv2)
              nodeLv1.expanded = true
              this.$set(this.nodeExpandedDict, dataLv1.path, true)
              return
            }
          }
        }
      }
    }
  },
  async created () {
    await this.getFileTree()
  },
  mounted () {
    this.listener = document.querySelector('body').addEventListener('click', ev => {
      this.isShowCreatePopoverDict = {}
    })
  },
  destroyed () {
    document.querySelector('body').removeEventListener('click', this.listener)
  }
}
</script>

<style scoped lang="scss">
  .file-list-container {
    margin-left: -15px;
    height: 100%;
    min-height: 100%;

    .top-part {
      display: flex;
      height: 33px;
      margin-bottom: 10px;

      .file-path-container {
        width: 100%;
        padding: 5px;
        margin: 0 10px 0 0;
        border-radius: 5px;
        border: 1px solid #eaecef;
        display: flex;
        justify-content: space-between;
        color: rgba(3, 47, 98, 1);

        .left {
          width: 100%;
          display: flex;

          .el-icon-back {
            margin-right: 10px;
            cursor: pointer;
          }

          .el-input {
            /*height: 22px;*/
            width: 100%;
            line-height: 10px;
          }
        }

        .el-icon-edit {
          cursor: pointer;
        }
      }

      .action-container {
        text-align: right;
        display: flex;
        /*padding: 1px 5px;*/
        /*height: 24px;*/

        .el-button {
          margin: 0;
        }
      }
    }

    .file-list {
      padding: 10px;
      list-style: none;
      height: 100%;
      overflow-y: auto;
      min-height: 100%;
      /*border-radius: 5px;*/
      /*border: 1px solid #eaecef;*/
      display: flex;
      align-items: center;
      justify-content: center;
      font-size: 24px;
    }
  }
</style>

<style scoped>
  .file-path >>> .el-input__inner {
    font-size: 14px;
    line-height: 18px;
    height: 18px;
    border-top: none;
    border-left: none;
    border-right: none;
    border-bottom: 2px solid #409EFF;
    border-radius: 0;
  }

  .CodeMirror-line {
    padding-right: 20px;
  }

  .item {
    border-bottom: 1px solid #eaecef;
  }

  .item-icon {
    display: inline-block;
    width: 18px;
  }

  .item-name {
    font-size: 14px;
    color: rgba(3, 47, 98, 1);
  }

  .add-type-list {
    text-align: right;
    margin-top: 10px;
  }

  .add-type {
    cursor: pointer;
    font-weight: bolder;
  }

  .file-tree-wrapper {
    float: left;
    width: 240px;
    height: calc(100vh - 200px);
    overflow: auto;
  }

  .file-tree-wrapper >>> .el-tree-node__content {
    height: 30px;
  }

  .file-tree-wrapper >>> .el-tree-node__content .item-name.active {
    font-weight: bolder;
  }

  .main-content {
    float: left;
    width: calc(100% - 240px);
    height: calc(100vh - 200px);
    border-left: 1px solid #eaecef;
    padding-left: 20px;
  }

</style>

<style>
  .create-item-popover {
    padding: 0;
    margin: 0;
  }

  .action-item-list {
    list-style: none;
    padding: 0;
    margin: 0
  }

  .action-item-title {
    padding-top: 10px;
    padding-left: 15px;
    padding-bottom: 5px;
  }

  .action-item-list .action-item {
    display: flex;
    align-items: center;
    height: 35px;
    padding: 0 0 0 10px;
    margin: 0;
    cursor: pointer;
  }

  .action-item-list .action-item:last-child {
    border-bottom: 1px solid #eaecef;
  }

  .action-item-list .action-item:hover {
    background: #F5F7FA;
  }

  .action-item-list .action-item svg {
    width: 20px;
  }

  .action-item-list .action-item .action-item-text {
    margin-left: 5px;
  }

  .add-btn-wrapper {
    width: 220px;
    border-top: 1px solid #eaecef;
    margin: 10px 10px;
  }

  .add-btn-wrapper .add-btn {
    width: 80px;
    margin-left: calc(120px - 40px - 10px);
    margin-top: 20px;
  }
</style>
