/*
	QUERY_TERM.H
	------------
	Copyright (c) 2017 Andrew Trotman
	Released under the 2-clause BSD license (See:https://en.wikipedia.org/wiki/BSD_licenses)
*/
/*!
	@file
	@brief Details of a term from a query
	@author Andrew Trotman
	@copyright 2017 Andrew Trotman
*/
#pragma once

#include <string.h>

#include <sstream>

#include "slice.h"
#include "unicode.h"
#include "allocator.h"

namespace JASS
	{
	/*
		CLASS QUERY_TERM
		----------------
	*/
	/*!
		@brief Details of a query term as parsed from the query.
		@details Objects of this type are generated by the query parsers and used to pass around details
		of a term used in a query
	*/
	class query_term
		{
		friend std::ostream &operator<<(std::ostream &stream, const query_term &term);
		friend class query_term_list;

		private:
			slice term;						///< The term.  Note that the memory is kept elsewhere
			size_t query_frequency;			///< Number of times the term occurs in the query

		public:
			/*
				QUERY_TERM::QUERY_TERM()
				------------------------
			*/
			/*!
				@brief Constructor for an empty object.
			*/
			query_term() :
				query_frequency(0)
				{
				/* Nothing */
				}

			/*
				QUERY_TERM::QUERY_TERM()
				------------------------
			*/
			/*!
				@brief Copy constructor.
				@param original [in] object to copy (note:n This is a shallow copy).
			*/
			query_term(const query_term &original) :
				term(original.term),
				query_frequency(original.query_frequency)
				{
				/* Nothing */
				}

			/*
				QUERY_TERM::QUERY_TERM()
				------------------------
			*/
			/*!
				@brief Constructor.
				@param term [in] Term that this object reprrsents.
				@param query_frequency [in] the number of times the term occurs in the query.
				
				@details Create a new query term object from a string and a frequency.  Node that the term slice is copied and that
				the term is nod duplicated.  That is, the memory containing the query term belongs to the caller and not to this object.
				This isn't a problem because ll memory associated with processing a query should be in a single allocator object.
			*/
			query_term(const slice &term, size_t query_frequency = 1) :
				term(term),
				query_frequency(query_frequency)
				{
				/* Nothing */
				}

			/*
				QUERY_TERM::OPERATOR STD::STRING()
				----------------------------------
			*/
			/*!
				@brief Convert to a std::string.  Useful for serialising.
			*/
			operator std::string() const
				{
				std::ostringstream result;

				result << *this;

				return result.str();
				}

			/*
				QUERY_TERM::TOKEN()
				-------------------
			*/
			/*!
				@brief Return the token as a slice.
				@return The token.
			*/
			slice token(void) const
				{
				return term;
				}

			/*
				QUERY_TERM::FREQUENCY()
				-----------------------
			*/
			/*!
				@brief Return the frequency of this term in this query.
				@return The frequency
			*/
			size_t frequency() const
				{
				return query_frequency;
				}

			/*
				QUERY_TERM::OPERATOR<()
				-----------------------
			*/
			/*!
				@brief Convert to a std::string.  Useful for serialising.
			*/
			bool operator<(const query_term &with) const
				{
				return term.size() < with.term.size() ? true :
					term.size() > with.term.size() ? false :
					memcmp((char *)term.address(), (char *)with.term.address(), term.size()) < 0;
				}

			/*
				QUERY_TERM::UNITTEST()
				----------------------
			*/
			/*!
				@brief Unit test this class
			*/
			static void unittest(void)
				{
				slice text("string");
				query_term first;
				query_term second(text, 2);
				query_term third(second);

				JASS_assert(first.term.address() == NULL);
				JASS_assert(strncmp((char *)second.term.address(), "string", second.term.size()) == 0);
				JASS_assert(second.term.size() == 6);
				JASS_assert(third.query_frequency == second.query_frequency);
				JASS_assert(third.term.address() == second.term.address());

				JASS_assert(static_cast<std::string>(second) == std::string("(string,2)"));

				JASS_assert(::strncmp((char *)second.token().address(), (char *)text.address(), text.size()) == 0);
				puts("query_term::PASSED");
				}
		};

	/*
		OPERATOR<<()
		------------
	*/
	/*!
		@brief Dump the contents of a query_term down an output stream.
		@param stream [in] The stream to write to.
		@param term [in] The query_term to write.
		@return The stream once the slice has been written.
	*/
	inline std::ostream &operator<<(std::ostream &stream, const query_term &term)
		{
		stream << "(" << term.term << "," << term.query_frequency << ")";
		return stream;
		}
	}
