/******************************************************************************/
/* SRD - DATA *****************************************************************/
/******************************************************************************/

#ifndef _H_EBCL_SRDDATA
#define _H_EBCL_SRDDATA
#include <ebcl/Externals.hh>
#include <ebcl/Strings.hh>
#include <ebcl/Types.hh>
namespace ebcl {

class T_Logger;

// Forward declarations
struct T_SRDLocationChaining;
M_CLASS_POINTERS( SRDLocationChaining );
class T_SRDLocation;
M_CLASS_POINTERS( SRDLocation );
class T_SRDToken;
M_CLASS_POINTERS( SRDToken );


/*= SOURCE LOCATIONS =========================================================*/

// E_SRDLocationChaining - Token location chaining circumstances
enum class E_SRDLocationChaining {
	INCLUDED ,
	LOADED ,
	CALLED ,
	GENERATED ,
	SUBSTITUTED ,
	EVALUATED ,
	EXPANDED
};

/*----------------------------------------------------------------------------*/

// T_SRDLocationChaining - Token location chaining structure
struct T_SRDLocationChaining
{
	E_SRDLocationChaining circumstances;
	uint32_t depth;
	SP_SRDLocation location;

	T_SRDLocationChaining(
			E_SRDLocationChaining how ,
			uint32_t depth ,
			SP_SRDLocation const& to ) noexcept;

	bool isGenerated( ) const noexcept;
};

/*----------------------------------------------------------------------------*/

// T_SRDLocation - Token source location information
class T_SRDLocation
{
   private:
	T_String source_;
	uint32_t line_;
	size_t character_;
	T_Optional< T_SRDLocationChaining > chaining_;

   public:
	// SRD locations are pooled
	void* operator new( size_t count ) noexcept;
	void operator delete( void* object ) noexcept;

	// ---------------------------------------------------------------------

	// Unknown location
	T_SRDLocation( ) noexcept;
	// Text input location
	T_SRDLocation( T_String const& source ,
			uint32_t line ,
			size_t character ) noexcept;
	// Binary input location
	T_SRDLocation( T_String const& source ,
			size_t byte ) noexcept;
	// Location of a token
	explicit T_SRDLocation(
			T_SRDToken const& token ) noexcept;

	T_SRDLocation( T_SRDLocation const& other ) noexcept;
	T_SRDLocation& operator= ( T_SRDLocation const& other ) noexcept;

	T_SRDLocation( T_SRDLocation&& other ) noexcept;
	T_SRDLocation& operator= ( T_SRDLocation&& other ) noexcept;

	friend M_DECLARE_SWAP( T_SRDLocation );

	// ---------------------------------------------------------------------

	void chain( E_SRDLocationChaining how ,
			SP_SRDLocation const& to ) noexcept;
	void chain( E_SRDLocationChaining how ,
			uint32_t depth ,
			SP_SRDLocation const& to ) noexcept;
	void clearChain( ) noexcept;

	// ---------------------------------------------------------------------

	bool unknown( ) const noexcept;
	T_String const& source( ) const noexcept;
	bool binary( ) const noexcept;
	uint32_t line( ) const noexcept;
	size_t character( ) const noexcept;
	size_t byte( ) const noexcept;

	bool isChained( ) const noexcept;
	T_SRDLocationChaining const& chaining( ) const noexcept;
};
M_DECLARE_SWAP( T_SRDLocation );
M_LSHIFT_OP( T_StringBuilder , T_SRDLocation const& ) noexcept;


/*= ERRORS ===================================================================*/

// Type of an error record
enum class E_SRDErrorType {
	ERR ,
	WARNING ,
	NOTE
};

// An error that occurred during SRD processing. Also includes warnings and
// notes.
class T_SRDError
{
   private:
	T_String error_;
	T_SRDLocation location_;
	E_SRDErrorType type_;

   public:
	T_SRDError( ) = delete;

	T_SRDError( T_String error ,
			T_SRDLocation location ,
			E_SRDErrorType type = E_SRDErrorType::ERR ) noexcept;

	T_SRDError( T_SRDError const& other ) noexcept;
	T_SRDError& operator= ( T_SRDError const& other ) noexcept;

	T_SRDError( T_SRDError&& other ) noexcept;
	T_SRDError& operator= ( T_SRDError&& other ) noexcept;

	friend M_DECLARE_SWAP( T_SRDError );

	// ---------------------------------------------------------------------

	T_String const& error( ) const noexcept;
	T_SRDLocation const& location( ) const noexcept;
	E_SRDErrorType type( ) const noexcept;
};
M_DECLARE_SWAP( T_SRDError );

/*----------------------------------------------------------------------------*/

// List of errors
class T_SRDErrors
{
   public:
	enum : uint32_t { MAX_ERRORS = 40 };

   private:
	T_AutoArray< T_SRDError , MAX_ERRORS / 2 > errors_;
	uint32_t errCount_ = 0;

   public:
	// ---------------------------------------------------------------------

	template< typename ... ArgTypes >
		void add( char const* error ,
				ArgTypes&&... locationArgs );
	template< typename ... ArgTypes >
		void add( T_String const& error ,
				ArgTypes&&... locationArgs );

	template< typename ... ArgTypes >
		void details( char const* message ,
				ArgTypes&&... locationArgs );
	template< typename ... ArgTypes >
		void details( T_String const& message ,
				ArgTypes&&... locationArgs );

	template< typename ... ArgTypes >
		void add( InPlace , ArgTypes&&... args );
	void add( T_SRDError const& error );
	void add( T_SRDError&& error );

	void addAll( T_SRDErrors const& errors );

	// ---------------------------------------------------------------------

	uint32_t size( ) const noexcept;
	T_SRDError const& operator[] ( uint32_t index ) const noexcept;

	void clear( ) noexcept;

	// ---------------------------------------------------------------------

    private:
	void checkAdded( T_SRDError const& last );
};
M_CLASS_POINTERS( SRDErrors );

/*----------------------------------------------------------------------------*/

// SRD processing exception
class X_SRDErrors : public std::exception
{
   public:
	const T_SRDErrors errors;
	X_SRDErrors( T_SRDErrors const& errors );
	char const * what( ) const noexcept override;
};


/*= TOKENS ===================================================================*/

// E_SRDTokenType - Token types
enum class E_SRDTokenType {
	LIST ,                  // Complete list
	START ,                 // List start
	END ,                   // List end
	WORD ,                  // Word
	VAR ,                   // Variable
	STRING ,                // Quoted string
	BINARY ,		// Binary data
	INT ,                   // Integer (32 bits)
	LONG ,                  // Integer (64 bits)
	FLOAT ,                 // Floating point value
	COMMENT ,               // Commented text
	FLUSH ,			// Special token that causes a parser to flush
};
T_StringBuilder& operator<< ( T_StringBuilder& sb , E_SRDTokenType tt );

// T_SRDList - Token lists
using T_SRDList = T_Array< T_SRDToken >;
M_CLASS_POINTERS( SRDList );

// T_SRDToken - Token data
class T_SRDToken
{
   private:
	using T_BinData_ = T_SharedPtr< T_Buffer< uint8_t > >;

	E_SRDTokenType type_;                   // Token type
	T_String text_;                         // Token's full string
	OP_SRDList list_;                       // List of tokens (type == LIST)
	int64_t longValue_;                     // 64-bit integer value
	double floatValue_;                     // 64-bit floating point value
	T_String stringValue_;                  // String value
	T_BinData_ binary_;			// Binary data

	SP_SRDLocation location_;

	T_SRDToken( ) = default;
	T_SRDToken( const E_SRDTokenType type ) noexcept;

   public:
	// ---------------------------------------------------------------------

	static bool IsWord( T_String const& string );

	// ---------------------------------------------------------------------

	static T_SRDToken ListStart( );
	static T_SRDToken ListEnd( );

	static T_SRDToken List( );
	static T_SRDToken List( T_SRDList const& list );
	static T_SRDToken List( T_SRDList&& list );

	// Either a Word or a String, depending on the contents
	static T_SRDToken AutoText( T_String text );
	static T_SRDToken Word( T_String word );
	static T_SRDToken String( T_String string );

	static T_SRDToken Variable( T_String variable );
	static T_SRDToken Comment( T_String text );

	// Binary data
	static T_SRDToken Binary(
			T_BinData_ const& data ) noexcept;
	template< typename T >
	static T_SRDToken Binary(
			T const* data ,
			uint32_t count ) noexcept;
	template< typename T >
	static T_SRDToken Binary(
			T_Buffer< T > const& data ) noexcept;

	// A Long or an Int, depending on the value
	static T_SRDToken AutoInteger( int64_t value );
	static T_SRDToken Integer( int32_t value );
	static T_SRDToken Long( int64_t value );
	static T_SRDToken Float( double value );

	// A special "flush" token
	static T_SRDToken Flush( ) noexcept;

	// ---------------------------------------------------------------------

	T_SRDToken( T_SRDToken const& other );
	T_SRDToken( T_SRDToken&& other ) noexcept;
	T_SRDToken& operator= ( T_SRDToken const& other );
	T_SRDToken& operator= ( T_SRDToken&& other ) noexcept;

	friend void swap( T_SRDToken& lhs , T_SRDToken& rhs ) noexcept;

	// ---------------------------------------------------------------------

	E_SRDTokenType type( ) const;
	bool isText( ) const;
	bool isNumeric( ) const;
	bool isInteger( ) const;

	T_String const& text( ) const;
	T_SRDList const& list( ) const;
	T_SRDList& list( );
	int64_t longValue( ) const;
	double floatValue( ) const;
	T_String const& stringValue( ) const;
	T_Buffer< uint8_t > const& binary( ) const;

	// ---------------------------------------------------------------------

	int compare( T_SRDToken const& other ) const;
	bool operator ==( T_SRDToken const& other ) const;
	bool operator !=( T_SRDToken const& other ) const;
	bool operator >( T_SRDToken const& other ) const;
	bool operator <( T_SRDToken const& other ) const;
	bool operator >=( T_SRDToken const& other ) const;
	bool operator <=( T_SRDToken const& other ) const;

	// ---------------------------------------------------------------------

	bool hasFullText( ) const;
	T_String fullText( ) const;
	T_SRDToken& setFullText( T_String text );
	T_SRDToken& generateFullText( );

	// ---------------------------------------------------------------------

	bool hasLocation( ) const noexcept;
	T_SRDLocation const& location( ) const noexcept;
	T_SRDLocation& location( ) noexcept;

	T_SRDToken& location( T_String const& source , size_t byte );
	T_SRDToken& location( T_String const& source , uint32_t line , size_t character );
	T_SRDToken& location( T_SRDLocation const& other );
	T_SRDToken& location( T_SRDLocation&& other );
	T_SRDToken& copyLocationOf( T_SRDToken const& other );
};
void swap( T_SRDToken& lhs , T_SRDToken& rhs ) noexcept;
M_DECLARE_COMPARATOR( T_SRDToken );


extern template class T_Array< T_SRDToken >;


} // namespace
#include <ebcl/inline/SRDData.hh>
#endif // _H_EBCL_SRDDATA