/* Vuex store, for centralized state management */

import Vue from 'vue';
import Vuex from 'vuex';
import client from 'api-client';
import main from './main';

Vue.use(Vuex);

function updateArray(oldArr, newArr) {
  // empty the array
  oldArr.splice(0, oldArr.length);
  // fill again
  for (let i = 0; i < newArr.length; i++) {
    oldArr.push(newArr[i]);
  }
}

export default new Vuex.Store({
  state: {
    path: '',
    loading: true,
    nodesLoading: false,
    asyncButtonEnabled: false,
    deleteButtonEnabled: false,
    jobs: [],
    jobsLoading: true,
    lastJobsCheckTime: null,
    user: 'anonymous',
    nodesToDelete: [],
    writable: false,
    nodeToShare: {
      path: null,
      groupRead: null,
      groupWrite: null
    }
  },
  mutations: {
    setLoading(state, loading) {
      state.loading = loading;
    },
    setPath(state, value) {
      if (!value) {
        value = '';
      }
      state.path = value;
    },
    setNodesLoading(state, value) {
      state.nodesLoading = value;
    },
    setAsyncButtonEnabled(state, value) {
      state.asyncButtonEnabled = value;
    },
    setDeleteButtonEnabled(state, value) {
      state.deleteButtonEnabled = value;
    },
    setJobs(state, jobs) {
      updateArray(state.jobs, jobs);
    },
    addJob(state, job) {
      state.jobs.push(job);
    },
    setJobsLoading(state, loading) {
      state.jobsLoading = loading;
    },
    setUsername(state, username) {
      state.user = username;
    },
    setNodesToDelete(state, paths) {
      updateArray(state.nodesToDelete, paths);
    },
    setWritable(state, value) {
      state.writable = value;
    },
    setNodeToShare(state, data) {
      state.nodeToShare.path = data.path;
      state.nodeToShare.groupRead = data.groupRead;
      state.nodeToShare.groupWrite = data.groupWrite;
    }
  },
  actions: {
    setPath({ state, commit, dispatch }, path) {
      commit('setPath', path);
      commit('setNodesLoading', true);
      client.getNode(state.path)
        .then(res => {
          dispatch('setNodes', res);
        })
        .finally(() => commit('setNodesLoading', false));
    },
    setNodes({ commit, dispatch }, res) {
      commit('setWritable', res.writable);
      document.getElementById('nodes').outerHTML = res.htmlTable;
      let checkboxes = document.querySelectorAll('#nodes input[type="checkbox"]');
      for (let i = 0; i < checkboxes.length; i++) {
        checkboxes[i].addEventListener('change', function() {
          dispatch('computeButtonsVisibility');
        });
      }
      dispatch('computeButtonsVisibility');
    },
    computeButtonsVisibility({ commit }) {
      commit('setAsyncButtonEnabled', document.querySelectorAll('#nodes input.async:checked').length > 0);
      commit('setDeleteButtonEnabled', document.querySelectorAll('#nodes input.deletable:checked').length > 0);
    },
    startAsyncRecallJob({ state, commit, dispatch }) {
      let asyncCheckboxes = document.querySelectorAll('#nodes input.async:checked');
      let paths = [];
      for (let i = 0; i < asyncCheckboxes.length; i++) {
        paths.push(asyncCheckboxes[i].getAttribute('data-node'));
      }
      client.startAsyncRecallJob(paths)
        .then(job => {
          main.showInfo('Job queued');
          commit('addJob', job);
          // Reload current node
          dispatch('setPath', state.path);
        });
    },
    checkJobs({ state, dispatch }) {
      if (state.jobs.filter(j => j.phase === 'QUEUED' || j.phase === 'PENDING' || j.phase === 'EXECUTING').length > 0 &&
        !state.jobsLoading && (state.lastJobsCheckTime !== null && new Date().getTime() - state.lastJobsCheckTime > 5000)) {
        dispatch('loadJobs');
      }
    },
    loadJobs({ state, commit }) {
      commit('setJobsLoading', true);
      client.loadJobs()
        .then(jobs => {
          for (let previousJob of state.jobs) {
            for (let newJob of jobs) {
              if (newJob.id === previousJob.id && newJob.phase !== previousJob.phase) {
                switch (newJob.phase) {
                  case 'EXECUTING':
                    main.showInfo('Job started');
                    break;
                  case 'COMPLETED':
                    main.showInfo('Job completed');
                    break;
                  case 'ERROR':
                    main.showError('Job failed');
                    break;
                }
              }
            }
          }
          commit('setJobs', jobs);
        })
        .finally(() => {
          commit('setJobsLoading', false);
          state.lastJobsCheckTime = new Date().getTime();
        });
    },
    loadUserInfo({ commit }) {
      client.getUserInfo()
        .then(res => commit('setUsername', res.username));
    },
    createFolder({ state, dispatch }, newFolderName) {
      client.createFolder(state.path, newFolderName)
        .then(() => {
          // Reload current node
          dispatch('setPath', state.path);
        });
    },
    uploadFiles({ state, dispatch }, files) {
      let names = [];
      for (let file of files) {
        names.push(file.name);
      }
      client.prepareForUpload(state.path, names)
        .then(uploadUrls => {
          let uploads = [];
          for (let i = 0; i < files.length; i++) {
            uploads.push(client.uploadFile(uploadUrls[i], files[i]));
          }
          Promise.all(uploads).then(() => {
            // Reload current node when all uploads completed
            dispatch('setPath', state.path);
          });
        });
    },
    deleteNodes({ state, dispatch }) {
      client.deleteNodes(state.nodesToDelete)
        .then(() => {
          // Reload current node
          dispatch('setPath', state.path);
        });
    }
  }
});