'use strict'

const constants = require('../constants')
const formatUtils = require('../formatUtils')
const lineBuilder = require('../lineBuilder')
const object = require('./object')

const UNEQUAL = constants.UNEQUAL

function describe (props) {
  const regexp = props.value
  return new DescribedRegexpValue(Object.assign({
    flags: getSortedFlags(regexp),
    source: regexp.source
  }, props))
}
exports.describe = describe

function deserialize (state, recursor) {
  return new DeserializedRegexpValue(state, recursor)
}
exports.deserialize = deserialize

const tag = Symbol('RegexpValue')
exports.tag = tag

function getSortedFlags (regexp) {
  const flags = regexp.flags || String(regexp).slice(regexp.source.length + 2)
  return flags.split('').sort().join('')
}

class RegexpValue extends object.ObjectValue {
  constructor (props) {
    super(props)
    this.flags = props.flags
    this.source = props.source
  }

  compare (expected) {
    return this.tag === expected.tag && this.flags === expected.flags && this.source === expected.source
      ? super.compare(expected)
      : UNEQUAL
  }

  formatShallow (theme, indent) {
    const ctor = this.ctor || this.stringTag
    const regexp = formatUtils.wrap(theme.regexp.source, this.source) + formatUtils.wrap(theme.regexp.flags, this.flags)

    return super.formatShallow(theme, indent).customize({
      finalize: innerLines => {
        if (ctor === 'RegExp' && innerLines.isEmpty) return lineBuilder.single(regexp)

        const innerIndentation = indent.increase()
        const header = lineBuilder.first(formatUtils.formatCtorAndStringTag(theme, this) + ' ' + theme.object.openBracket)
          .concat(lineBuilder.line(innerIndentation + regexp))

        if (!innerLines.isEmpty) {
          header.append(lineBuilder.line(innerIndentation + theme.regexp.separator))
          header.append(innerLines.withFirstPrefixed(innerIndentation).stripFlags())
        }

        return header.append(lineBuilder.last(indent + theme.object.closeBracket))
      },

      maxDepth: () => {
        return lineBuilder.single(
          formatUtils.formatCtorAndStringTag(theme, this) + ' ' +
          theme.object.openBracket + ' ' +
          regexp + ' ' +
          theme.maxDepth + ' ' +
          theme.object.closeBracket)
      }
    })
  }

  serialize () {
    return [this.flags, this.source, super.serialize()]
  }
}
Object.defineProperty(RegexpValue.prototype, 'tag', { value: tag })

const DescribedRegexpValue = object.DescribedMixin(RegexpValue)

class DeserializedRegexpValue extends object.DeserializedMixin(RegexpValue) {
  constructor (state, recursor) {
    super(state[2], recursor)
    this.flags = state[0]
    this.source = state[1]
  }
}
