import Convert from './convert';
import {DataType} from './dataType'

// Ошибки DataBlock класса
class DataBlockError extends Error {
  name = 'DataBlock'
}

// Выделяет блок из файла для дальнейшей обработки
export default class DataBlock {

  // Байты блока
  private _data = new Uint8Array()
  get data() {
    return this._data
  }

  // Смещение блока
  private _offset = 0
  get offset() {
    return this._offset
  }

  private resetMeta() {
    this._offset = 0
  }

  constructor(bytes?: Uint8Array | DataType | number) {
    if (bytes) {
      if (bytes instanceof Uint8Array) {
        this.setBytes(bytes)
      } else {
        this._data = new Uint8Array(bytes)
      }
    }
  }

  // Проверят, что блоки содержат один набор данных
  isEqual(block: DataBlock) {
    if (this.data.length != block.data.length) return false
    for (let i = 0; i < this.data.length; i++) {
      if (this.data[i] !== block.data[i]) return false
    }
    return true
  }

  // Устанавливает файл или блок из него
  async setFile(file: File, offset = 0, length: DataType | number = file.size - offset) {
    if (offset < 0 || offset > file.size)
      throw new DataBlockError(`incorrect offset ${offset} for file length ${file.size}`)
    if (offset + length > file.size)
      throw new DataBlockError(
        `incorrect offset ${offset} or length ${length}, it's greater than file size ${file.size}`)

    this.resetMeta()
    this._offset = offset

    const blob = file.slice(offset, offset + length)
    const arr = await blob.arrayBuffer()
    this._data = new Uint8Array(arr)
    return this
  }

  // // Устанавливает строку
  // setString(str: string) {
  //   this._data = Convert.strToArr(str)
  //   return this
  // }
  //
  // // Устанавливает число
  // setNumber(num: number, size?: DataType | number) {
  //   let arr = Convert.numToArr(num)
  //   if (size) {
  //     let parts = [...<any>arr] as number[]
  //     parts.unshift(...new Array(size - arr.length).fill(0))
  //     arr = new Uint8Array(parts)
  //   }
  //   this._data = arr
  //   return this
  // }

  // Устанавливает байты
  setBytes(bytes: Uint8Array, offset: number = 0, length: number = bytes.length - offset!) {
    this.resetMeta()

    this._offset = offset
    this._data = bytes.slice(offset, offset + length)
  }

  // Переводит данные в строку
  toString(encoding = 'utf-8') {
    return Convert.arrToStr(this.data, encoding)
  }

  // Переводит данные в Null-Terminated строку
  toStringNT(encoding = 'utf-8') {
    let str = this.toString(encoding)
    const pos = str.indexOf('\0')
    if (pos >= 0) {
      str = str.substr(0, pos)
    }
    return str
  }

  // Переводит данные в число
  toNumber() {
    return Convert.arrToNum(this.data)
  }

  // Представляет число в виде hex строки
  toHex() {
    return this.toNumber().toString(16)
  }

  // Представляет данные в виде массива hex байт
  toHexArr() {
    return Array.from(this.data).map(n => n.toString(16))
  }

  // Копирует блок или его часть
  copy(offset = 0, length = this.data.length - offset) {
    const data = this.data.buffer.slice(offset, offset + length)
    const block = new DataBlock(new Uint8Array(data))
    block._offset = offset
    return block
  }

  // Копирует блок начиная со сдвига и до \0 символа, либо конца родительского блока
  copyNT(offset = 0) {
    let end = offset
    for (let i = offset; i < this.data.length; i++) {
      if (this.data[i] === 0) {
        end = i
        break
      }
    }
    return this.copy(offset, end - offset)
  }
}
