/***************************************************************************
                          SIMPLY POWERFUL TOOLKIT (SPTK)
                          CExcelRowRecord.cpp  -  description
                             -------------------
    begin                : Mon Jan 19 2004
    copyright            : (C) 2000-2006 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 <string.h>
#include <sptk3/excel/CExcelRow.h>
#include <sptk3/excel/CExcelRowRecord.h>
#include <sptk3/CException.h>

using namespace sptk;

CExcelRowRecord::CExcelRowRecord(unsigned rowNumber,CExcelRow *row) 
: CExcelRecord(ROW,16,new char[17],true) 
{
   m_streamPosition = 0;
   m_firstCellPosition = 0;
   m_firstDefinedColumn = 0;
   m_lastDefinedColumn = 0;

   m_rowNumber = rowNumber;
   m_cells = row;

   memset(m_data,0,m_bytes);

   setshort(m_data,0,rowNumber);             // Row number
   setshort(m_data,6,row->m_height);         // Row height
   setshort(m_data,8,0);                     // irwMac - should be 0
   setshort(m_data,10,0x3054);               // Reserved?
}

void CExcelRowRecord::updateRowRecord(char *recordStreamPtr) {
   if (m_firstDefinedColumn != (unsigned)-1) {
      setshort(recordStreamPtr,6,m_firstDefinedColumn);  // First defined column
      setshort(recordStreamPtr,8,m_lastDefinedColumn+1); // Last defined column + 1
      //unsigned firstCellRelativePos = m_firstCellPosition-m_streamPosition;
      //setshort(m_data,11,firstCellRelativePos); // Relative position of the first cell
   }
}

void CExcelRowRecord::saveRowRecord(CExcelRecordsBuffer& records) {
   m_streamPosition = records.bytes();
   records.append(ROW,bytes(),data());
}

void CExcelRowRecord::saveMultipleRK(CExcelRecordsBuffer& records,char *mulRKBuffer,unsigned firstColumn,unsigned& count) {
   if (count > 1) {
      unsigned recSize = 6 + count * 6;
      CExcelRecord::setshort(mulRKBuffer,recSize - 2,firstColumn+count-1);
      records.append(MULRK,recSize,mulRKBuffer);
   } else {
      records.append(RK,10,mulRKBuffer);
   }
   count = 0;
}

void CExcelRowRecord::saveCellRecords(CExcelRecordsBuffer& records,const CExcelSST& sst,unsigned secondRowStart) {
   m_firstCellPosition = records.bytes();

   unsigned cnt = m_cells->size();

   m_firstDefinedColumn = (unsigned)-1;
   m_lastDefinedColumn = (unsigned)-1;

   double value = 0;
   int    intValue, rkValue = 0;

   int mulRkStartColumn = -1;
   unsigned mulRkCount = 0;
   char *mulRkBuffer = 0L;

   CBuffer buffer(20);

   for (unsigned i = 0; i < cnt; i++) {

      CExcelCell& cell = (*m_cells)[i];
      bool cellIsNotNull = false;

      const CExcelCellFormat *format = cell.format();

      char *ptr = buffer.data();
      setshort(ptr,0,m_rowNumber);     // Row number
      setshort(ptr,2,i);               // Column number

      unsigned formatIndex = 0;      
      if (format)
         formatIndex = format->index(); // Index to the XF record

      setshort(ptr,4,formatIndex);

      unsigned dataType = cell.dataType();

      if (!(dataType&(VAR_INT|VAR_FLOAT)) && mulRkCount) // We have not stored MULRK set
         saveMultipleRK(records,mulRkBuffer,mulRkStartColumn,mulRkCount);

      switch (cell.dataType()) {
         case VAR_NONE:
            break;

         case VAR_STRING:
            {
               const char *s = (*m_cells)[i].getString();
               if (s[0]) {
                  unsigned len = strlen(s);
                  int index = sst.index(s);
                  if (index >= 0) {
                     setint(ptr,6,index);  // Index of the string in SST
                     records.append(LABELSST,10,ptr);
                  } else {
                     buffer.checkSize(len+10);
                     ptr = buffer.data();
                     setshort(ptr,6,len);
                     ptr[8] = 0;
                     memcpy(ptr+8+1,s,len);
                     records.append(LABEL,8+len+1,ptr);
                  }
                  cellIsNotNull = true;
               }
            }
            break;

         case VAR_INT:
            intValue = (*m_cells)[i].getInteger();
            if (CExcelRecord::int2RK(intValue,rkValue)) {
               //memcpy(ptr+6,&rkValue,sizeof(int));       // Integer RK value
               if (!mulRkBuffer) {
                  mulRkBuffer = new char[(cnt-i+1) * 6];
                  setshort(mulRkBuffer,0,m_rowNumber);   // Row number
               }
               if (!mulRkCount) { // We have new potential MULRK set
                  mulRkStartColumn = i;
                  setshort(mulRkBuffer,2,i);             // Column number
               }
               char *p = mulRkBuffer+mulRkCount*6;
               setshort(p,4,formatIndex);
               memcpy(p+6,&rkValue,sizeof(int));
               mulRkCount++;
               //records.append(RK,10,ptr);
            } else {
               if (mulRkCount) // We have not stored MULRK set
                  saveMultipleRK(records,mulRkBuffer,mulRkStartColumn,mulRkCount);
               setdouble(ptr,6,(double)intValue);        // Double value
               records.append(NUMBER,14,ptr);
            }
            cellIsNotNull = true;
            break;

         case VAR_FLOAT:
            {
               if (cell.dataType() == VAR_INT)
                  value = (*m_cells)[i].getInteger();
               else value = (*m_cells)[i].getFloat();

               if (CExcelRecord::double2RK(value,rkValue)) {
                  //setint(ptr,6,rkValue);                   // Integer RK value
                  //memcpy(ptr+6,&rkValue,sizeof(int));
                  //records.append(RK,10,ptr);
                  if (!mulRkBuffer) {
                     mulRkBuffer = new char[(cnt-i+1) * 6];
                     setshort(mulRkBuffer,0,m_rowNumber);   // Row number
                  }
                  if (!mulRkCount) { // We have new potential MULRK set
                     mulRkStartColumn = i;
                     setshort(mulRkBuffer,2,i);             // Column number
                  }
                  char *p = mulRkBuffer+mulRkCount*6;
                  setshort(p,4,formatIndex);
                  memcpy(p+6,&rkValue,sizeof(int));
                  mulRkCount++;
               } else {
                  if (mulRkCount) // We have not stored MULRK set
                     saveMultipleRK(records,mulRkBuffer,mulRkStartColumn,mulRkCount);
                  setdouble(ptr,6,value);                     // Double value
                  records.append(NUMBER,14,ptr);
               }
               cellIsNotNull = true;
            }
            break;

         default:
            {
               std::string s = (*m_cells)[i].asString();
               unsigned len = s.length();
               unsigned bsize = len + 9;
               char *ptr = new char[bsize+1];
               setshort(ptr,0,m_rowNumber);     // Row number
               setshort(ptr,2,i);               // Column number
               if (format)
                  setshort(ptr,4,format->index()); // Index to the XF record
               else
                  setshort(ptr,4,0); 
               setshort(ptr,6,len);                        // Length of the string
               ptr[8] = 0;
               memcpy(ptr+9,s.c_str(),len);                // Copy the string itself
               records.append(LABEL,bsize,ptr);
               delete ptr;
               cellIsNotNull = true;
            }
            break;
      }

      if (cellIsNotNull) {
         if (m_firstDefinedColumn == (unsigned)-1)
            m_firstDefinedColumn = i;
         m_lastDefinedColumn = i;
      }
   }
   if (mulRkCount) // We have not stored MULRK set
      saveMultipleRK(records,mulRkBuffer,mulRkStartColumn,mulRkCount);

   updateRowRecord(records.data()+m_streamPosition);
   if (mulRkBuffer)
      delete mulRkBuffer;
}
