/*	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
{

RecordSB RecordSB::clone(void)
{
	RecordSB r_clone;
	if(!isValid()) { return r_clone; }

	sp<Scope> spScope = m_spLayout->scope();
//	r_clone = spScope->createRecord(m_spLayout);
#if FE_DATA_STORE==FE_SB
	r_clone = spScope->produceRecord(m_spLayout->name());
#else
	feX("fix this");
#endif

	UWORD cnt = spScope->getAttributeCount();
	for(UWORD i = 0; i < cnt; i++)
	{
		LayoutSB::Offset offset = m_spLayout->offsetTable()[i];
		if(offset != LayoutSB::offsetNone)
		{
			sp<Attribute> spAttribute=spScope->attribute(i);
			if(spAttribute->isCloneable())
			{
				sp<BaseType> spBT = spAttribute->type();
				spBT->assign(
					(void *)((char *)(r_clone.data()) + offset),
					(void *)((char *)(data()) + offset));
			}
		}
	}

	return r_clone;
}

typedef std::map< sp<RecordGroup>, sp<RecordGroup> >	t_srcrg_dstrg;
typedef std::map< RecordSB, RecordSB >						t_srcr_dstr;

class RecCloner
{
	public:
		RecCloner(sp<TypeMaster> a_typemaster)
		{
			m_spRGType = a_typemaster->lookupType< sp<RecordGroup> >();
			m_spRecordType = a_typemaster->lookupType<RecordSB>();
		}
		RecordSB			clone(RecordSB &r_src);
		sp<RecordGroup> clone(sp<RecordGroup> rg_src);
	private:
		t_srcrg_dstrg		m_rgs;
		t_srcr_dstr			m_rs;
		sp<BaseType>		m_spRGType;
		sp<BaseType>		m_spRecordType;
};

RecordSB RecCloner::clone(RecordSB &r_src)
{
	RecordSB r_clone;
	if(!r_src.isValid()) { return r_clone; }

	sp<Scope> spScope = r_src.layout()->scope();

	t_srcr_dstr::iterator i_r = m_rs.find(r_src);
	if(i_r != m_rs.end())
	{
		r_clone = i_r->second;
		return r_clone;
	}
	else
	{
#if FE_DATA_STORE==FE_SB
		r_clone = spScope->createRecord(r_src.layout());
#else
		feX("fix this");
#endif
		m_rs[r_src] = r_clone;
	}

	UWORD cnt = spScope->getAttributeCount();
	for(UWORD i = 0; i < cnt; i++)
	{
		if(r_clone.layout()->checkAttribute(i))
		{
			sp<Attribute> spAttribute=spScope->attribute(i);
			sp<BaseType> spBT = spAttribute->type();
			if(spBT == m_spRGType)
			{
				r_clone.accessAttribute< sp<RecordGroup> >(i) =
					r_src.accessAttribute< sp<RecordGroup> >(i);
			}
			else if(spBT == m_spRecordType)
			{
				r_clone.accessAttribute<RecordSB>(i) =
					r_src.accessAttribute<RecordSB>(i);
			}
			else if(spAttribute->isCloneable())
			{
				spBT->assign(
					r_clone.rawAttribute(i),
					r_src.rawAttribute(i));
			}
		}
	}
	return r_clone;
}

sp<RecordGroup> RecCloner::clone(sp<RecordGroup> rg_src)
{
	sp<RecordGroup> rg_clone;
	if(!rg_src.isValid()) { return rg_clone; }

	t_srcrg_dstrg::iterator i_rg = m_rgs.find(rg_src);
	if(i_rg != m_rgs.end())
	{
		rg_clone = i_rg->second;
		return rg_clone;
	}
	else
	{
		rg_clone = new RecordGroup();
		m_rgs[rg_src] = rg_clone;
	}

#if FE_DATA_STORE==FE_SB
	for(RecordGroup::iterator it = rg_src->begin();
		it != rg_src->end(); it++)
	{
		sp<RecordArraySB> spRA(*it);
		for(int i = 0; i < spRA->length(); i++)
		{
			RecordSB r_src = spRA->getRecord(i);
			rg_clone->add(clone(r_src));
		}
	}
#else
	feX("fix this");
#endif
	return rg_clone;
}

#if 0
RecordSB RecordSB::deepclone(void)
{
	RecordSB r_clone;
	if(!isValid()) { return r_clone; }

	RecCloner cloner(m_spLayout->scope()->typeMaster());

	r_clone = cloner.clone(*this);

	return r_clone;
}
#endif

bool RecordSB::extractInstance(Instance &instance, const String &attrName)
{
	UWORD attrIndex;
	FEASSERT(m_spLayout.isValid());
	const sp<Attribute>& rspAttribute =
		m_spLayout->scope()->findAttribute(attrName, attrIndex);
	if(!rspAttribute.isValid())
	{
		return false;
	}

	if(m_spLayout->offsetTable()[attrIndex] == LayoutSB::offsetNone)
	{
		return false;
	}

	const sp<BaseType>& rspBT = rspAttribute->type();

	void *pV =
		(void *)((char *)(data())+m_spLayout->offsetTable()[attrIndex]);

	int *pRefCnt = NULL;
	if(m_spLayout->scope()->refCount().check(*this))
	{
		pRefCnt = &(m_spLayout->scope()->refCount()(*this));
	}

	instance.set(pV, rspBT, pRefCnt);

	return true;
}

String RecordSBInfo::print(void *instance)
{
	if(!instance)
		return String("<error>");

	const RecordSB* pR = (RecordSB*)instance;
	if(!pR->isValid())
		return String("<invalid>");

	String string;
	string.sPrintf("%u %s",pR->idr(),pR->layout().isValid()?
			pR->layout()->name().c_str(): "<invalid layout>");
	return string;
}

IWORD RecordSBInfo::output(std::ostream &ostrm, void *instance, t_serialMode mode)
{
	feX(e_unsupported,
		"RecordInfo::output",
		"record output should be done via fe::Stream");
	return 0;
}

void RecordSBInfo::input(std::istream &istrm, void *instance, t_serialMode mode)
{
	feX(e_unsupported,
		"RecordInfo::input",
		"record input should be done via fe::Stream");
}

IWORD RecordSBInfo::iosize(void)
{
	return c_implicit;
}

bool RecordSBInfo::getConstruct(void)
{
	return true;
}

void RecordSBInfo::construct(void *instance)
{
	new(instance)RecordSB;
}

void RecordSBInfo::destruct(void *instance)
{
	((RecordSB *)instance)->~RecordSB();
}

} /* namespace */

