/*
    Ousía
    Copyright (C) 2014, 2015  Benjamin Paaßen, Andreas Stöckel
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
    You should have received a copy of the GNU General Public License
    along with this program.  If not, see .
*/
/**
 * @file Argument.hpp
 *
 * Contains the declaration of the Argument and Arguments classes which are used
 * to define the list of Arguments that can be passed to a Method or the set
 * of attributes that are attached to an XML node or similar.
 *
 * The Argument and Arguments classes have some ressemblance to the Attribute
 * and StructType types, however the classes defined here have been built to
 * represent types which are known at compile time, whereas Attribute and
 * StructType represent types defined at runtime by the user.
 *
 * @author Andreas Stöckel (astoecke@techfak.uni-bielefeld.de)
 */
#ifndef _OUSIA_ARGUMENT_HPP_
#define _OUSIA_ARGUMENT_HPP_
#include 
#include 
#include 
#include "Variant.hpp"
namespace ousia {
// Forward declaration
class Logger;
class Rtti;
/**
 * The Argument class represents a single argument that can be passed to a
 * function.
 */
class Argument {
private:
	/**
	 * Contains the name of the argument. Used for logging and in case the
	 * arguments are presented as map.
	 */
	std::string name;
	/**
	 * Type that should be returned by the Variant rttiType function.
	 */
	Rtti const* type;
	/**
	 * Describes the inner type of the variant -- e.g. the type of the elements
	 * inside an array. Normally set to RttiTypes::None.
	 */
	Rtti const* innerType;
	/**
	 * Default value. Note that a value of nullptr does not indicate that no
	 * default value has been set. Use the "hasDefault" flag for this purpose.
	 * Nullptr is a valid value for objects.
	 */
	Variant defaultValue;
	/**
	 * True if a default value is set, false otherwise.
	 */
	bool hasDefaultValue;
	/**
	 * Private constructor used for manually setting all internal data fields.
	 *
	 * @param name is the name of the Argument.
	 * @param variantType is the variant type of the argument that is to be
	 * expected.
	 * @param rttiType is the rttiType of the argument. Only used it the type
	 * of the variant is an object.
	 * @param defaultValue is the default value to be used.
	 * @param hasDefault indicates whether the defaultValue actually should be
	 * used.
	 */
	Argument(std::string name, const Rtti &type, const Rtti &innerType,
	         Variant defaultValue, bool hasDefault);
	/**
	 * Private constructor used to build an argument describing a primitive type
	 * with default value.
	 *
	 * @param name is the name of the Argument.
	 * @param variantType is the variant type of the argument that is to be
	 * expected.
	 * @param defaultValue is the default value to be used.
	 */
	Argument(std::string name, const Rtti &type, Variant defaultValue);
	/**
	 * Private constructor used to build an argument describing a primitive type
	 * without default value.
	 *
	 * @param name is the name of the Argument.
	 * @param variantType is the variant type of the argument that is to be
	 * expected.
	 */
	Argument(std::string name, const Rtti &type);
public:
	/**
	 * Named constructor for an argument with any type.
	 *
	 * @param name is the name of the argument as used for error messages and in
	 * case the arguments are given as a map.
	 * @return a new Argument instance.
	 */
	static Argument Any(std::string name);
	/**
	 * Named constructor for an argument with any type.
	 *
	 * @param name is the name of the argument as used for error messages and in
	 * case the arguments are given as a map.
	 * @param defaultValue is the default value to be used in case this argument
	 * is not supplied.
	 * @return a new Argument instance.
	 */
	static Argument Any(std::string name, Variant defaultValue);
	/**
	 * Named constructor for a boolean argument with no default value.
	 *
	 * @param name is the name of the argument as used for error messages and in
	 * case the arguments are given as a map.
	 * @return a new Argument instance.
	 */
	static Argument Bool(std::string name);
	/**
	 * Named constructor for a boolean argument with default value.
	 *
	 * @param name is the name of the argument as used for error messages and in
	 * case the arguments are given as a map.
	 * @param defaultValue is the default value to be used in case this argument
	 * is not supplied.
	 * @return a new Argument instance.
	 */
	static Argument Bool(std::string name, Variant::boolType defaultValue);
	/**
	 * Named constructor for an integer argument with no default value.
	 *
	 * @param name is the name of the argument as used for error messages and in
	 * case the arguments are given as a map.
	 * @return a new Argument instance.
	 */
	static Argument Int(std::string name);
	/**
	 * Named constructor for an integer argument with default value.
	 *
	 * @param name is the name of the argument as used for error messages and in
	 * case the arguments are given as a map.
	 * @param defaultValue is the default value to be used in case this argument
	 * is not supplied.
	 * @return a new Argument instance.
	 */
	static Argument Int(std::string name, Variant::intType defaultValue);
	/**
	 * Named constructor for a double argument with no default value.
	 *
	 * @param name is the name of the argument as used for error messages and in
	 * case the arguments are given as a map.
	 * @return a new Argument instance.
	 */
	static Argument Double(std::string name);
	/**
	 * Named constructor for a double argument with default value.
	 *
	 * @param name is the name of the argument as used for error messages and in
	 * case the arguments are given as a map.
	 * @param defaultValue is the default value to be used in case this argument
	 * is not supplied.
	 * @return a new Argument instance.
	 */
	static Argument Double(std::string name, Variant::doubleType defaultValue);
	/**
	 * Named constructor for a string argument with no default value.
	 *
	 * @param name is the name of the argument as used for error messages and in
	 * case the arguments are given as a map.
	 * @return a new Argument instance.
	 */
	static Argument String(std::string name);
	/**
	 * Named constructor for a string argument with default value.
	 *
	 * @param name is the name of the argument as used for error messages and in
	 * case the arguments are given as a map.
	 * @param defaultValue is the default value to be used in case this argument
	 * is not supplied.
	 * @return a new Argument instance.
	 */
	static Argument String(std::string name,
	                       const Variant::stringType &defaultValue);
	/**
	 * Named constructor for an object argument with no default value. Object
	 * arguments always point at an instance of the Managed class. The concrete
	 * Object type must be specified in the "type" argument.
	 *
	 * @param name is the name of the argument as used for error messages and in
	 * case the arguments are given as a map.
	 * @param type is the Rtti of acceptable objects. All objects where the
	 * "isa" function returns true for the given type are be accepted.
	 * @return a new Argument instance.
	 */
	static Argument Object(std::string name, const Rtti &type);
	/**
	 * Named constructor for an object argument with default value. The default
	 * value can only be nullptr. Object arguments always point at an instance
	 * of the Managed class. The concrete Object type must be specified in the
	 * "type" argument.
	 *
	 * @param name is the name of the argument as used for error messages and in
	 * case the arguments are given as a map.
	 * @param type is the Rtti of acceptable objects. All objects where the
	 * "isa" function returns true for the given type are be accepted.
	 * @param defaultValue must be set to nullptr. Default object instances
	 * cannot be stored.
	 * @return a new Argument instance.
	 */
	static Argument Object(std::string name, const Rtti &type,
	                       std::nullptr_t defaultValue);
	/**
	 * Named constructor for a function argument with no default value.
	 *
	 * @param name is the name of the argument as used for error messages and in
	 * case the arguments are given as a map.
	 * @return a new Argument instance.
	 */
	static Argument Function(std::string name);
	/**
	 * Named constructor for a function argument with default value.
	 *
	 * @param name is the name of the argument as used for error messages and in
	 * case the arguments are given as a map.
	 * @param defaultValue is the default value to be used in case this argument
	 * is not supplied.
	 * @return a new Argument instance.
	 */
	static Argument Function(std::string name,
	                         Variant::functionType defaultValue);
	/**
	 * Named constructor for an integer argument with no default and no specific
	 * inner type.
	 *
	 * @param name is the name of the argument as used for error messages and in
	 * case the arguments are given as a map.
	 * @return a new Argument instance.
	 */
	static Argument Array(std::string name);
	/**
	 * Named constructor for an array argument with default value and no
	 * specific inner type.
	 *
	 * @param name is the name of the argument as used for error messages and in
	 * case the arguments are given as a map.
	 * @param defaultValue is the default value to be used in case this argument
	 * is not supplied.
	 * @return a new Argument instance.
	 */
	static Argument Array(std::string name,
	                      const Variant::arrayType &defaultValue);
	/**
	 * Named constructor for an array argument of objects of the given RTTI
	 * type.
	 *
	 * @param name is the name of the argument as used for error messages and in
	 * case the arguments are given as a map.
	 * @param innerType is the inner type of the array. All array elements are
	 * forced to be of this type.
	 * @return a new Argument instance.
	 */
	static Argument Array(std::string name, const Rtti &innerType);
	/**
	 * Named constructor for an array argument of objects of the given RTTI
	 * type.
	 *
	 * @param name is the name of the argument as used for error messages and in
	 * case the arguments are given as a map.
	 * @param innerType is the inner type of the array. All array elements are
	 * forced to be of this type.
	 * @return a new Argument instance.
	 * @param defaultValue is the default value to be used in case this argument
	 * is not supplied.
	 */
	static Argument Array(std::string name, const Rtti &innerType,
	                      const Variant::arrayType &defaultValue);
	/**
	 * Named constructor for a map argument with no default value and no
	 * specific inner type.
	 *
	 * @param name is the name of the argument as used for error messages and in
	 * case the arguments are given as a map.
	 * @return a new Argument instance.
	 */
	static Argument Map(std::string name);
	/**
	 * Named constructor for a map argument with default value and no specific
	 * inner type.
	 *
	 * @param name is the name of the argument as used for error messages and in
	 * case the arguments are given as a map.
	 * @param defaultValue is the default value to be used in case this argument
	 * is not supplied.
	 * @return a new Argument instance.
	 */
	static Argument Map(std::string name, const Variant::mapType &defaultValue);
	/**
	 * Named constructor for a map argument with no default value and a given
	 * inner type.
	 *
	 * @param name is the name of the argument as used for error messages and in
	 * case the arguments are given as a map.
	 * @param innerType is the inner type of the map. All map entries are forced
	 * to be of this type.
	 * @return a new Argument instance.
	 */
	static Argument Map(std::string name, const Rtti &innerType);
	/**
	 * Named constructor for a map argument with default value and a given inner
	 * type.
	 *
	 * @param name is the name of the argument as used for error messages and in
	 * case the arguments are given as a map.
	 * @param innerType is the inner type of the map. All map entries are forced
	 * to be of this type.
	 * @param defaultValue is the default value to be used in case this argument
	 * is not supplied.
	 * @return a new Argument instance.
	 */
	static Argument Map(std::string name, const Rtti &innerType,
	                    const Variant::mapType &defaultValue);
	/**
	 * Named constructor for a cardinality argument with no default value.
	 *
	 * @param name is the name of the argument as used for error messages and in
	 * case the arguments are given as a map.
	 * @return a new Argument instance.
	 */
	static Argument Cardinality(std::string name);
	/**
	 * Named constructor for a Cardinality argument with default value.
	 *
	 * @param name is the name of the argument as used for error messages and in
	 * case the arguments are given as a map.
	 * @param defaultValue is the default value to be used in case this argument
	 * is not supplied.
	 * @return a new Argument instance.
	 */
	static Argument Cardinality(std::string name,
	                            Variant::cardinalityType defaultValue);
	/**
	 * Makes sure the given variant is in the requested format and returns true
	 * if the variant was valid. Logs any error to the given logger instance.
	 * In case the validation was not successful, but the Argument instance was
	 * given an default value, the variant is set to that default value. If no
	 * default value was given, the variant is set to a valid value of the
	 * requested type.
	 *
	 * @param var is the variant that should be verified and transformed to
	 * match the argument specification.
	 * @param logger is the logger instance to which errors should be written.
	 * @return true if the given variant was valid, false otherwise.
	 */
	bool validate(Variant &var, Logger &logger) const;
	/**
	 * Returns the name of the argument. The name is used for logging and in
	 * case a map is presented as arguments.
	 *
	 * @return the name of the argument given in the constructor.
	 */
	const std::string &getName() const;
	/**
	 * Returns the default value. Note that a value of nullptr does not indicate
	 * that no default value has been set. Use the "hasDefault" flag for this
	 * purpose. Nullptr is a valid value for objects.
	 *
	 * @return the default value that was given in the constructor (may be
	 * nullptr) and nullptr if no default value was given.
	 */
	const Variant& getDefaultValue() const;
	/**
	 * Returns true if a default value was set in the constructor.
	 *
	 * @return true if a default value is set, false otherwise.
	 */
	bool hasDefault() const;
};
/**
 * The Arguments class represents a list of Argument instances and allows to
 * either compare an array or a map of Variant instances against this argument
 * list.
 */
class Arguments {
private:
	/**
	 * List storing all arguments this instance consists of.
	 */
	std::vector arguments;
	/**
	 * Map containing all used argument names.
	 */
	std::unordered_map names;
	/**
	 * Set to true if arguments were explicitly given in the constructor,
	 * false otherwise.
	 */
	bool valid;
public:
	/**
	 * Static Arguments instance with no explicit arguments set.
	 */
	static const Arguments None;
	/**
	 * Default constructor. Provides no arguments.
	 */
	Arguments() : valid(false){};
	/**
	 * Constructor of the Arguments class from a list of Argument instances.
	 *
	 * @param arguments is a list of Argument instances with which the Arguments
	 * instance should be initialized.
	 */
	Arguments(std::initializer_list arguments);
	/**
	 * Checks whether the content of the given variant array matches the
	 * argument list stored in this Arguments instance. Any ommited default
	 * arguments are added to the array.
	 *
	 * @param arr is the variant array that should be validated. The array is
	 * extended by all missing default values. The resulting array is ensured to
	 * be of the correct length and all entries to be of the correct type, even
	 * if validation errors occured (to facilitate graceful degradation).
	 * @param logger is the logger instance to which error messages or warnings
	 * will be written.
	 * @return true if the operation was successful, false if an error occured.
	 */
	bool validateArray(Variant::arrayType &arr, Logger &logger) const;
	/**
	 * Checks whether the content of the given variant map matches the
	 * argument list stored in this Arguments instance. Any ommited default
	 * arguments are added to the map.
	 *
	 * @param map is the variant map that should be validated. The map is
	 * extended by all missing default values. The resulting map is ensured to
	 * be of the correct length and all entries to be of the correct type, even
	 * if validation errors occured (to facilitate graceful degradation).
	 * @param logger is the logger instance to which error messages or warnings
	 * will be written.
	 * @param ignoreUnknown if set to true, unknown map entries are ignored
	 * (a note is issued). This behaviour can be usefull if forward
	 * compatibility must be achieved (such as for XML based formats).
	 * @return true if the operation was successful, false if an error occured.
	 */
	bool validateMap(Variant::mapType &map, Logger &logger,
	                 bool ignoreUnknown = false) const;
};
}
#endif /* _OUSIA_ARGUMENT_HPP_ */