/* $Id$
 *
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements. See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to you under the Apache License, Version
 * 2.0 (the "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef __ETCHVALIDATOR_H__
#define __ETCHVALIDATOR_H__

    #define VALIDATOR_CACHE_DEF()                                                               \
    static capu::SmartPointer<EtchValidator>* Validators(EtchRuntime* runtime);

#define VALIDATOR_CACHE_IMPL(validatorClassName)                                                \
    static EtchValidatorCaches SValidatorCache##validatorClassName;                             \
                                                                                                \
class EtchValidatorStringRuntimeListener : public EtchRuntimeListener {                         \
    status_t onRuntimeChanged(EtchRuntime* runtime) {                                           \
    if (runtime == NULL) {                                                                      \
        return ETCH_ERROR;                                                                      \
    }                                                                                           \
    if (runtime->isClosed()) {                                                                  \
        SValidatorCache##validatorClassName.clearCache();                                       \
        runtime->unregisterListener(this);                                                      \
    }                                                                                           \
    return ETCH_OK;                                                                             \
    }                                                                                           \
};                                                                                              \
                                                                                                \
    static EtchValidatorStringRuntimeListener SRuntimeChangedListener;                          \
                                                                                                \
    capu::SmartPointer<EtchValidator>* validatorClassName::Validators(EtchRuntime* runtime) { \
    return SValidatorCache##validatorClassName.get(runtime);                                    \
}

#define VALIDATOR_GET_DEF()                                                                                                         \
    static status_t Get(EtchRuntime* runtime, capu::uint32_t ndim, capu::SmartPointer<EtchValidator> &val);


#define VALIDATOR_GET_IMPL(validatorClassName)                                                                                      \
    status_t validatorClassName::Get(EtchRuntime* runtime, capu::uint32_t ndim, capu::SmartPointer<EtchValidator> &val) {           \
                                                                                                                                    \
                                                                                                                                    \
    if (ndim > MAX_NDIMS) {                                                                                                         \
        return ETCH_EINVAL;                                                                                                         \
    }                                                                                                                               \
    if (ndim >= MAX_CACHED) {                                                                                                       \
        val = new validatorClassName(runtime, ndim);                                                                                \
        return ETCH_OK;                                                                                                             \
    }                                                                                                                               \
    if (Validators(runtime)[ndim].get() == NULL) {                                                                                  \
        Validators(runtime)[ndim] = new validatorClassName(runtime, ndim);                                                          \
        runtime->registerListener(&SRuntimeChangedListener);                                                                        \
        ETCH_LOG_TRACE(runtime->getLogger(), runtime->getLogger().getValidatorContext(), "##validatorClassName has been created");  \
    }                                                                                                                               \
    val = Validators(runtime)[ndim];                                                                                                \
    return ETCH_OK;                                                                                                                 \
}

#include "common/EtchObject.h"
#include "support/EtchRuntime.h"

class EtchValidator : public EtchObject {
public:

  EtchValidator(const EtchObjectType *type) {
    addObjectType(type);
  }

  EtchValidator(const EtchValidator& other)
   : EtchObject(other) {
  }

  /**
   * Destructor
   */
  virtual ~EtchValidator() {

  }

  /**
   * The maximum number of dimensions for arrays (9).
   */
  const static capu::uint32_t MAX_NDIMS = 9;

  /**
   * The maximum number of validators to cache per type.
   */
  const static capu::uint32_t MAX_CACHED = 4;

  /**
   * @param val for an array type, return a validator for an element of
   * the array (which might be a scalar or another array element).
   * @return ETCH_OK if elementValidator is succesfully get
   *         ETCH_EINVAL otherwise
   */
  virtual status_t getElementValidator(capu::SmartPointer<EtchValidator> &val) = 0;

  /**
   * Checks the value for being valid. If the value is valid
   * by this validator, return true.
   * @param value the value to be validated.
   * @return true if the value is valid by any validator in the
   * chain, or false if all reject it.
   */
  virtual capu::bool_t validate(capu::SmartPointer<EtchObject> value) = 0;

  /**
   * @param value
   * @param result the appropriate value given the input value and this
   * validator's sensibility about what it should be really (e.g.,
   * input Byte but output Long.
   * @return TRUE if the value is validated
   *         FALSE otherwise
   */
  virtual status_t validateValue(capu::SmartPointer<EtchObject> value, capu::SmartPointer<EtchObject>& result) = 0;

};

/**
 * Etch validator chaches for each runtime
 */
class EtchValidatorCaches : public EtchRuntimeListener {
public:
  /**
   * Validator cache
   */
  struct ValidatorCache {
    capu::uint64_t id;
    capu::SmartPointer<EtchValidator> validators[EtchValidator::MAX_CACHED];
  };

  /**
   * Construct a new instance from the Validators class.
   */
  EtchValidatorCaches() {
  }

  /**
   * Destructor
   */
  virtual ~EtchValidatorCaches() {
      clearCache();
  }

  status_t onRuntimeChanged(EtchRuntime* runtime) {
    return ETCH_OK;
  }

  void clearCache() {
      capu::List<ValidatorCache*>::Iterator iter = mValidatorsCache.begin();
      while(iter != mValidatorsCache.end()) {
          ValidatorCache* entry = *iter;
          iter++;
          delete entry;
      }
      mValidatorsCache.clear();
  }

  capu::SmartPointer<EtchValidator>* get(EtchRuntime* runtime) {
    capu::List<ValidatorCache*>::Iterator iter = mValidatorsCache.begin();
    while(iter != mValidatorsCache.end()) {
      ValidatorCache* entry = *iter;
      iter++;
      if(entry->id == runtime->getId()) {
        return entry->validators;
      }
    }
    ValidatorCache* entry = new ValidatorCache();
    entry->id = runtime->getId();
    mValidatorsCache.insert(entry);
    return entry->validators;
  }

private:
  capu::List<ValidatorCache*> mValidatorsCache;
};

#endif /* ETCHVALIDATOR_H */

