
throw Error "internal/Validator.coffee needs to be audited entirely!"

NamedFunction = require "./NamedFunction"
Type = require "./Type"
Kind = require "./Kind"

assert = require "./assert"

Validator = NamedFunction "Validator", (opts) ->
	Type.validate opts,
		types: [Object, {}]
		defaults: [Object, {}]
		assert: [Boolean, true]
		validate: [Function, Validator.validate]
	validator = -> 
		opts.validate
			targets: arguments
			types: opts.types
			defaults: opts.defaults
			assert: opts.assert
	return Type.set validator, Validator

# Type validation and default values without having to create your own Validator.
Validator.validate = (opts) ->

	Type.validate opts,
		target: Kind.validate Object
		targets: [Array]
		types: [Object, {}]
		defaults: [Object, {}]
		compare: [Function, Type.is]

	specs = {}

	for key, type in opts.types
		specs[key] = [type]

	for key, defalt in opts.defaults
		spec = specs[key]
		if spec?
		else
			specs[key] = [null, defalt]
		

	Type.validate opts, specs


	keys = Object.keys opts.types

	# Ensure all default keys are enumerated.
	for key in Object.keys opts.defaults
		keys.push key if opts.types[key] is undefined 

	# Validate each key for each target
	for target in opts.targets ? [opts.target]
		for key in keys
			return false unless _validateKey target, key, opts 

	return true

module.exports = Validator

#
# Internal
#

_validateKey = (target, key, opts) ->
	value = target[key]
	type = opts.types[key]

	if !type?
		target[key] = _getDefaultValue opts.defaults, key if value is undefined
		return

	if type.isRequired
		success = type.isTypeOf value
		if opts.assert then assert success, "Property with required type must be defined by user"
		return success

	if Type.is type, Object
		if value is undefined
			value = target[key] = {}
		validateTypes
			target: value
			types: type
			defaults: opts.defaults[key]
			compare: opts.compare
		return

	# Fill in default value if necessary.
	value = target[key] = _getDefaultValue opts.defaults, key if value is undefined

	validTypes = if Type.is opts.type, Array then opts.type else [opts.type]
	return if opts.compare value, validType for validType in validTypes

	throw new Error "invalid type"	

_getDefaultValue = (defaults, key) ->
	value = defaults[key]
	return if Type.of(value).isLazy then value() else value
