text: 'use strict'
  
  const { createFormatter } = require('fmt-obj')
  const slice = require('sliced')
  
  const {
    getColor,
    colorize,
    isString,
    isEmpty,
    isSymbol,
    isObject,
    isBuffer,
    isError,
    isDate,
    isFalsy,
    isArray,
    hasWhiteSpace
  } = require('./Util')
  const CONST = require('./Constants')
  
  const ESCAPE_REGEX = /%{2,2}/g
  const TYPE_REGEX = /(%?)(%([Jjds]))/g
  
  function prettyObj (obj, color, opts) {
    const lineColor = getColor(CONST.LINE_COLOR)
    const { offset, depth } = opts
  
    const fmtObj = createFormatter({
      offset,
      formatter: {
        punctuation: lineColor,
        annotation: lineColor,
        property: getColor(color),
        literal: lineColor,
        number: lineColor,
        string: lineColor
      }
    })
  
    return fmtObj(obj, depth)
  }
  
  function serialize (obj, color, key) {
    // symbols cannot be directly casted to strings
    if (isSymbol(key)) key = key.toString()
    if (isSymbol(obj)) obj = obj.toString()
    if (isFalsy(obj)) obj = JSON.stringify(obj)
    if (!isObject(obj)) {
      if (key && isString(obj) && hasWhiteSpace(obj)) obj = `'${obj}'`
      return key ? `${key}=${obj}` : obj
    }
  
    if (isBuffer(obj)) {
      obj = obj.toString('base64')
      return key ? `${key}=${obj}` : obj
    }
  
    if (isError(obj)) return obj.message || obj
  
    let msg = ''
    const keys = Object.keys(obj)
    const { length } = keys
    let i = 0
    while (i < length) {
      key = keys[i]
      const value = obj[key]
  
      if (isArray(value)) {
        msg += key + '=['
        let j = 0
        const l = value.length
        while (j < l) {
          msg += serialize(value[j], color)
          if (j < l - 1) {
            msg += ' '
          }
          j++
        }
        msg += ']'
      } else if (isDate(value)) {
        msg += key + '=' + value
      } else {
        msg += serialize(value, color, colorize(color, key))
      }
      if (i < length - 1) {
        msg += ' '
      }
      i++
    }
    return msg
  }
  
  const createFormat = opts =>
    function format (messages) {
      const args = slice(arguments, 1)
      const color = args.pop()
  
      if (!isEmpty(args)) {
        messages = messages.replace(TYPE_REGEX, function (
          match,
          escaped,
          ptn,
          flag
        ) {
          let arg = args.shift()
          switch (flag) {
            case 's':
              arg = colorize(color, String(arg))
              break
            case 'd':
              arg = colorize(color, Number(arg))
              break
            case 'j':
              arg = serialize(arg, color)
              break
            case 'J':
              arg = prettyObj(arg, color, opts)
              break
          }
          if (!escaped) return arg
          args.unshift(arg)
          return match
        })
        if (!isEmpty(args)) {
          for (let arg of args) {
            messages += ` ${serialize(arg, color)}`
          }
        }
      }
  
      if (messages.replace != null) messages = messages.replace(ESCAPE_REGEX, '%')
      return serialize(messages, color)
    }
  
  module.exports = createFormat
  