//------------------------------------------------------------------------------
// <copyright file="LqrSMACParserFormatter.cpp" 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>
// </summary>
//
// <owner></owner>
//------------------------------------------------------------------------------


#include "LqrSMACParserFormatter.h"
#include <sstream>
#include <string.h>

using namespace std;

namespace LiquidRobotics {
namespace SMACFramework {
namespace PacketParserFormatter {

using namespace Detail;

const string AttributeName_endian 			= "endian";			// default is little
const string AttributeName_name 			= "name";			// mandatory non-blank field
const string AttributeName_position 		= "position";		// default is blank, which indicates sequential assignment
const string AttributeName_size 			= "size";			// default is blank, which defaults to 1 (cannot define size_bytes)
const string AttributeName_size_bytes 		= "size_bytes";		// default is blank, which defaults to 1 (cannot define size)
const string AttributeName_size_max 		= "size_max";		// maximum value of size or size_bytes
const string AttributeName_size_min 		= "size_min";		// minimum value of size or size_bytes
const string AttributeName_type 			= "type";			// mandatory non-blank field
const string AttributeName_within 			= "within";			// default is blank, which indicates not within another field

const string Category_domain				= "Domain";			// Domain { list of DomainMember };
const string Category_domain_member			= "DomainMember";	// DomainMember +name;
const string Category_enumeration			= "Enumeration";	// Enumeration +name +type { list of EnumerationValue }; -> type of data
const string Category_enumeration_value 	= "EnumerationValue"; // EnumerationValue +name +value;
const string Category_field					= "Field";			// Field +name +type ...;
const string Category_format				= "Format";			// Format +name { list of fields }; -> type of data
const string Category_linkage				= "Linkage";		// Linkage +name +type +base +within +selector;

const string EndianTag_big 					= "big";			// if numeric value, sent MSB first
const string EndianTag_little 				= "little";			// if numeric value, sent LSB first (default)

const string PositionTag_begin_offset 		= "begin+";			// indicates an offset from the beginning of the structure
const string PositionTag_end_offset			= "end-";			// indicates an offset from the end of the structure
const string PositionTag_follows 			= "follows:";		// field follows the named field within the structure
const string PositionTag_preceeds 			= "preceeds:";		// field preceeds the named field within the structure

const string SizeTag_linked 				= "linked:";		// size of field is determined by the field
const string SizeType_remaining 			= "remaining";		// the field is dimensioned according to the number of unused bytes that follow

const string TypeName_bit 					= "bit";			// bit field (position is starting bit number, size is number of bits, within must be defined)
const string TypeName_byte 					= "byte";			// byte field (uint8_t, used to define bit fields or general purpose packets)
const string TypeName_char 					= "char";			// character field (int8_t)
const string TypeName_short 				= "short";			// 16-bit integer
const string TypeName_ushort 				= "ushort";			// 16-bit unsigned integer
const string TypeName_long 					= "long";			// 32-bit integer
const string TypeName_ulong 				= "ulong";			// 32-bit unsigned integer
const string TypeName_longlong 				= "longlong";		// 64-bit integer (if supported)
const string TypeName_ulonglong 			= "ulonglong";		// 64-bit unsigned integer (if supported)
const string TypeName_float 				= "float";			// 32-bit floating point
const string TypeName_double 				= "double";			// 64-bit floating point

IMPLEMENT_EXCEPTION(BadAttributeName, std::runtime_error);
IMPLEMENT_EXCEPTION(BadAttributeValue, std::runtime_error);
IMPLEMENT_EXCEPTION(CircularDependency, std::runtime_error);
IMPLEMENT_EXCEPTION(ConfigFileParserError, std::runtime_error);
IMPLEMENT_EXCEPTION(FailedToOpenLexicalAnalyzer, std::runtime_error);
IMPLEMENT_EXCEPTION(FileNotFound, std::runtime_error);
IMPLEMENT_EXCEPTION(ItemIsNotADomain, std::runtime_error);
IMPLEMENT_EXCEPTION(LexicalAnalysisError, std::runtime_error);
IMPLEMENT_EXCEPTION(NoSuchDomainName, std::runtime_error);
IMPLEMENT_EXCEPTION(NoSuchItemName, std::runtime_error);
IMPLEMENT_EXCEPTION(UnexpectedEndOfStream, std::runtime_error);

SHARED_LIB_PUBLIC SystemError::SystemError(const std::string& whatArg, int _errnoSave) throw()
: std::runtime_error(whatArg), errnoSave(_errnoSave)
{
}

SHARED_LIB_PUBLIC string SystemError::DescribeError() const
{
	return DescribeError(errnoSave);
}

SHARED_LIB_PUBLIC string SystemError::DescribeError(int _errno)
{
	char buffer[200];
	strerror_r(_errno, buffer, sizeof(buffer));
	return string(buffer);
}

ParserFormatter::ParserFormatter()
{
}

ParserFormatter::~ParserFormatter()
{
}

void ParserFormatter::Initialize(const string& _configPath)
{
	ConfigStream stm(_configPath);
	ConfigItem item;

	while(item = ConfigItem(), item.Initialize(stm, false))
		cout << item << endl;
}

void ParserFormatter::Clear()
{
	namesToItems.clear();
}

IPFFormatterPtr ParserFormatter::CreateFormatter(const string& itemName)
{
	IPFFormatterPtr result;
	return result;
}

IPFNamedItemPtr ParserFormatter::CreateNamedItem(const string& itemName, const AttributeMap& attributes)
{
	IPFNamedItemPtr result;
	return result;
}

bool ParserFormatter::HasDomain(const string& domainName) const
{
	IPFNamedItemPtrMap::const_iterator found = namesToItems.find(domainName);
	bool nameFound = (found != namesToItems.end());
	bool isDomain = true;

	if (nameFound)
		try						{ dynamic_cast<const IPFDomain*>(found->second.get()); }
		catch(bad_cast& exc)	{ isDomain = false; }

	return nameFound && isDomain;
}

IPFDomain& ParserFormatter::LookupDomain(const string& itemName)
{
	IPFNamedItemPtrMap::iterator found = namesToItems.find(itemName);

	if (found == namesToItems.end())
	{
		stringstream stm;
		stm << "There is no domain name '" << itemName << "' defined";

		throw NoSuchDomainName(stm.str());
	}

	IPFDomain* pDomain = NULL;

	try
	{
		pDomain = dynamic_cast<IPFDomain*>(found->second.get());
	}
	catch(bad_cast& exc)
	{
		stringstream stm;
		stm << "Item '" << itemName << "' is not a domain";

		throw ItemIsNotADomain(stm.str());
	}

	return *pDomain;
}

const IPFDomain& ParserFormatter::LookupDomain(const string& itemName) const
{
	IPFNamedItemPtrMap::const_iterator found = namesToItems.find(itemName);

	if (found == namesToItems.end())
	{
		stringstream stm;
		stm << "There is no domain name '" << itemName << "' defined";

		throw NoSuchDomainName(stm.str());
	}

	const IPFDomain* pDomain = NULL;

	try
	{
		pDomain = dynamic_cast<const IPFDomain*>(found->second.get());
	}
	catch(bad_cast& exc)
	{
		stringstream stm;
		stm << "Item '" << itemName << "' is not a domain";

		throw ItemIsNotADomain(stm.str());
	}

	return *pDomain;
}

IPFFormatterPtr ParserFormatter::Parse(const ByteArray& input, const string& itemName)
{
	IPFFormatterPtr result;
	return result;
}

bool ParserFormatter::Has(const string& itemName) const
{
	return namesToItems.find(itemName) != namesToItems.end();
}

IPFNamedItem& ParserFormatter::Lookup(const string& itemName)
{
	IPFNamedItemPtrMap::iterator found = namesToItems.find(itemName);

	if (found == namesToItems.end())
	{
		stringstream stm;
		stm << "There is no item name '" << itemName << "' defined";

		throw NoSuchDomainName(stm.str());
	}

	return *found->second;
}

const IPFNamedItem& ParserFormatter::Lookup(const string& itemName) const
{
	IPFNamedItemPtrMap::const_iterator found = namesToItems.find(itemName);

	if (found == namesToItems.end())
	{
		stringstream stm;
		stm << "There is no item name '" << itemName << "' defined";

		throw NoSuchDomainName(stm.str());
	}

	return *found->second;
}

}
}
}
