/*	Copyright (C) 2003-2008 Free Electron Organization
	Any use of this software requires a license.  If a valid license
	was not distributed with this file, visit freeelectron.org. */

#include "data.pmh"

namespace fe
{
namespace data
{

BinaryWriter::BinaryWriter(sp<Scope> spScope) : Scanner(spScope)
{
}

BinaryWriter::~BinaryWriter(void)
{
}

void BinaryWriter::output(std::ostream &ostrm, sp<RecordGroup> spRG)
{
	String s;
	scan(spRG);
	setupSBIDs(m_spScannedRecords);

	// info
#ifdef DOCCODE
	s = "INFO"; fe::output(ostrm,s);
#endif
	fe::output(ostrm, (U8)e_info);
	writeInfo(ostrm);

	// attributes
	for(unsigned int i = m_sentAttrID; i < m_attrArray.size(); i++)
	{
#ifdef DOCCODE
		s = "ATTRIBUTE"; fe::output(ostrm,s);
#endif
		fe::output(ostrm, (U8)e_attribute);
		write(ostrm, m_attrArray[i]);
	}
	m_sentAttrID = m_attrArray.size();

	// layouts
	for(t_layout_loinfo::iterator it = m_layouts.begin();
		it != m_layouts.end(); it++)
	{
		if(!it->second.m_sent)
		{
#ifdef DOCCODE
			s = "LAYOUT"; fe::output(ostrm,s);
#endif
			fe::output(ostrm, (U8)e_layout);
			write(ostrm, it->first, it->second.m_id);
			it->second.m_sent = true;
		}
	}

	// state blocks
	for(RecordGroup::iterator it = m_spScannedRecords->begin();
		it != m_spScannedRecords->end(); it++)
	{
#ifdef DOCCODE
		s = "STATE"; fe::output(ostrm,s);
#endif
		sp<RecordArray> spRA = *it;
		fe::output(ostrm, (U8)e_state);
		deepwrite(ostrm, spRA);
	}
	m_spScannedRecords->clear();

	// record groups
	for(t_rg_id::iterator it = m_rgs.begin(); it != m_rgs.end(); it++)
	{
#ifdef DOCCODE
		s = "GROUP"; fe::output(ostrm,s);
#endif
		fe::output(ostrm, (U8)e_group);
		write(ostrm, it->first, it->second);
	}

	// record arrays as groups
	for(t_ra_id::iterator it = m_ras.begin(); it != m_ras.end(); it++)
	{
#ifdef DOCCODE
		s = "ARRAY"; fe::output(ostrm,s);
#endif
		fe::output(ostrm, (U8)e_group);
		write(ostrm, it->first, it->second);
	}

	// end
#ifdef DOCCODE
	s = "END"; fe::output(ostrm,s);
#endif
	fe::output(ostrm, (U8)e_end);
	m_nextRGID = 1;
	m_rgs.clear();
	m_ras.clear();
	m_sbs.clear();
}

void BinaryWriter::write(std::ostream &ostrm, sp<RecordGroup> spRG, int id)
{
	// OUTPUT: rg id
	fe::output(ostrm, id);

	U32 total_count = 0;
	for(RecordGroup::iterator it = spRG->begin(); it != spRG->end(); it++)
	{
		sp<RecordArray> spRA = *it;
		total_count += spRA->length();
	}
	// OUTPUT: record count
	fe::output(ostrm, total_count);

	for(RecordGroup::iterator it = spRG->begin(); it != spRG->end(); it++)
	{
		sp<RecordArray> spRA = *it;
		for(int i = 0; i < spRA->length(); i++)
		{
			// OUTPUT: record id
			if(m_sbs.find(spRA->idr(i)) == m_sbs.end())
			{
				feX(e_cannotFind,
					"BinaryWriter::write RG",
					"attempt to write unknown record");
			}
			fe::output(ostrm, m_sbs[spRA->idr(i)]);
		}
	}
}

void BinaryWriter::write(std::ostream &ostrm, sp<RecordArray> spRA, int id)
{
	// OUTPUT: rg id
	fe::output(ostrm, id);

	U32 total_count = spRA->length();

	// OUTPUT: record count
	fe::output(ostrm, total_count);

	for(int i = 0; i < spRA->length(); i++)
	{
		// OUTPUT: record id
		if(m_sbs.find(spRA->idr(i)) == m_sbs.end())
		{
			feX(e_cannotFind,
				"BinaryWriter::write RA",
				"attempt to write unknown record");
		}
		fe::output(ostrm, m_sbs[spRA->idr(i)]);
	}
}

void BinaryWriter::deepwrite(std::ostream &ostrm, sp<RecordArray> spRA)
{
	t_layout_loinfo::iterator it = m_layouts.find(spRA->layout());
	if(it == m_layouts.end())
	{
		feX(e_usage,
			"BinaryWriter::write",
			"layout not scanned properly");
	}


	// OUTPUT: layout id
	fe::output(ostrm, it->second.m_id);

	// OUTPUT: state block count
	fe::output(ostrm, (I32)spRA->length());

	for(int i = 0; i < spRA->length(); i++)
	{
		// OUTPUT: state block
		write(ostrm, spRA->getRecord(i));
	}
}

void BinaryWriter::write(std::ostream &ostrm, Record record)
{
	UWORD cnt = m_spScope->getAttributeCount();
	for(UWORD i = 0; i < cnt; i++)
	{
		if(record.layout()->checkAttribute(i))
		{
			if(m_spScope->attribute(i)->isSerialize())
			{
				sp<BaseType> spBT = m_spScope->attribute(i)->type();
				if(spBT == m_spRecordGroupType)
				{
					sp<RecordGroup> *pspRG;
					void *instance = record.rawAttribute(i);
					pspRG = reinterpret_cast<sp<RecordGroup> *>(instance);

					// OUTPUT: record group id
					if(pspRG->isValid())
					{
						fe::output(ostrm, (U32)getID(*pspRG));
					}
					else
					{
						fe::output(ostrm, (U32)0);
					}
				}
				else if(spBT == m_spRecordArrayType)
				{
					sp<RecordArray> *pspRA;
					void *instance = record.rawAttribute(i);
					pspRA = reinterpret_cast<sp<RecordArray> *>(instance);

					// OUTPUT: record array (group) id
					if(pspRA->isValid())
					{
						fe::output(ostrm, (U32)getID(*pspRA));
					}
					else
					{
						fe::output(ostrm, (U32)0);
					}
				}
				else if(spBT == m_spRecordType)
				{
					Record *pR;
					void *instance = record.rawAttribute(i);
					pR = reinterpret_cast<Record *>(instance);

					// OUTPUT: record id
					if(pR->isValid())
					{
						if(m_sbs.find(pR->idr()) == m_sbs.end())
						{
							fe::output(ostrm, (U32)0);
						}
						fe::output(ostrm, m_sbs[pR->idr()]);
					}
					else
					{
						fe::output(ostrm, (U32)0);
					}
				}
				else if(spBT == m_spWeakRecordType)
				{
					WeakRecord *pR;
					void *instance = record.rawAttribute(i);
					pR = reinterpret_cast<WeakRecord *>(instance);

					// OUTPUT: record id
					if(pR->isValid())
					{
						if(m_sbs.find(pR->idr()) == m_sbs.end())
						{
							fe::output(ostrm, (U32)0);
						}
						fe::output(ostrm, m_sbs[pR->idr()]);
					}
					else
					{
						fe::output(ostrm, (U32)0);
					}
				}
				else if(spBT->getInfo().isValid())
				{
					void *instance = record.rawAttribute(i);

					// OUTPUT: instance data via Info
					spBT->getInfo()->output(ostrm, instance, BaseType::Info::e_binary);
				}
			}
		}
	}
}


void BinaryWriter::write(std::ostream &ostrm, sp<Layout> spLayout, int id)
{
	spLayout->initialize();

	if(spLayout->scope() != m_spScope)
	{
		feX("AsciiWriter::write",
			"multiple scope write note supported");
	}

	// OUTPUT: layout id
	fe::output(ostrm, (U32)id);

	// OUTPUT: layout name
	fe::output(ostrm, spLayout->name());

	U32 attribute_count = 0;
	UWORD cnt = m_spScope->getAttributeCount();
	for(UWORD i = 0; i < cnt; i++)
	{
		if(spLayout->checkAttribute(i))
		{
			sp<BaseType> spBT = m_spScope->attribute(i)->type();
			if(spBT->getInfo().isValid())
			{
				if(m_spScope->attribute(i)->isSerialize())
				{
					attribute_count++;
				}
			}
		}
	}

	// OUTPUT: attribute count
	fe::output(ostrm, attribute_count++);

	for(UWORD i = 0; i < cnt; i++)
	{
		if(spLayout->checkAttribute(i))
		{
			sp<BaseType> spBT = m_spScope->attribute(i)->type();
			if(spBT->getInfo().isValid())
			{
				if(m_spScope->attribute(i)->isSerialize())
				{
					// OUTPUT: attribute id
					U32 attribute_id;
					attribute_id = m_attrs[m_spScope->attribute(i)].m_id;
					fe::output(ostrm, attribute_id);
				}
			}
		}
	}
}

void BinaryWriter::write(std::ostream &ostrm, sp<Attribute> spAttribute)
{
	sp<BaseType> spBT = spAttribute->type();

	// OUTPUT: attribute name
	fe::output(ostrm, spAttribute->name());

	std::list<String> typenames;
	m_spScope->typeMaster()->reverseLookup(spBT, typenames);
	// OUTPUT: typename count
	fe::output(ostrm, (U32)(typenames.size()));

	std::list<String>::iterator it;
	for(it = typenames.begin();it != typenames.end(); it++)
	{
		// OUTPUT: typename
		fe::output(ostrm, *it);
	}
	// OUTPUT: type size
	fe::output(ostrm, (I32)spBT->getInfo()->iosize());
}

void BinaryWriter::writeInfo(std::ostream &ostrm)
{
	// OUTPUT: version
	fe::output(ostrm, FE_SERIAL_VERSION);
}


} /* namespace */
} /* namespace */

