///////////////////////////////////////////////////////////////////////////////
//                                                         
// CegoKeyObject.cc 
// ----------------
// Cego key object entry implementation
//     
// Design and Implementation by Bjoern Lemke               
//     
// (C)opyright 2000-2010 Bjoern Lemke
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2, or (at your option)
// any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; see the file COPYING.  If not, write to
// the Free Software Foundation, 59 Temple Place - Suite 330,
// Boston, MA 02111-1307, USA.
//
// IMPLEMENTATION MODULE
//
// Class: CegoKeyObject
// 
// Description: 
//
// Status: QG-2.6
//
///////////////////////////////////////////////////////////////////////////////

// cego includes
#include "CegoKeyObject.h"
#include "CegoXMLdef.h"
#include "CegoXMLHelper.h"

#include <string.h>
#include <stdlib.h>

CegoKeyObject::CegoKeyObject()
{
}

CegoKeyObject::CegoKeyObject(const CegoKeyObject& ko)  : CegoDecodableObject(ko)
{
    _refTable = ko._refTable;
    _refLen = ko._refLen;
    _keyLen = ko._keyLen;
    _keySchema = ko._keySchema;
    _refSchema = ko._refSchema;
    _tabName = ko._tabName;
}

CegoKeyObject::CegoKeyObject(ObjectType type, const Chain& objName, int tabSetId) : CegoDecodableObject(type, objName, tabSetId)
{
}

CegoKeyObject::CegoKeyObject(int tabSetId, const Chain& fkey, const Chain& tabName, const ListT<CegoField>& keySchema, const Chain& refTable, const ListT<CegoField>& refSchema) : CegoDecodableObject(CegoObject::FKEY, fkey, tabSetId)
{
    _keySchema = keySchema;
    _refTable = refTable;
    _refSchema = refSchema;
    _tabName = tabName;
}

CegoKeyObject::~CegoKeyObject()
{   
}

void CegoKeyObject::setTabName(const Chain& tabName)
{
    _tabName = tabName;
}

const Chain& CegoKeyObject::getTabName() const
{
    return _tabName;
}

void CegoKeyObject::setKeySchema(const ListT<CegoField>& keySchema)
{
    _keySchema = keySchema;
}

const ListT<CegoField>& CegoKeyObject::getKeySchema() const
{
    return _keySchema;
}

void CegoKeyObject::setRefTable(const Chain& refTable)
{
    _refTable = refTable;
}

const Chain& CegoKeyObject::getRefTable() const
{
    return _refTable;
}

void CegoKeyObject::setRefSchema(const ListT<CegoField>& refSchema)
{
    _refSchema = refSchema;
}

const ListT<CegoField>& CegoKeyObject::getRefSchema() const
{
    return _refSchema;
}

int CegoKeyObject::getEntrySize() const
{

    int entrySize = CegoObject::getBaseSize();

    entrySize += 1; // object tabname size
    entrySize += _tabName.length();

    entrySize += 1; // size of keyList
    CegoField* pK = _keySchema.First();
    while (pK)
    {
	// key information
	entrySize += pK->getAttrName().length();
	pK = _keySchema.Next();
    }
    entrySize += _refTable.length();

    entrySize += 1; // size of refList
    CegoField* pR = _refSchema.First();
    while (pR)
    {
	// key information
	entrySize += pR->getAttrName().length();
	pR = _refSchema.Next();
    }

    return entrySize;
}

void CegoKeyObject::encode(char *buf)
{
    char* bufPtr = buf;

    int entrySize = getEntrySize();

    CegoObject::encodeBase(bufPtr, entrySize);
    bufPtr += CegoObject::getBaseSize();

    char c;

    c = (char)_tabName.length();
    memcpy (bufPtr, &c , 1);
    bufPtr++;

    memcpy(bufPtr, (char*)_tabName, _tabName.length());    
    bufPtr=bufPtr + _tabName.length();

    char *keyLenPtr = bufPtr;

    // memcpy (bufPtr, &c , 1);
    bufPtr++;
	
    _keyLen = 0;
    CegoField* pK = _keySchema.First();
    while (pK)
    {
	memcpy(bufPtr, (char*)pK->getAttrName(), pK->getAttrName().length());    
	bufPtr=bufPtr + pK->getAttrName().length();
	_keyLen += pK->getAttrName().length();
	pK = _keySchema.Next();
    }
    
    memcpy(bufPtr, (char*)_refTable, _refTable.length());    
    bufPtr=bufPtr + _refTable. length();
    
    char* refLenPtr = bufPtr;
    // memcpy (bufPtr, &c , 1);
    bufPtr++;

    _refLen = 0;
    CegoField* pR = _refSchema.First();
    while (pR)
    {
	// key information
	memcpy(bufPtr, (char*)pR->getAttrName(), pR->getAttrName().length());    
	bufPtr=bufPtr + pR->getAttrName().length();
	_refLen += pR->getAttrName().length();
	pR = _refSchema.Next();
    }
    
    memcpy (keyLenPtr, &_keyLen , 1);
    memcpy (refLenPtr, &_refLen , 1);
    
}

void CegoKeyObject::decode(char *buf)
{

    char* bufPtr = buf;

    int size;

    CegoObject::decodeBase(bufPtr, size);
    bufPtr += CegoObject::getBaseSize();

    bufPtr++;
    _tabName =  Chain(bufPtr);    
    bufPtr=bufPtr + _tabName.length();
    
    char c;
    memcpy (&c, bufPtr , 1);
    bufPtr++;
    
    int i=0;
    while ( i < c )
    {
	Chain attrName = Chain(bufPtr);
	_keySchema.Insert(CegoField(Chain(), attrName));
	
	bufPtr=bufPtr + attrName.length();
	i=i+attrName.length();
    }
    
    _refTable = Chain(bufPtr);
    bufPtr=bufPtr + _refTable.length();
    
    memcpy (&c, bufPtr , 1);
    bufPtr++;
    
    i=0;
    while ( i < c )
    {
	Chain attrName = Chain(bufPtr);
	_refSchema.Insert(CegoField(Chain(), attrName));
	
	bufPtr=bufPtr + attrName.length();
	i=i+attrName.length();
    }
}

void CegoKeyObject::setEmpty()
{
    _keySchema.Empty();
    _refSchema.Empty();
}

CegoKeyObject& CegoKeyObject::operator = ( const CegoKeyObject& ko)
{
    CegoObject::operator=(ko);

    _refTable = ko._refTable;

    _refLen = ko._refLen;
    _keyLen = ko._keyLen;

    _keySchema = ko._keySchema;
    _refSchema = ko._refSchema;
    _tabName = ko._tabName;

    return (*this);
}

bool CegoKeyObject::operator == ( const CegoKeyObject& oe)
{
    return CegoObject::operator==(oe);
}

Chain CegoKeyObject::toChain() const
{
    Chain s;
    s = CegoObject::toChain();

    s += Chain("TabName: ") + _tabName + Chain("\n");
    s += Chain("Key: ");
    CegoField *pK = _keySchema.First();
    while ( pK ) 
    {
	s += pK->getAttrName();
	pK = _keySchema.Next();
	if ( pK )
	    s += Chain(",");
    }
    s += Chain("\n");
    s += Chain("RefTable: ") + _refTable + Chain("\n");
    s += Chain("Ref: ");
    CegoField *pR = _refSchema.First();
    while ( pR ) 
    {
	s += pR->getAttrName();
	pR = _refSchema.Next();
	if ( pR )
	    s += Chain(",");
    }
    s += Chain("\n");
    s += Chain("EntrySize = ") + Chain(getEntrySize()) + Chain("\n");
    
    return s;
}

Element* CegoKeyObject::getElement() const
{

    Element* pRoot = new Element(XML_OBJ_ELEMENT);

    pRoot->setAttribute(XML_TSID_ATTR, Chain(getTabSetId()));
    pRoot->setAttribute(XML_OBJTYPE_ATTR, XML_FKEYOBJ_VALUE);
    pRoot->setAttribute(XML_OBJNAME_ATTR, getName());

    pRoot->setAttribute(XML_TABLENAME_ATTR, _tabName);
    pRoot->setAttribute(XML_REFTABLENAME_ATTR, _refTable);


    Element *pKSElement = new Element(XML_KEYSCHEMA_ELEMENT);

    CegoField *pK = _keySchema.First();
    while ( pK ) 
    {

	Element *pColElement = new Element(XML_COL_ELEMENT);
	
	CegoXMLHelper xh;
	xh.setColInfo(pColElement, pK);

	pKSElement->addContent(pColElement);
	pK = _keySchema.Next();
    }

    pRoot->addContent(pKSElement);


    Element *pRSElement = new Element(XML_REFSCHEMA_ELEMENT);

    CegoField *pR = _refSchema.First();
    while ( pR ) 
    {
	Element *pColElement = new Element(XML_COL_ELEMENT);

	CegoXMLHelper xh;
	xh.setColInfo(pColElement, pR);

	pRSElement->addContent(pColElement);
	pR = _refSchema.Next();
    }

    pRoot->addContent(pRSElement);

    return pRoot;
}

void CegoKeyObject::putElement(Element* pElement)
{
    Element *pRoot = pElement;
    
    if ( pRoot )
    {
	
	Chain objName = pRoot->getAttributeValue(XML_OBJNAME_ATTR); 
	setName(objName);

	int tabSetId = pRoot->getAttributeValue(XML_TSID_ATTR).asInteger();
	setTabSetId(tabSetId);	

	_tabName = pRoot->getAttributeValue(XML_TABLENAME_ATTR); 
	_refTable = pRoot->getAttributeValue(XML_REFTABLENAME_ATTR); 

	Chain objType = pRoot->getAttributeValue(XML_OBJTYPE_ATTR);
	setType(CegoObject::FKEY);
	
	ListT<Element*> keySchemaList = pRoot->getChildren(XML_KEYSCHEMA_ELEMENT);
	
	ListT<CegoField> kfl;
	Element **pKS = keySchemaList.First();
	if ( pKS ) 
	{
	    
	    ListT<Element*> keyColList = (*pKS)->getChildren(XML_COL_ELEMENT);
	    Element **pCol = keyColList.First();
	    while( pCol ) 
	    {
		CegoXMLHelper xh;
		CegoField f;
		xh.getColInfo(objName, *pCol, f);
		kfl.Insert(f);
		pCol = keyColList.Next();
	    }
	}

	setKeySchema(kfl);

	ListT<Element*> refSchemaList = pRoot->getChildren(XML_REFSCHEMA_ELEMENT);
	
	ListT<CegoField> rfl;
	Element **pRS = refSchemaList.First();
	if ( pRS ) 
	{
	    
	    ListT<Element*> refColList = (*pRS)->getChildren(XML_COL_ELEMENT);
	    Element **pCol = refColList.First();
	    while( pCol ) 
	    {
		CegoXMLHelper xh;
		CegoField f;
		xh.getColInfo(objName, *pCol, f);
		rfl.Insert(f);
		pCol = refColList.Next();
	    }
	}
	
	setRefSchema(rfl);

    }
}


Chain CegoKeyObject::getFormatted() const
{
    Chain s;

    int maxAttrLen1 = 12;
    int maxAttrLen2 = 12;

    CegoField* pK = _keySchema.First();
    while (pK)
    {
	if (maxAttrLen1 < pK->getAttrName().length())
	    maxAttrLen1 = pK->getAttrName().length();
	pK = _keySchema.Next();
    }
    CegoField *pR = _refSchema.First();
    while (pR)
    {
	if (maxAttrLen2 < pR->getAttrName().length())
	    maxAttrLen2 = pR->getAttrName().length();
	pR = _refSchema.Next();
    }

    int maxAttrLen = maxAttrLen1+maxAttrLen2;
    
    s += "+" + fill("-", maxAttrLen + 1) + "+" + "\n";
    s += "| ObjectName : ";
    s += getName();
    s += fill(" ", maxAttrLen - 12 - getName().length()) + "|\n";
	
    s += "| ObjectType : fkey " +  fill(" ", maxAttrLen - 18) + "|\n";


    s += "| Table      : " + _tabName;
    s += fill(" ", maxAttrLen - 12 - _tabName.length()) + "|" + "\n";
    s += "| RefTable   : " + _refTable;
    s += fill(" ", maxAttrLen - 12 -_refTable.length()) + "|" + "\n";

	
    s += "+---------" + fill("-", maxAttrLen1-9)  + "+---------" +  fill("-", maxAttrLen2-9) + "+\n";
    s += "| KeyAttr " +  fill(" ", maxAttrLen1-9) + "| RefAttr " +  fill(" ", maxAttrLen2-9) + "|\n";
    s += "+---------" + fill("-", maxAttrLen1-9) + "+---------" +  fill("-", maxAttrLen2-9) + "+\n";
	
    pK = _keySchema.First();
    pR = _refSchema.First();
    while (pK && pR)
    {
	
	int num = maxAttrLen1 - pK->getAttrName().length() ;

	s += "| " + pK->getAttrName() + fill(" ", num) + "| ";

	num = maxAttrLen2 - pR->getAttrName().length() ;
	s +=  pR->getAttrName() + fill(" ", num) + "|\n";

	pK = _keySchema.Next();
	pR = _refSchema.Next();

    }
    s +="+" + fill("-", maxAttrLen1) + "+" + fill("-", maxAttrLen2) + "+\n";

    
    return s;
}


Chain CegoKeyObject::fill(const Chain& s, int num) const
{
    
    Chain fs = Chain("");
    while (num > 0)
    {
	fs = fs + s;
	num--;
    }

    return fs;
}
