<template>
  <div>
    <div class="control">
      <label class="control__label">Choose CSV file</label>
      <div class="control__description">
        You can import an existing CSV by uploading the .CSV file with tabular
        data. Most spreadsheet applications will allow you to export your
        spreadsheet as a .CSV file.
      </div>
      <div class="control__elements">
        <div class="file-upload">
          <input
            v-show="false"
            ref="file"
            type="file"
            accept=".csv"
            @change="select($event)"
          />
          <a
            class="button button--large button--ghost file-upload__button"
            @click.prevent="$refs.file.click($event)"
          >
            <i class="fas fa-cloud-upload-alt"></i>
            Choose CSV file
          </a>
          <div class="file-upload__file">{{ filename }}</div>
        </div>
        <div v-if="$v.filename.$error" class="error">
          This field is required.
        </div>
      </div>
    </div>
    <div v-if="filename !== ''" class="row">
      <div class="col col-4">
        <div class="control">
          <label class="control__label">Column separator</label>
          <div class="control__elements">
            <Dropdown v-model="columnSeparator" @input="reload()">
              <DropdownItem name="auto detect" value="auto"></DropdownItem>
              <DropdownItem name="," value=","></DropdownItem>
              <DropdownItem name=";" value=";"></DropdownItem>
              <DropdownItem name="|" value="|"></DropdownItem>
              <DropdownItem name="<tab>" value="\t"></DropdownItem>
              <DropdownItem
                name="record separator (30)"
                :value="String.fromCharCode(30)"
              ></DropdownItem>
              <DropdownItem
                name="unit separator (31)"
                :value="String.fromCharCode(31)"
              ></DropdownItem>
            </Dropdown>
          </div>
        </div>
      </div>
      <div class="col col-8">
        <div class="control">
          <label class="control__label">Encoding</label>
          <div class="control__elements">
            <CharsetDropdown
              v-model="encoding"
              @input="reload()"
            ></CharsetDropdown>
          </div>
        </div>
      </div>
    </div>
    <div v-if="filename !== ''" class="row">
      <div class="col col-6">
        <div class="control">
          <label class="control__label">First row is header</label>
          <div class="control__elements">
            <Checkbox v-model="values.firstRowHeader" @input="reload()"
              >yes</Checkbox
            >
          </div>
        </div>
      </div>
    </div>
    <div
      v-if="error !== ''"
      class="alert alert--error alert--has-icon margin-top-1"
    >
      <div class="alert__icon">
        <i class="fas fa-exclamation"></i>
      </div>
      <div class="alert__title">Something went wrong</div>
      <p class="alert__content">
        {{ error }}
      </p>
    </div>
    <TableImporterPreview
      v-if="error === '' && Object.keys(preview).length !== 0"
      :preview="preview"
    ></TableImporterPreview>
  </div>
</template>

<script>
import Papa from 'papaparse'
import { required } from 'vuelidate/lib/validators'

import form from '@baserow/modules/core/mixins/form'
import CharsetDropdown from '@baserow/modules/core/components/helpers/CharsetDropdown'
import importer from '@baserow/modules/database/mixins/importer'
import TableImporterPreview from '@baserow/modules/database/components/table/TableImporterPreview'

export default {
  name: 'TableCSVImporter',
  components: { TableImporterPreview, CharsetDropdown },
  mixins: [form, importer],
  data() {
    return {
      values: {
        data: '',
        firstRowHeader: true,
      },
      filename: '',
      columnSeparator: 'auto',
      encoding: 'utf-8',
      error: '',
      rawData: null,
      preview: {},
    }
  },
  validations: {
    values: {
      data: { required },
    },
    filename: { required },
  },
  methods: {
    /**
     * Method that is called when a file has been chosen. It will check if the file is
     * not larger than 15MB. Otherwise it will take a long time and possibly a crash
     * if so many entries have to be loaded into memory. If the file is valid, the
     * contents will be loaded into memory and the reload method will be called which
     * parses the content.
     */
    select(event) {
      if (event.target.files.length === 0) {
        return
      }

      const file = event.target.files[0]
      const maxSize = 1024 * 1024 * 15

      if (file.size > maxSize) {
        this.filename = ''
        this.values.data = ''
        this.error = 'The maximum file size is 15MB.'
        this.preview = {}
        this.$emit('input', this.value)
      } else {
        this.filename = file.name
        const reader = new FileReader()
        reader.addEventListener('load', (event) => {
          this.rawData = event.target.result
          this.reload()
        })
        reader.readAsArrayBuffer(event.target.files[0])
      }
    },
    /**
     * Parses the raw data with the user configured delimiter. If all looks good the
     * data is stored as a string because all the entries don't have to be reactive.
     * Also a small preview will be generated. If something goes wrong, for example
     * when the CSV doesn't have any entries the appropriate error will be shown.
     */
    reload() {
      const decoder = new TextDecoder(this.encoding)
      const decodedData = decoder.decode(this.rawData)
      const limit = this.$env.INITIAL_TABLE_DATA_LIMIT
      const count = decodedData.split(/\r\n|\r|\n/).length
      if (limit !== null && count > limit) {
        this.values.data = ''
        this.error = `It is not possible to import more than ${limit} rows.`
        this.preview = {}
        return
      }

      Papa.parse(decodedData, {
        delimiter: this.columnSeparator === 'auto' ? '' : this.columnSeparator,
        complete: (data) => {
          if (data.data.length === 0) {
            // We need at least a single entry otherwise the user has probably chosen
            // a wrong file.
            this.values.data = ''
            this.error = 'This CSV file is empty.'
            this.preview = {}
          } else {
            // If parsed successfully and it is not empty then the initial data can be
            // prepared for creating the table. We store the data stringified because
            // it doesn't need to be reactive.
            this.values.data = JSON.stringify(data.data)
            this.error = ''
            this.preview = this.getPreview(
              data.data,
              this.values.firstRowHeader
            )
          }
        },
        error(error) {
          // Papa parse has resulted in an error which we need to display to the user.
          // All previously loaded data will be removed.
          this.values.data = ''
          this.error = error.errors[0].message
          this.preview = {}
        },
      })
    },
  },
}
</script>