const { ContentType } = require('./common')
const { CHANGE_CIPHER_SPEC, ALERT, HANDSHAKE, APPLICATION_DATA } = ContentType

const Defragger = require('./defragger')

/**
 * Reader
 */
class Reader {
  /**
   * construct a reader
   */
  constructor () {
    this.defragger = new Defragger()
    this.frag = null
  }

  /**
   * concat fragments
   */
  appendFrag (frag) {
    if (this.frag) {
      if (frag.type !== this.frag.type) {
        throw new Error('fragment type mismatch')
      }
      this.frag.data = Buffer.concat([this.frag.data, frag.data])
    } else {
      this.frag = frag
    }
  }

  /**

  */
  slice (size) {
    if (!this.frag) throw new Error('null frag')
    if (this.frag.data.length < size) throw new Error('inadequate data')

    const slice = this.frag.data.slice(0, size)
    if (size === this.frag.data.length) {
      this.frag = null
    } else {
      this.frag.data = this.frag.data.slice(size)
    }
    return slice
  }

  /**
  append input data
  */
  append (data) {
    this.defragger.append(data)
  }

  /**
   * read a message from current fragment
   */
  readFromFrag () {
    if (!this.frag) return null

    switch (this.frag.type) {
      case CHANGE_CIPHER_SPEC:
        if (this.frag.data[0] !== 1) throw new Error('bad change cipher spec')
        this.slice(1)
        return { type: CHANGE_CIPHER_SPEC }
      case ALERT: {
        if (this.frag.data.length < 2) return null
        const level = this.frag.data[0]
        const description = this.frag.data[1]
        this.slice(2)
        return { type: ALERT, level, description }
      }
      case HANDSHAKE: {
        if (this.frag.data.length < 4) return null
        // TODO validate
        const length = readUInt24(this.frag.data.slice(1))
        // const body =
        return {
          type: HANDSHAKE,
          handshake: {
            type: this.frag.data[0],
            body: null
          }
        }
      }
      case APPLICATION_DATA: {

      } break
      default:
        throw new Error('invalid content type')
    }
  }

  /**
   * set cipher
   */
  setCipher (cipher) {
    this.defragger.setCipher()
  }

  /**
   * read a message
   */
  read () {
    let message
    while (!(message = this.readFromFrag())) {
      const frag = this.defragger.read()
      if (!frag) return null
      this.appendFrag(frag)
    }
    return message
  }
}

module.exports = Reader