/*! \file pipropertystorage.h
 * \brief Storage of properties for GUI usage
 *
 * This file declare PIPropertyStorage
*/
/*
	PIP - Platform Independent Primitives
	Storage of properties for GUI usage
	Ivan Pelipenko peri4ko@yandex.ru, Andrey Bychkov work.a.b@yandex.ru

	This program is free software: you can redistribute it and/or modify
	it under the terms of the GNU Lesser 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 Lesser General Public License for more details.

	You should have received a copy of the GNU Lesser General Public License
	along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef PIPROPERTYSTORAGE_H
#define PIPROPERTYSTORAGE_H

#include "pivariant.h"

/**
 * @brief Key-value storage, based on PIVector with PIPropertyStorage::Property elements. Each element in vector
 * contains unique name and you can identify property by name with propertyValueByName(), propertyByName().
 * You can add property using addProperty(const Property&), addProperty(const PIString&, const PIVariant&, const PIString&, int).
 */
class PIP_EXPORT PIPropertyStorage {
public:
	PIPropertyStorage() {}

	/**
	 * @brief PIPropertyStorage element.
	 */
	struct PIP_EXPORT Property {
		Property(const PIString & n = PIString(), const PIString & c = PIString(), const PIVariant & v = PIVariant(), int f = 0):
			name(n), comment(c), value(v), flags(f) {}
		Property(const Property & o):
			name(o.name), comment(o.comment), value(o.value), flags(o.flags) {}
		Property(Property && o) {swap(o);}

		Property & operator =(const Property & v) {
			name = v.name;
			comment = v.comment;
			value = v.value;
			flags = v.flags;
			return *this;
		}
		Property & operator =(Property && v) {swap(v); return *this;}


		bool toBool() const {return value.toBool();}
		int toInt() const {return value.toInt();}
		float toFloat() const {return value.toFloat();}
		double toDouble() const {return value.toDouble();}
		PIString toString() const {return value.toString();}

		void swap(Property & o) {
			name.swap(o.name);
			comment.swap(o.comment);
			value.swap(o.value);
			piSwap(flags, o.flags);
		}

		/*! Uniqueue id of property */
		PIString name;

		/*! Optional description of property */
		PIString comment;

		/*! Custom value of property */
		PIVariant value;

		/*! Abstract flags which may be used for user needs */
		int flags;
	};

	PIPropertyStorage(const PIVector<Property> & pl) {props = pl;}

	PIPropertyStorage(PIVector<Property> && pl): props(std::move(pl)) {}

	typedef PIVector<Property>::const_iterator const_iterator;
	typedef PIVector<Property>::iterator iterator;
	typedef Property value_type;

	iterator begin() {return props.begin();}
	const_iterator begin() const {return props.begin();}
	iterator end() {return props.end();}
	const_iterator end() const {return props.end();}

	int length() const {return props.length();}
	int size() const {return props.size();}
	bool isEmpty() const {return props.isEmpty();}
	Property & front() {return props.front();}
	const Property & front() const {return props.front();}
	Property & back() {return props.back();}
	const Property & back() const {return props.back();}
	void removeAt(int i) {props.remove(i);}
	void clear() {props.clear();}

	PIPropertyStorage copy() const {return PIPropertyStorage(*this);}
	int propertiesCount() const {return props.size();}
	PIVector<Property> & properties() {return props;}
	const PIVector<Property> & properties() const {return props;}
	const PIPropertyStorage & propertyStorage() const {return *this;}

	/**
	 * @brief Check if property with \a name exists
	 * @return "true" if property exists
	 */
	bool isPropertyExists(const PIString & _name) const;

	/**
	 * @brief Remove all properties
	 */
	void clearProperties() {props.clear();}

	/**
	 * @brief Add property if name isn't present in storage, otherwrise update existing property with same name.
	 * @return "true" if new property added, else if update existing property return "false"
	 * @param p to copy in storage
	 */
	bool addProperty(const Property & p);
	bool addProperty(Property && p);

	/**
	 * @brief First of all construct Property with method params. After then add property if name isn't present
	 * in storage, otherwrise update existing property with same name.
	 * @return "true" if new property added, else if update existing property return "false"
	 */
	bool addProperty(const PIString & _name, const PIVariant & _def_value, const PIString & _comment = PIString(), int _flags = 0);

	/**
	 * @brief Remove property with \a name
	 * @return "true" if property exists and removed
	 */
	bool removeProperty(const PIString & _name);

	/**
	 * @brief Remove properties wich has \a flag set
	 * @return removed properties count
	 */
	int removePropertiesByFlag(int flag);

	/**
	 * @brief Merge other \a properties_ into this
	 * @param flag_ignore  - properties wich has this flag set will be ignored in merge
	 */
	void updateProperties(const PIVector<Property> & properties_, int flag_ignore = 0);

	/**
	 * @brief Search property by name and return it.
	 *
	 * @param name of property
	 * @return property value or default constructed Property
	 */
	Property propertyByName(const PIString & name) const;

	/**
	 * @brief Search property by name and return property value.
	 *
	 * @param name of property
	 * @return property value or invalid PIVariant if name unknown
	 */
	PIVariant propertyValueByName(const PIString & name) const;

	/**
	 * @brief Set value of property with specific name if name is present in storage.
	 *
	 * @param name of property to set value
	 * @param value to set
	 * @return "true" if property exists and updated
	 */
	bool setPropertyValue(const PIString & name, const PIVariant & value);

	/**
	 * @brief Set comment of property with specific name if name is present in storage.
	 *
	 * @param name of property to set comment
	 * @param comment to set
	 * @return "true" if property exists and updated
	 */
	bool setPropertyComment(const PIString & name, const PIString & comment);

	/**
	 * @brief Set flags of property with specific name if name is present in storage.
	 *
	 * @param name of property to set flags
	 * @param flags to set
	 * @return "true" if property exists and updated
	 */
	bool setPropertyFlags(const PIString & name, int flags);

	PIPropertyStorage & operator <<(const PIPropertyStorage::Property & p) {props << p; return *this;}
	PIPropertyStorage & operator <<(const PIVector<Property> & p) {props << p; return *this;}
	PIPropertyStorage & operator <<(const PIPropertyStorage & p) {props << p.props; return *this;}
	Property & operator[](int i) {return props[i];}
	const Property & operator[](int i) const {return props[i];}
	Property & operator[](const PIString & name);
	const Property operator[](const PIString & name) const;

	static Property parsePropertyLine(PIString l);

protected:
	PIVector<Property> props;

};


inline PIByteArray & operator <<(PIByteArray & s, const PIPropertyStorage::Property & v) {s << v.name << v.value << v.comment << v.flags; return s;}
inline PIByteArray & operator >>(PIByteArray & s, PIPropertyStorage::Property & v) {s >> v.name >> v.value >> v.comment >> v.flags; return s;}

inline PIByteArray & operator <<(PIByteArray & s, const PIPropertyStorage & v) {s << v.properties(); return s;}
inline PIByteArray & operator >>(PIByteArray & s, PIPropertyStorage & v) {s >> v.properties(); return s;}

#endif // PIPROPERTYSTORAGE_H
