 # Takes 'resolved items' and generates JSON Schema for their wrapper array
 # *Element* node. This function chooses strategy and delegates to other
 # helper functions.
-buildItemsRepr = ({arrayElement, resolvedItems, resolvedType, fixed}, cb) ->
+buildArrayItemsRepr = ({arrayElement, resolvedItems, resolvedType, fixed}, cb) ->
   if fixed
     buildTupleItemsRepr arrayElement, resolvedItems, resolvedType, cb
   else if (ri for ri in resolvedItems when ri.fixed).length  # if contains fixed

# Takes 'resolved items' and generates JSON Schema for their wrapper array
# *Element* node.
buildArrayRepr = (context, cb) ->
  buildItemsRepr context, (err, itemsRepr) ->
    return cb err if err

    repr = type: 'array'

handleArrayElement = (arrayElement, resolvedType, inherited, cb) ->
  fixed = inspect.isOrInheritsFixed arrayElement, inherited
  heritage = inspect.getHeritage fixed, resolvedType

  async.waterfall [
    (next) -> resolveItems arrayElement, heritage, next
    (resolvedItems, next) -> buildArrayRepr {arrayElement, resolvedItems, resolvedType, fixed}, next
  ], cb


inspectEnum = (enumElement, resolvedType, cb) ->
  return cb new Error "Multiple nested types for enum." if resolvedType.nested.length > 1
  nestedTypeName = resolvedType.nested?[0]

  items = inspect.listItems enumElement
  if items.length
    hasSamples = inspect.haveVariableValues items
    resolveTypes items, nestedTypeName, (err, resolvedTypes) ->
      return cb err if err

      if inspect.areIdenticalAndPrimitive (rt.name for rt in resolvedTypes)
        strategy = if hasSamples then 'singleType' else 'values'
      else
        strategy = 'types'
      nestedTypeName ?= resolvedTypes[0].name

      cb null, {inline: false, strategy, nestedTypeName}
  else
    hasSamples = inspect.hasVariableValues enumElement
    strategy = if hasSamples then 'singleType' else 'values'
    cb null, {inline: true, strategy, nestedTypeName}


# Generates JSON Schema representation for given *Element* node containing
# an enum type.
handleEnumElement = (enumElement, resolvedType, inherited, cb) ->
  fixed = inspect.isOrInheritsFixed enumElement, inherited
  heritage = inspect.getHeritage fixed, resolvedType

  inspectEnum enumElement, resolvedType, (err, {strategy, inline, nestedTypeName}) ->
    return cb err if err

    switch strategy
      when 'types'  # cannot be inline
        async.waterfall [
          (next) -> resolveItems enumElement, heritage, next
          (resolvedItems, next) -> next null, anyOf: (ri.repr for ri in resolvedItems)
        ], cb

      when 'singleType'
        cb null, type: nestedTypeName

      else  # strategy 'values'
        if inline
          vals = inspect.listValues enumElement
          async.waterfall [
            (next) ->
              async.mapSeries vals, (val, done) ->
                coerceLiteral val.literal, nestedTypeName, done
              , next
            (reprs, next) -> next null, type: nestedTypeName, enum: reprs
          ], cb
        else
          async.waterfall [
            (next) ->
              items = inspect.listItems enumElement
              async.mapSeries items, (item, done) ->
                val = inspect.listValues(item)[0]
                coerceLiteral val.literal, nestedTypeName, done
              , next
            (reprs, next) -> next null, type: nestedTypeName, enum: reprs
          ], cb


# Generates JSON Schema representation for given *Element* node containing
# a primitive type (string, number, etc.).
handlePrimitiveElement = (primitiveElement, resolvedType, inherited, cb) ->
  # special case: inside enum, primitive elements are treated as fixed
  insideEnum = inherited.parentTypeName is 'enum'
  fixed = insideEnum or inspect.isOrInheritsFixed primitiveElement, inherited

  if fixed
    vals = inspect.listValues primitiveElement, true
    if vals.length
