/***************************************************************************
                          SIMPLY POWERFUL TOOLKIT (SPTK)
                          CExcelRecord.cpp  -  description
                             -------------------
    begin                : Mon Jan 19 2004
    copyright            : (C) 2000-2005 by Alexey S.Parshin
    email                : alexeyp@gmail.com
 ***************************************************************************/

/***************************************************************************
   This library is free software; you can redistribute it and/or modify it 
   under the terms of the GNU Library General Public License as published by
   the Free Software Foundation; either version 2 of the License, or (at 
   your option) any later version.

   This library 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 Library
   General Public License for more details.

   You should have received a copy of the GNU Library General Public License
   along with this library; if not, write to the Free Software Foundation, 
   Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.

   Please report all bugs and problems to "alexeyp@gmail.com"
 ***************************************************************************/

#include <stdio.h>
#include <math.h>
#include <string.h>
#include <sptk3/sptk-config.h>
#include <sptk3/excel/CExcelRecord.h>

using namespace std;

CExcelRecord::CExcelRecord(short rectype,short reclen,const char *buffer,bool manageMemory)
{
	m_manageMemory = manageMemory;
	m_bytes = reclen;
	m_type  = rectype;
	m_data  = (char *)buffer;
}

void CExcelRecord::append(const char *data,unsigned sz) {
	char *newData = new char[m_bytes+sz+1];
	memcpy(newData,m_data,m_bytes);
	memcpy(newData+m_bytes,data,sz);
	if (m_manageMemory && m_data) delete m_data;
	m_manageMemory = true;
	m_data = newData;
	m_bytes += sz;
}

#if WORDS_BIGENDIAN == 1

short CExcelRecord::reorderBytes(short val) {
	char *bytes = (char *)&val;
	char  b = *bytes;
	*bytes = *(bytes + 1);
	*(bytes + 1) = b;
	return val;
}

unsigned short CExcelRecord::reorderBytes(unsigned short val) {
	char *bytes = (char *)&val;
	char  b = *bytes;
	*bytes = *(bytes + 1);
	*(bytes + 1) = b;
	return val;
}

int CExcelRecord::reorderBytes(int val) {
	char *head = (char *)&val;
	char *tail = head + sizeof(val) - 1;
	for (unsigned i = 0; i < sizeof(val) / 2; i++) {
		char b = *head;
		*head = *tail;
		*tail = b;
		head++;
		tail--;
	}
	return val;
}

unsigned CExcelRecord::reorderBytes(unsigned val) {
	char *head = (char *)&val;
	char *tail = head + sizeof(val) - 1;
	for (unsigned i = 0; i < sizeof(val) / 2; i++) {
		char b = *head;
		*head = *tail;
		*tail = b;
		head++;
		tail--;
	}
	return val;
}

long CExcelRecord::reorderBytes(long val) {
	char *head = (char *)&val;
	char *tail = head + sizeof(val) - 1;
	for (unsigned i = 0; i < sizeof(val) / 2; i++) {
		char b = *head;
		*head = *tail;
		*tail = b;
		head++;
		tail--;
	}
	return val;
}

double CExcelRecord::reorderBytes(double val) {
	char *head = (char *)&val;
	char *tail = head + sizeof(val) - 1;
	for (unsigned i = 0; i < sizeof(val) / 2; i++) {
		char b = *head;
		*head = *tail;
		*tail = b;
		head++;
		tail--;
	}
	return val;
}

#else

inline short CExcelRecord::reorderBytes(short val) {
	return val;
}

inline unsigned short CExcelRecord::reorderBytes(unsigned short val) {
	return val;
}

inline int CExcelRecord::reorderBytes(int val) {
	return val;
}

inline unsigned CExcelRecord::reorderBytes(unsigned val) {
	return val;
}

inline long CExcelRecord::reorderBytes(long val) {
	return val;
}

inline double CExcelRecord::reorderBytes(double val) {
	return val;
}

#endif

unsigned short CExcelRecord::getshort(const char *rec,int offset) {
	unsigned short val;
	memcpy(&val,rec+offset,sizeof(val));
	return reorderBytes(val);
}

void CExcelRecord::setshort(char *rec,int offset,short value) {
	short val = reorderBytes(value);
	memcpy(rec+offset,&val,sizeof(val));
}

unsigned CExcelRecord::getint(const char *rec,int offset) {
	unsigned val;
	memcpy(&val,rec+offset,sizeof(val));
	return reorderBytes(val);
}

void CExcelRecord::setint(char *rec,int offset,unsigned value) {
	unsigned val = reorderBytes(value);
	memcpy(rec+offset,&val,sizeof(val));
}

long CExcelRecord::getlong(const char *rec,int offset) {
	long val;
	memcpy(&val,rec+offset,sizeof(val));
	return reorderBytes(val);
}

void CExcelRecord::setlong(char *rec,int offset,long value) {
	long val = reorderBytes(value);
	memcpy(rec+offset,&val,sizeof(val));
}

double CExcelRecord::getdouble(const char *rec,int offset) {
	double val;
	memcpy(&val,rec+offset,sizeof(val));
	return reorderBytes(val);
}

void CExcelRecord::setdouble(char *rec,int offset,double value) {
	double val = reorderBytes(value);
	memcpy(rec+offset,&val,sizeof(val));
}

double CExcelRecord::RK2double (long rk,CVariantType& vtype) {
	union {
		double	number;
		long		words[2];
	} data;
	if (rk & 0x02) {
      // 30-bit integer number
		double number = (double) (rk >> 2);
		vtype = VAR_INT;
		if (rk & 0x01) number /= 100;
		return number;
	} else {
		long m = long(rk & 0xfffffffc);
#if WORDS_BIGENDIAN == 1
		data.words[0] = m;
		data.words[1] = 0;
#else
      // hiw words of IEEE number
		data.words[0] = 0;
		data.words[1] = m;
#endif
		vtype = VAR_FLOAT;
		if (rk & 0x01) data.number /= 100;
		if (floor(data.number) == data.number)
			vtype = VAR_INT;
		return data.number;
	}
}

bool CExcelRecord::int2RK(int value,int& rkValue) {
	if (value & 0xC0000000)
		return false;
	rkValue = reorderBytes((value << 2) | 0x2);
	return true;
}

bool CExcelRecord::double2RK(double number,int& rkValue) {
#if WORDS_BIGENDIAN == 1
	long *lword = (long *)&number + 1;
	long *hword = (long *)&number;
#else
	long *lword = (long *)&number;
	long *hword = (long *)&number + 1;
#endif
	if (*lword == 0 && (*hword & 0x3) == 0) {
		rkValue = reorderBytes(*hword);
		return true;
	}

	number *= 100;
	if (*lword == 0 && (*hword & 0x3) == 0) {
		rkValue = reorderBytes(*hword | 0x1);
		return true;
	}

	return false;
}

char *CExcelRecord::gettypename(unsigned rectype) {
	switch(rectype) {
		case MS1904:
			return "1904";
		case ADDIN:
			return "ADDIN";
		case ADDMENU:
			return "ADDMENU";
		case ARRAY:
			return "ARRAY";
		case AUTOFILTER:
			return "AUTOFILTER";
		case AUTOFILTERINFO:
			return "AUTOFILTERINFO";
		case BACKUP:
			return "BACKUP";
		case BLANK:
			return "BLANK";
		case BOF:
			return "BOF";
		case BOOKBOOL:
			return "BOOKBOOL";
		case BOOLERR:
			return "BOOLERR";
		case BOTTOMMARGIN:
			return "BOTTOMMARGIN";
		case BOUNDSHEET:
			return "BOUNDSHEET";
		case CALCCOUNT:
			return "CALCCOUNT";
		case CALCMODE:
			return "CALCMODE";
		case CODEPAGE:
			return "CODEPAGE";
		case COLINFO:
			return "COLINFO";
		case CONTINUE:
			return "CONTINUE";
		case COORDLIST:
			return "COORDLIST";
		case COUNTRY:
			return "COUNTRY";
		case CRN:
			return "CRN";
		case DBCELL:
			return "DBCELL";
		case DCON:
			return "DCON";
		case DCONNAME:
			return "DCONNAME";
		case DCONREF:
			return "DCONREF";
		case DEFAULTROWHEIGHT:
			return "DEFAULTROWHEIGHT";
		case DEFCOLWIDTH:
			return "DEFCOLWIDTH";
		case DELMENU:
			return "DELMENU";
		case DELTA:
			return "DELTA";
		case DIMENSIONS:
			return "DIMENSIONS";
		case DOCROUTE:
			return "DOCROUTE";
		case DSF:
			return "DOUBLESTREAMFILE";
		case EDG:
			return "EDG";
		case EXTSST:
			return "EXTSST";
		case MSEOF:
			return "EOF";
		case EXTERNCOUNT:
			return "EXTERNCOUNT";
		case EXTERNNAME:
			return "EXTERNNAME";
		case EXTERNSHEET:
			return "EXTERNSHEET";
		case FILEPASS:
			return "FILEPASS";
		case FILESHARING:
			return "FILESHARING";
		case FILESHARING2:
			return "FILESHARING2"; 
		case FILTERMODE:
			return "FILTERMODE"; 
		case FNGROUPCOUNT:
			return "FNGROUPCOUNT"; 
		case FNGROUPNAME:
			return "FNGROUPNAME";
		case FONT1:
		case FONT2:
			return "FONT";
		case FOOTER:
			return "FOOTER"; 
		case FORMAT:
			return "FORMAT";
		case FORMULA1:
		case FORMULA2:
			return "FORMULA";
		case GCW:
			return "GCW";
		case GRIDSET:
			return "GRIDSET";
		case GUTS:
			return "GUTS";
		case HCENTER:
			return "HCENTER"; 
		case HEADER:
			return "HEADER";
		case HIDEOBJ:
			return "HIDEOBJ";
		case HORIZONTALPAGEBREAKS:
			return "HORIZONTALPAGEBREAKS";
		case IMDATA:
			return "IMDATA";
		case INDEX:
			return "INDEX"; 
		case INTERFACEEND:
			return "INTERFACEEND"; 
		case INTERFACEHDR:
			return "INTERFACEHDR";
		case ITERATION:
			return "ITERATION"; 
		case LABEL:
			return "LABEL"; 
		case LABELSST:
			return "LABELSST";
		case LEFTMARGIN:
			return "LEFTMARGIN"; 
		case LHNGRAPH:
			return "LHNGRAPH";
		case LHRECORD:
			return "LHRECORD"; 
		case LPR:
			return "LPR";
		case MMS:
			return "MMS"; 
		case MSODRAWINGGROUP:
			return "MSODRAWINGGROUP";
		case MSODRAWING:
			return "MSODRAWING";
		case MSODRAWINGSELECTION:
			return "MSODRAWINGSELECTION";
		case MULBLANK:
			return "MULBLANK";
		case MULRK:
			return "MULRK";
		case NAME:
			return "NAME"; 
		case NOTE:
			return "NOTE";
		case NUMBER:
			return "NUMBER"; 
		case OBJ:
			return "OBJ";
		case OBJPROTECT:
			return "OBJPROTECT";
		case OBPROJ:
			return "OBPROJ";
		case OLESIZE:
			return "OLESIZE"; 
		case PALETTE:
			return "PALETTE";
		case PANE:
			return "PANE";
		case PASSWORD:
			return "PASSWORD";
		case PLS:
			return "PLS";
		case PRECISION:
			return "PRECISION";
		case PRINTGRIDLINES:
			return "PRINTGRIDLINES"; 
		case PRINTHEADERS:
			return "PRINTHEADERS"; 
		case PROTECT:
			return "PROTECT";
		case PROT4REV:
			return "PROT4REV";
		case PROT4REVPASS:
			return "PROT4REVPASS";
		case PUB:
			return "PUB"; 
		case RECIPNAME:
			return "RECIPNAME";
		case REFMODE:
			return "REFMODE";
		case REFRESHALL:
			return "REFRESHALL";
		case RIGHTMARGIN:
			return "RIGHTMARGIN";
		case RK:
			return "RK";
		case ROW:
			return "ROW";
		case RSTRING:
			return "RSTRING";
		case SAVERECALC:
			return "SAVERECALC";
		case SCENARIO:
			return "SCENARIO"; 
		case SCENMAN:
			return "SCENMAN"; 
		case SCENPROTECT:
			return "SCENPROTECT"; 
		case SCL:
			return "SCL";
		case SELECTION:
			return "SELECTION"; 
		case SETUP:
			return "SETUP";
		case SHRFMLA:
			return "SHRFMLA";
		case SORT:
			return "SORT"; 
		case SOUND:
			return "SOUND";
		case SST:
			return "SST";
		case STANDARDWIDTH:
			return "STANDARDWIDTH";
		case STRING:
			return "STRING";
		case STYLE:
			return "STYLE";
		case SUB:
			return "SUB";
		case SUPBOOK:
			return "SUPPORTINGBWORKBOOK"; 
		case SXDI:
			return "SXDI"; 
		case SXEXT:
			return "SXEXT";
		case SXIDSTM:
			return "SXIDSTM"; 
		case SXIVD:
			return "SXIVD";
		case SXLI:
			return "SXLI";
		case SXPI:
			return "SXPI";
		case SXSTRING:
			return "SXSTRING"; 
		case SXTBL:
			return "SXTBL"; 
		case SXTBPG:
			return "SXTBPG";
		case SXTBRGIITM:
			return "SXTBRGIITM";
		case SXVD:
			return "SXVD";
		case SXVI:
			return "SXVI"; 
		case SXVIEW:
			return "SXVIEW";
		case SXVS:
			return "SXVS"; 
		case TABID:
			return "TABID";
		case TABIDCONF:
			return "TABIDCONF";
		case TABLE:
			return "TABLE"; 
		case TEMPLATE:
			return "TEMPLATE"; 
		case TOPMARGIN:
			return "TOPMARGIN";
		case UDDESC:
			return "UDDESC"; 
		case UNCALCED:
			return "UNCALCED"; 
		case USESELFS:
			return "USESELFS";
		case VCENTER:
			return "VCENTER";
		case VERTICALPAGEBREAKS:
			return "VERTICALPAGEBREAKS"; 
		case WINDOW1:
			return "WINDOW1";
		case WINDOW2:
			return "WINDOW2";
		case WINDOWPROTECT:
			return "WINDOWPROTECT";
		case WRITEACCESS:
			return "WRITEACCESS"; 
		case WRITEPROT:
			return "WRITEPROT";
		case WSBOOL:
			return "WSBOOL"; 
		case XCT:
			return "XCT"; 
		case XF:
			return "XF";
		default:
			return "UNKNOWN";
	}
}

void CExcelRecordList::clear() {
   for (iterator itor = begin(); itor != end(); itor++)
      delete *itor;
   vector<CExcelRecord *>::clear();
}
