///////////////////////////////////////////////////////////////////////////////
//                                                         
// CegoBTreeObject.cc 
// ------------------
// Cego btree object class implementation
//     
// Design and Implementation by Bjoern Lemke               
//     
// (C)opyright 2000-2013 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: CegoBTreeObject
// 
// Description: 
//
// Status: QG-2.6
//
///////////////////////////////////////////////////////////////////////////////

// cego includes
#include "CegoBTreeObject.h"
#include "CegoXMLHelper.h"
#include "CegoTypeConverter.h"
#include "CegoXMLdef.h"

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

CegoBTreeObject::CegoBTreeObject()
{
}

CegoBTreeObject::CegoBTreeObject(Element *pTO)
{
    putElement(pTO);
}

CegoBTreeObject::CegoBTreeObject(const CegoBTreeObject& oe) : CegoContentObject(oe)
{
    _fileId = oe._fileId;
    _pageId = oe._pageId;
}

CegoBTreeObject::CegoBTreeObject(int tabSetId, CegoObject::ObjectType type, const Chain& objName ) : CegoContentObject(tabSetId, type, objName)
{
    _fileId = 0;
    _pageId = 0;
}

CegoBTreeObject::CegoBTreeObject(int tabSetId, CegoObject::ObjectType type,  const Chain& objName, const ListT<CegoField>& schema, const Chain& tabName) : CegoContentObject(tabSetId, type, objName, tabName, schema)
{
    _fileId = 0;
    _pageId = 0;    
}

CegoBTreeObject::~CegoBTreeObject()
{   
}

void CegoBTreeObject::setSchema(const ListT<CegoField>& schema)
{
    _schema = schema;
}

void CegoBTreeObject::setDataFileId(int fileId)
{
    _fileId = fileId;
}

int CegoBTreeObject::getDataFileId() const
{
    return _fileId;
}

void CegoBTreeObject::setDataPageId(int pageId)
{
    _pageId = pageId;
}

int CegoBTreeObject::getDataPageId() const
{
    return _pageId;
}


int CegoBTreeObject::getEntrySize() const
{
    
    int entrySize = CegoContentObject::getBaseContentSize();

    entrySize = entrySize + sizeof(int); // fileId
    entrySize = entrySize + sizeof(int); // pageId
        
    return entrySize;
}

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

    int entrySize = getEntrySize();

    CegoContentObject::encodeBaseContent(bufPtr, entrySize);
    bufPtr += CegoContentObject::getBaseContentSize();

    memcpy(bufPtr, &_fileId, sizeof(int));
    bufPtr = bufPtr + sizeof(int);
    
    memcpy(bufPtr, &_pageId, sizeof(int));
    bufPtr = bufPtr + sizeof(int);
    
}

void CegoBTreeObject::decode(char *buf)
{
    char* bufPtr = buf;

    int size;

    CegoContentObject::decodeBaseContent(bufPtr, size);
    bufPtr += CegoContentObject::getBaseContentSize();
    
    memcpy(&_fileId, bufPtr, sizeof(int));
    bufPtr = bufPtr + sizeof(int);

    memcpy(&_pageId, bufPtr, sizeof(int));
    bufPtr = bufPtr + sizeof(int);
        
}

bool CegoBTreeObject::isValid()
{
    return  _fileId != 0 || _pageId != 0;
}

CegoBTreeObject& CegoBTreeObject::operator = ( const CegoBTreeObject& oe) 
{

    CegoContentObject::operator=(oe);

    _fileId = oe._fileId;
    _pageId = oe._pageId;

    return (*this);
}

bool CegoBTreeObject::operator == ( const CegoBTreeObject& oe)
{
    return CegoContentObject::operator==(oe);
}


Chain CegoBTreeObject::toChain() const
{
    return getTabName() + Chain("@") + getTableSet() + Chain(" ") + getName();
}

Element* CegoBTreeObject::getElement() const
{
    Element* pRoot = new Element(XML_OBJ_ELEMENT);

    pRoot->setAttribute(XML_TSID_ATTR, Chain(getTabSetId()));
    switch ( getType() )
    {
    case CegoObject::BTREE:
	pRoot->setAttribute(XML_OBJTYPE_ATTR, XML_BTREEOBJ_VALUE);	
	break;
    case CegoObject::PBTREE:
	pRoot->setAttribute(XML_OBJTYPE_ATTR, XML_PBTREEOBJ_VALUE);	
	break;
    case CegoObject::UBTREE:
	pRoot->setAttribute(XML_OBJTYPE_ATTR, XML_UBTREEOBJ_VALUE);	
	break;
    default:
	break;
    }

    pRoot->setAttribute(XML_OBJNAME_ATTR, getName());

    CegoField *pF = _schema.First();
    while ( pF ) 
    {
	Element *pColElement = new Element(XML_SCHEMA_ELEMENT);

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

	pRoot->addContent(pColElement);
	pF = _schema.Next();
    }

    return pRoot;
}

void CegoBTreeObject::putElement(Element* pElement)
{
    Element *pRoot = pElement;
    
    if ( pRoot )
    {
	
	Chain objName = pRoot->getAttributeValue(XML_OBJNAME_ATTR); 
	int tabSetId = pRoot->getAttributeValue(XML_TSID_ATTR).asInteger();
	
	setName(objName);
	setTabName(objName);
	setTabSetId(tabSetId);

	Chain objType = pRoot->getAttributeValue(XML_OBJTYPE_ATTR);
	if ( objType == Chain(XML_BTREEOBJ_VALUE))
	    setType(CegoObject::BTREE);
	else if ( objType == Chain(XML_PBTREEOBJ_VALUE))
	    setType(CegoObject::PBTREE);
	else if ( objType == Chain(XML_UBTREEOBJ_VALUE))
	    setType(CegoObject::UBTREE);

	ListT<Element*> colList = pRoot->getChildren(XML_SCHEMA_ELEMENT);
	
	ListT<CegoField> fl;
	Element **pCol = colList.First();
	while ( pCol ) 
	{
	    Chain colName = (*pCol)->getAttributeValue(XML_COLNAME_ATTR);
	    Chain colType = (*pCol)->getAttributeValue(XML_COLTYPE_ATTR);
	    Chain colSize = (*pCol)->getAttributeValue(XML_COLSIZE_ATTR);

	    Chain colNullable = (*pCol)->getAttributeValue(XML_COLNULLABLE_ATTR);
	    Chain colDefValue = (*pCol)->getAttributeValue(XML_COLDEFVALUE_ATTR);


	    bool isNullable;
	    if ( colNullable == Chain(XML_TRUE_VALUE) ) 
		isNullable = true;
	    else
		isNullable = false;

	    
	    CegoTypeConverter tc;
	    CegoDataType dataType = tc.getTypeId(colType);

	    CegoFieldValue defValue;
	    if ( colDefValue != Chain("") )
	    {
		defValue = CegoFieldValue(dataType, colDefValue);
	    }

	    CegoField f(CegoField(objName, objName, colName, dataType, colSize.asInteger(), defValue, isNullable));		
	    fl.Insert(f);

	    pCol = colList.Next();
	}
	
	setSchema(fl);
    }
}

Chain CegoBTreeObject::getFormatted() const
{

    Chain s;
	
    int maxAttrLen = 12;
    int maxDefLen = 10;

    CegoField* pF = _schema.First();
    while (pF)
    {
	if (maxAttrLen < pF->getAttrName().length())
	    maxAttrLen = pF->getAttrName().length();
	if (maxDefLen < pF->getValue().valAsChain().length())
	    maxDefLen = pF->getValue().valAsChain().length();
	
	pF = _schema.Next();
    }
    
    s += Chain("+") + fill("-", maxAttrLen) + fill(Chain("-"), maxDefLen + 30) + Chain("+\n");
    s += Chain("| ObjectName : ") + getName() + fill(" ", maxAttrLen + maxDefLen + 17 - getName().length()) + Chain("|\n");
	
    s += Chain("| ObjectType : ");
    switch ( getType() )
    {
    case CegoObject::TABLE:
	s += Chain("table          "); 
	break;
    case CegoObject::INDEX:
	s += Chain("index          ");
	break;
    case CegoObject::UINDEX:
	s += Chain("unique index   ");
	break;
    case CegoObject::PINDEX:
	s += Chain("primary index  ");
	break;
    case CegoObject::VIEW:
	s += Chain("view           ");
	break;
    default:
	break;
    }

    s += fill(" ", maxAttrLen + maxDefLen + 1 ) + Chain("|\n");
    
    s += Chain("+-----------") + fill("-", maxAttrLen-10) + Chain("+---------------+") + fill("-", maxDefLen + 1) + Chain("+----------+\n");
    s += Chain("| Attribute ") + fill(" ", maxAttrLen-10) + Chain("| Type          | Default ") + fill(" ", maxDefLen - 8) + Chain("| Nullable |\n");
    s += Chain("+-----------") + fill("-", maxAttrLen-10) + Chain("+---------------+") + fill("-", maxDefLen + 1) + Chain("+----------+\n");
	
    pF = _schema.First();
    while (pF)
    {
	
	int num = maxAttrLen - pF->getAttrName().length() ;
	s += Chain("| ") + pF->getAttrName() + fill(" ", num) + Chain(" |");
	    
	    
	switch (pF->getType())
	{
	case INT_TYPE:
	{
	    s += Chain("  int          |");
	    break;
	}
	case LONG_TYPE:
	{
	    s +=  Chain("  long         |");
	    break;
	}
	case BOOL_TYPE:
	{
	    s += Chain("  bool         |");
	    break;
	}
	case DATETIME_TYPE:
	{
	    s += Chain("  datetime     |");
	    break;
	}
	case VARCHAR_TYPE:
	{
	    Chain l(pF->getLength());
	    s += Chain("  string[") + l + Chain("]") + fill(" ", 6 - l.length()) + Chain("|");
	    break;
	}
	case BIGINT_TYPE:
	{
	    Chain l(pF->getLength());
	    s += Chain("  bigint[") + l + Chain("]") + fill(" ", 6 - l.length()) + Chain("|");
	    break;
	}
	case SMALLINT_TYPE:
	{
	    s += Chain("  smallint     |");
	    break;
	}
	case TINYINT_TYPE:
	{
	    s += Chain("  tinyint      |");
	    break;
	}	
	case DECIMAL_TYPE:
	{
	    Chain l(pF->getLength());
	    s += Chain("  decimal[") + l + Chain("]") + fill(" ", 5 - l.length()) + Chain("|");
	    break;
	}
	case FIXED_TYPE:
	{
	    Chain l(pF->getLength());
	    s += Chain("  fixed[") + l + Chain("]") + fill(" ", 7 - l.length()) + Chain("|");
	    break;
	}
	case FLOAT_TYPE:
	{
	    s += Chain(" float         |");
	    break;
	}
	case DOUBLE_TYPE:
	{
	    s += Chain(" double        |");
	    break;
	}
	case BLOB_TYPE:
	{
	    s += Chain(" blob          |");
	    break;
	}
	default:
	    break;
	}
	
	Chain defVal = pF->getValue().valAsChain();
	
	s += Chain(" ") + defVal + fill(" ", maxDefLen - defVal.length()) + Chain(" |");
	
	if ( pF->isNullable() )
	    s += Chain(" y        |");
	else
	    s += Chain(" n        |");
	
	pF = _schema.Next();
	    
	s += "\n";
    }
    s += Chain("+") + fill("-", maxAttrLen + 1) + Chain("+---------------+") + fill("-", maxDefLen + 1) + Chain("+----------+\n");

    return s;

}

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

    return fs;
}

CegoContentObject* CegoBTreeObject::clone(bool isAttrRef)
{
    CegoBTreeObject *pClone = new CegoBTreeObject(*this);
    return (pClone);
}
