//-------------------------------------------------------------------------------
// <copyright file="LqrSMACSubseaStationCollection.h" company="LiquidRobotics">
//
// Copyright (c) 2014 Liquid Robotics, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// This software is associated with a release.  Liquid Robotics makes no
// representation that the design, interfaces or component distribution will
// remain unchanged in future releases.
//
// </copyright>
//
// <summary>
//   Declaration of SubseaStationMap class, a serializable entity that acts as an
//   indexed collection of SubseaStation entries.
// </summary>
//
// <owner>Mike Cookson</owner>
//-------------------------------------------------------------------------------

#ifndef __LqrSMACSubseaStationCollection_H__
#define __LqrSMACSubseaStationCollection_H__

#include "LqrSMACSubseaStation.h"
#include "LqrSMACLogger.h"
#include <list>
#include <set>
#include <boost/serialization/set.hpp>
#include <boost/serialization/shared_ptr.hpp>

namespace LiquidRobotics {
namespace SMACFramework {

///
/// SubseaStationCollection is a Boost serializable entity that holds a collection of
/// of SubseaStation entities.  Entries can be added or modified, but members must have
/// a unique name.  Methods allow for the nearest station and all stations in range to
/// be returned.
///
class SHARED_LIB_PUBLIC SubseaStationCollection
{
public:

	typedef std::string string;
	typedef std::list<std::string> StringList;

	/// Class constructor with required parameters
	SubseaStationCollection();

	/// Copy constructor
	SubseaStationCollection(
			const SubseaStationCollection& rhs			/**< [in] instance to be copied */
			);

	/// Assignment operator
	SubseaStationCollection& operator=(
			const SubseaStationCollection& rhs			/**< [in] instance to be copied */
			);

	/// Adds a new station to the list controlled by the collection, throws if the name of the station is duplicated in the existing list
	void AddStation(
			const SubseaStation& newStation,				/**< [in] the new station to be added to the collection */
			bool overwrite = false							/**< [in] true if it's OK to overwrite the station, false if a duplicate name throws */
			);

	/// Removes all stations in the set
	void Clear();

	/// Deletes the named station, throws if the named station does not exist; station names are not case sensitive
	void DeleteStation(
			const string& stationName					/**< [in] the name of the station to be deleted */
			);

	/// Finds the station nearest "currentPosition" and returns the reference of the station; throws if the station list is empty
	const SubseaStation& FindNearestStation(
			const Geodetic::Location& currentPosition	/**< [in] the current latitude and longitude, station distances are measured from this point */
			) const;

	/// Finds the station nearest "currentPosition" and returns the reference of the station; throws if the station list is empty.
	/// This method "only" checks the distance to the nearest station; it does not use angular_limit, watch_circle or slant_range_limit.
	const SubseaStation& FindNearestStation2(
			const Geodetic::Location& currentPosition	/**< [in] the current latitude and longitude, station distances are measured from this point */
			) const;

	/// Finds the group of stations that are in range of "currentPosition" and returns a list of names of the station; the list will be empty if nothing is in range
	StringList FindStationsInRange(
			const Geodetic::Location& currentPosition	/**< [in] the current latitude and longitude, station distances are measured from this point */
			) const;

	/// Find the range to the specified station.
	double RangeToStation (const string station_name);

	/// Updates an existing station in the list with a set of new values; throws if the named station does not exist or if the new station name overwrites another station in the list
	void UpdateStation(
			const string& stationName,					/**< [in] the name of the existing station to be updated */
			const SubseaStation& newStation				/**< [in] the new definition of the station */
			);

	/// Lists the contents of the collection.
	void List (Logger& logger);

	/// Gets the named station, throws if the named station does not exist; station names are not case sensitive
	const SubseaStation& GetStation(
			const string& stationName					/**< [in] the name of the station to be returned */
			);

	static void RunTests();

private:


	/// Class to use as a comparison function for SubseaStationSet
	struct SubseaStationSetComparitor
	{
		/// Functor to compare to entries in SubseaStationSet (performs a caseless "less" function on the name and handles nil pointers)
		bool operator()(const SubseaStation::Ptr& val1, const SubseaStation::Ptr& val2) const;
	};

	typedef std::set<SubseaStation::Ptr, SubseaStationSetComparitor> SubseaStationSet;

	friend class boost::serialization::access;

	template<class Archive>
	void save(Archive& ar, const unsigned int version) const
	{
		ar & BOOST_SERIALIZATION_NVP(stations);				// version 1
	}

	template<class Archive>
	void load(Archive& ar, const unsigned int version)
	{
		ar & BOOST_SERIALIZATION_NVP(stations);				// version 1

		ValidateAll();
	}

	BOOST_SERIALIZATION_SPLIT_MEMBER()

	/// Copies "rhs" to "stations" as a deep copy (doesn't just save a reference)
	void DeepCopyStations(const SubseaStationSet& rhs);

	/// Validates the entire list of stations (verifies the set is valid)
	void ValidateAll() const;

	SubseaStationSet stations;				///< the searchable set of stations
};

}
}

BOOST_CLASS_VERSION(LiquidRobotics::SMACFramework::SubseaStationCollection, 1)

#endif
