/***************************************************************************
                          SIMPLY POWERFUL TOOLKIT (SPTK)
                          CExcelProperty.cpp  -  description
                             -------------------
    begin                : 18 March 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 <sptk3/excel/CExcelRecord.h>
#include <sptk3/excel/CExcelProperty.h>
#include <sptk3/excel/CExcelHeader.h>
#include <sptk3/CException.h>

using namespace std;

CExcelProperty::CExcelProperty(
   std::string aname,unsigned ptype,unsigned nodeColor,
   unsigned previousProperty,unsigned nextProperty,
   unsigned firstChildPropety,unsigned startBlock,
   unsigned fileSize) 
{
   memset(m_data,0,PROPERTY_SIZE);
   unsigned nameLength = aname.length();
   if (ptype) {
      if (nameLength > 0x1F) nameLength = 0x1F;
      CExcelRecord::setshort(m_data,0x40,(nameLength+1)*2);
      for (unsigned i = 0; i < 0x20; i++) {
         m_data[i*2] = aname[i];
         if (aname[i] == 0)
            break;
      }
      m_data[_PROPERTY_TYPE_OFFSET] = char(ptype);
      m_data[_NODE_COLOR_OFFSET] = char(nodeColor);
   }
   CExcelRecord::setint(m_data,_PREVIOUS_PROPERTY_OFFSET,previousProperty);
   CExcelRecord::setint(m_data,_NEXT_PROPERTY_OFFSET,nextProperty);
   CExcelRecord::setint(m_data,_CHILD_PROPERTY_OFFSET,firstChildPropety);
   CExcelRecord::setint(m_data,_START_BLOCK_OFFSET,startBlock);
   CExcelRecord::setint(m_data,_SIZE_OFFSET,fileSize);
}

CExcelProperty::CExcelProperty(const char *data) {
   memcpy(m_data,data,PROPERTY_SIZE);
}

std::string CExcelProperty::name() const {
   char nm[0x20];
   for (unsigned i = 0; i < 0x20; i++) {
      nm[i] = m_data[i*2];
      if (nm[i] == 0)
         break;
   }
   return nm;
}

unsigned CExcelProperty::propertyType() const {
   return (unsigned char) m_data[_PROPERTY_TYPE_OFFSET];
}

unsigned CExcelProperty::firstBATBlock() const {
   return CExcelRecord::getint(m_data,_START_BLOCK_OFFSET);
}

void CExcelProperty::firstBATBlock(unsigned sbo) {
   CExcelRecord::setint(m_data,_START_BLOCK_OFFSET,sbo);
}

unsigned CExcelProperty::fileLength() const {
   return CExcelRecord::getint(m_data,_SIZE_OFFSET);
}

void CExcelProperty::fileLength(unsigned fl) {
   CExcelRecord::setint(m_data,_SIZE_OFFSET,fl);
}

unsigned CExcelProperty::blockCount() const {
   unsigned len = fileLength();
   unsigned cnt = len / BIG_BLOCK_SIZE;
   if (cnt * BIG_BLOCK_SIZE < len) cnt++;
   return cnt;
}

void CExcelProperty::extract(CExcelBAT *batTable,FILE *f) {

   const char *BAT = batTable->batData().data();
   unsigned    position = 0;
   char        buffer[BIG_BLOCK_SIZE];

   unsigned block = firstBATBlock();
   unsigned blockSize = BIG_BLOCK_SIZE;

   unsigned offset = 0;
   if (block == 0) {
      while ((int)CExcelRecord::getint(BAT,offset) < 0) {
         block++;
         offset += 4;
      }
   }

   m_streamData.reset();

   // Walking through the BAT
   while ((int)block >= 0) {

      unsigned newPosition = (block + 1) * blockSize;
      if (position != newPosition) {
         if (fseek(f,newPosition,SEEK_SET) != 0)
            throw CException("Bad block position in Excel stream");
         position = newPosition;
      }

      if (fread(buffer,1,BIG_BLOCK_SIZE,f) != blockSize)
         throw CException("Can't read block from Excel stream");

      m_streamData.append(buffer,blockSize);

      offset = block * 4;

      block = CExcelRecord::getint(BAT,offset);
      position += blockSize;
   }
}
// -----------------------------------------------------------------------------------
void CExcelProperties::clear() {
   for (iterator itor = begin(); itor != end(); itor++)
      delete *itor;
   vector<CExcelProperty *>::clear();
}

void CExcelProperties::save(FILE *f) {
   // Adding several empty propeties to have always the complete blocks
   unsigned originalCount = size();
   while (size() % 4) {
      push_back(new CExcelProperty("",0));
   }

   for (unsigned i = 0; i < size(); i++) {
      CExcelProperty* property = (*this)[i];
      fwrite(property->data(),1,PROPERTY_SIZE,f);
   }

   // Removing artificial empty properties
   while (size() > originalCount) {
      delete (*this)[originalCount];
      erase(begin()+originalCount);
   }
}

unsigned CExcelProperties::blocks() const {
   unsigned blks = size() / 4;
   if (blks * 4 < size()) blks++;
   return blks;
}

void CExcelProperties::extract(CExcelHeader *fileHeader,CExcelBAT *batTable,FILE *f) {
   const char *header = fileHeader->data();
   unsigned propertiesBlockNumber = CExcelRecord::getint(header,_PROPERTY_START_OFFSET);
   int rc = fseek(f,(propertiesBlockNumber+1)*BIG_BLOCK_SIZE,SEEK_SET);
   if (rc)
      throw CException("Bad position for reading property from Excel file");

   clear();
   char readBuffer[BIG_BLOCK_SIZE];
   rc = fread(readBuffer,BIG_BLOCK_SIZE,1,f);
   if (!rc)
      throw CException("Can't read property from Excel file");

   unsigned i = 0;
   for (; i < 4; i++)
      push_back(new CExcelProperty(readBuffer + i * PROPERTY_SIZE));

   // We are only interested in Workbook's data
   // Look for "Workbook" - Excel 97 style
   CExcelProperty *workBookProperty = find("Workbook");

   // Look for "Book" - Excel 95 style
   if (!workBookProperty)
      workBookProperty = find("Book");

   if (!workBookProperty)
      throw CException("Can't find 'Workbook' or 'Book' property.",__FILE__,__LINE__);

   workBookProperty->extract(batTable,f);
}

CExcelProperty *CExcelProperties::find(const char *str) {
   for (unsigned i = 0; i < size(); i++) {
      CExcelProperty* property = (*this)[i];
      if (property->name() == str)
         return property;
   }
   return 0;
}
