module.exports = class Validator
  
  JsPath = require '../jspath'
  {ValidationError,
   ValidationErrors} = require '../errortypes'
  
  @inbuilts = require './inbuilts'
  
  @validate =(model, callback)->
    errs = []
    {data} = model
    {constructor} = model
    for own path, validators of constructor.validators
      for {message, validate, option} in validators
        unless 'string' is typeof message
          {errorCode, message} = message
        value = JsPath.getFrom data, path
        err = null
        valid =
          try
            validate value
          catch e
            message = "#{message}\n#{e.message}"
            no
        unless valid
          path = (@path or []).concat path
          err = new ValidationError message, constructor, {
            path
            value
            option
            errorCode
          }
          errs.push err
          #model.emit 'error', err, model
    # recursively .validate() embedded models
    for own path of constructor.embeds
      embeddedModel = JsPath.getFrom data, path
      if embeddedModel?
        errs.push embeddedModel.validate()...
    # recursively validate every 
    for own path of constructor.subcollectionEmbeds
      subcollection = JsPath.getFrom data, path
      if subcollection
        for embeddedModel, index in subcollection
          errs.push embeddedModel.validate()...
    if errs.length
      err = new ValidationErrors errs
    else
      err = null
    callback? err, model
    errs
  
  constructor:(validator, option)->
    [@message, @validate] = validator
      # handle a special case where the arity of the
      # validator is 2, when we curry in the RHS as
      # the first argument of the function.
    if @validate.length is 2
      if 'function' is typeof option
        @option = option
      else
        # if "option" is an array, assume that
        # the first item is a custom error message.
        [@message, @option] = option
      # curry the option into the validation validate:
      # TODO: validators should have an execution context.
      @validate = @validate.bind null, @option