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

using namespace std;
using namespace sptk;

IFMT Biff8formats[MAX_BIFF8FORMAT+1] = {
   { VAR_STRING, 0x0, "" },
   { VAR_INT,    0x1, "0" },
   { VAR_FLOAT,  0x2, "0.0" },
   { VAR_INT,    0x3, "#,##0" },
   { VAR_FLOAT,  0x4, "#,##0.00" },
   { VAR_MONEY,  0x5, "\"$\"#,##0_);\\(\"$\"#,##0\\)" },
   { VAR_MONEY,  0x6, "\"$\"#,##0_);[Red]\\(\"$\"#,##0\\)" },
   { VAR_MONEY,  0x7, "\"$\"#,##0.00_);\\(\"$\"#,##0.00\\)" },
   { VAR_MONEY,  0x8, "\"$\"#,##0.00_);[Red]\\(\"$\"#,##0.00\\)" },
   { VAR_INT,    0x9, "0%" },
   { VAR_FLOAT,  0xA, "0.00%" },
   { VAR_FLOAT,  0xB, "0.00E+00" },
   { VAR_STRING, 0xC, "# ?/?" },
   { VAR_STRING, 0xD, "# \?\?/\?\?" },
   { VAR_DATE,   0xE, "m/d/yy" },
   { VAR_DATE,   0xF, "d-mmm-yy" },
   { VAR_DATE,   0x10,"d-mmm" },
   { VAR_DATE,   0x11,"mmm-yy" },
   { VAR_DATE_TIME, 0x12,"h:mm AM/PM" },
   { VAR_DATE_TIME, 0x13,"h:mm:ss AM/PM" },
   { VAR_DATE_TIME, 0x14,"h:mm" },
   { VAR_DATE_TIME, 0x15,"h:mm:ss" },
   { VAR_DATE_TIME, 0x16,"m/d/yy h:mm" },
   { VAR_STRING, 0x17, "" },
   { VAR_STRING, 0x18, "" },
   { VAR_STRING, 0x19, "" },
   { VAR_STRING, 0x1A, "" },
   { VAR_STRING, 0x1B, "" },
   { VAR_STRING, 0x1C, "" },
   { VAR_STRING, 0x1D, "" },
   { VAR_STRING, 0x1E, "" },
   { VAR_STRING, 0x1F, "" },
   { VAR_STRING, 0x20, "" },
   { VAR_STRING, 0x21, "" },
   { VAR_STRING, 0x22, "" },
   { VAR_STRING, 0x23, "" },
   { VAR_STRING, 0x24, "" },
   { VAR_INT,    0x25,"(#,##0_);(#,##0)" },
   { VAR_INT,    0x26,"(#,##0_);[Red](#,##0)" },
   { VAR_FLOAT,  0x27,"(#,##0.00_);(#,##0.00)" },
   { VAR_FLOAT,  0x28,"(#,##0.00_);[Red](#,##0.00)" },
   { VAR_INT,    0x29,"_(* #,##0_);_(* \\(#,##0\\);_(* \"-\"_);_(@_)" },
   { VAR_MONEY,  0x2A,"_(\"$\"* #,##0_);_(\"$\"* \\(#,##0\\);_(\"$\"* \"-\"_);_(@_)" },
   { VAR_FLOAT,  0x2B,"_(* #,##0.00_);_(* \\(#,##0.00\\);_(* \"-\"??_);_(@_)" },
   { VAR_MONEY,  0x2C,"_(\"$\"* #,##0.00_);_(\"$\"* \\(#,##0.00\\);_(\"$\"* \"-\"??_);_(@_)" },
   { VAR_DATE_TIME, 0x2D,"mm:ss" },
   { VAR_DATE_TIME, 0x2E,"[h]:mm:ss" },
   { VAR_DATE_TIME, 0x2F,"mm:ss.0" },
   { VAR_FLOAT,  0x30,"##0.0E+0" },
   { VAR_STRING, 0x31,"@" }
};

CExcelCellFormat::CExcelCellFormat(
   unsigned _fontIndex, unsigned _formatIndex, unsigned _align, bool _wrap, 
   CExcelBorderLine _border, unsigned _pattern, unsigned _patternForeColor, unsigned _patternBackColor)
{
   m_index = 0;
   m_dataFormat = 0L;
   memset(m_data,0,20);
   fontIndex(_fontIndex);
   formatIndex(_formatIndex);
   align(_align);
   wrap(_wrap);
   border(_border);
   fillPattern(_pattern);
   patternForeColor(_patternForeColor);
   patternBackColor(_patternBackColor);
}

CExcelCellFormat::CExcelCellFormat(const void *XFData) {
   m_index = 0;
   memcpy(m_data,XFData,20);
}

CExcelCellFormat::CExcelCellFormat(const CExcelCellFormat& xfRecord) {
   m_index = xfRecord.m_index;
   memcpy(m_data,xfRecord.m_data,20);
}

void CExcelCellFormat::border(CExcelBorderLine lineType) {
   unsigned b = lineType & 0xF;
   b += b << 4;
   b += b << 8;
   CExcelRecord::setshort(m_data,10,b);
}

void CExcelCellFormat::align(unsigned flags) { 
   flags &= 0x77;
   if (wrap())
      flags |= 0x8;
   CExcelRecord::setshort(m_data,6,flags); 
}

void CExcelCellFormat::wrap(bool _wrap) { 
   unsigned flags = align();
   if (_wrap)
      flags |= 0x8;
   CExcelRecord::setshort(m_data,6,flags); 
}

void CExcelCellFormat::fillPattern(unsigned patternIndex) {
   patternIndex = (patternIndex << 26) & 0xFC000000;
   unsigned attr = CExcelRecord::getint(m_data,14) & 0x1FFFFFF;
   attr |= patternIndex;
   CExcelRecord::setint(m_data,14,attr);
}

unsigned CExcelCellFormat::patternBackColor() const {
   unsigned color = CExcelRecord::getshort(m_data,18);
   return (color & 0x3F80) >> 7;
}

void CExcelCellFormat::patternBackColor(unsigned backColorIndex) {
   unsigned color = CExcelRecord::getshort(m_data,18);
   color = (color & 0x7F) | (backColorIndex << 7);
   CExcelRecord::setshort(m_data,18,color);
}

unsigned CExcelCellFormat::patternForeColor() const {
   unsigned color = CExcelRecord::getshort(m_data,18);
   return color & 0x7F;
}

void CExcelCellFormat::patternForeColor(unsigned foreColorIndex) {
   unsigned color = CExcelRecord::getshort(m_data,18);
   color = (color & 0x3F80) | foreColorIndex;
   CExcelRecord::setshort(m_data,18,color);
}

int CExcelCellFormats::addFont(const char *fontName,unsigned fontHeight,unsigned fontAttr,unsigned charSet) {
   return m_fonts.add(fontName,fontHeight,fontAttr,charSet);
}

int CExcelCellFormats::addFont(unsigned fontDataSize,const char *fontData) {
   return m_fonts.add(fontDataSize,fontData);
}

void CExcelCellFormats::init() {
   clear();

   m_fonts.init();
   /*
   unsigned defaultFormats[8] = { 5, 6, 7, 8, 9, 0x2A, 0x29, 0x2C, 0x2B };
   unsigned i;
   for (i = 0; i < 8; i++) {
      unsigned formatIndex = defaultFormats[i];
      addNumericFormat(Biff8formats[formatIndex].id,Biff8formats[formatIndex].format);
   }
   */

   for (unsigned f = 1; f <= MAX_BIFF8FORMAT; f++) {
      const IFMT& fmt = Biff8formats[f];
      if (fmt.kind != VAR_STRING)
         addNumericFormat(fmt.id,fmt.format);
   }
/*
   CExcelCellFormat *xfrDefault = new CExcelCellFormat(
         0,           // font index
         0,           // numeric format index
         CAF_LEFT,    // align
         CBL_NONE     // border
      );
   insert(xfrDefault);
*/
   char defaultXFdata[20];
   memset(defaultXFdata,0,20);
   CExcelRecord::setshort(defaultXFdata,4,short(0xFFF5));
   CExcelRecord::setshort(defaultXFdata,6,short(0x20));
   CExcelRecord::setshort(defaultXFdata,18,short(0x20C0));

   for (unsigned i = 0; i < 21; i++) {
      switch (i) {
         case 1:  defaultXFdata[0] = 1; 
            defaultXFdata[9] = char(0xF4); 
            break;
         case 3:  defaultXFdata[0] = 2; break;
         case 5:  defaultXFdata[0] = 0; break;
         case 15: defaultXFdata[4] = 1; 
            defaultXFdata[5] = 0; 
            defaultXFdata[9] = 0; 
            break;
         case 16: defaultXFdata[0] = 5; 
            defaultXFdata[2] = char(0x2B); 
            defaultXFdata[4] = char(0xF5); 
            defaultXFdata[5] = char(0xFF); 
            defaultXFdata[9] = char(0xF8); 
            break;
         case 17: defaultXFdata[2] = 0x29; 
            break;
         case 18: defaultXFdata[2] = 0x2C; 
            break;
         case 19: defaultXFdata[2] = 0x2A; 
            break;
         case 20: defaultXFdata[2] = 0x9; 
            break;
      }
      CExcelCellFormat *xfrDefault = new CExcelCellFormat(defaultXFdata);
      insert(xfrDefault);
   }
}

void CExcelCellFormats::addNumericFormat(unsigned numericFormatIndex,std::string numericFormat) {
   CExcelNumericFormat *nf;
   CExcelNumericFormats::iterator itor = m_numericFormats.find(numericFormatIndex);
   if (itor != m_numericFormats.end()) { 
      nf = itor->second;
      nf->format(numericFormat.c_str());
   } else {
      nf = new CExcelNumericFormat(numericFormatIndex,numericFormat);
      m_numericFormats[numericFormatIndex] = nf;
   }
}

CExcelCellFormat *CExcelCellFormats::insert(CExcelCellFormat *xfr) {
   xfr->m_index = size();
   m_formatList.push_back(xfr);
   unsigned formatIndex = xfr->formatIndex();
   if (formatIndex) {
      CExcelNumericFormats::iterator itor = m_numericFormats.find(formatIndex);
      if (itor != m_numericFormats.end())
         xfr->m_dataFormat = itor->second;
      else
         xfr->m_dataFormat = 0L;
   } else {
      xfr->m_dataFormat = 0L;
   }
   return xfr;
}

CExcelCellFormat *CExcelCellFormats::insert(const void *XFData) {
   return insert(new CExcelCellFormat(XFData));
}

CExcelCellFormat *CExcelCellFormats::insert(
   unsigned fontIndex,
   unsigned numericFormatIndex,
   unsigned align,
   bool     wrap,
   CExcelBorderLine border,
   unsigned pattern,
   unsigned patternForeColorIndex,
   unsigned patternBackColorIndex
)
{
   return insert(
         new CExcelCellFormat(
            fontIndex,
            numericFormatIndex,
            align,
            wrap,
            border,
            pattern,
            patternForeColorIndex,
            patternBackColorIndex
      )
   );
}

void CExcelCellFormats::clear() { 
   vector<CExcelCellFormat *>::iterator itor = m_formatList.begin();
   for (; itor != m_formatList.end(); itor++)
      delete *itor;
   m_formatList.clear(); 
   m_fonts.clear();
   m_numericFormats.clear();
}

void CExcelCellFormats::saveRecords(CExcelRecordsBuffer& records) {
   CExcelNumericFormats::iterator numericFormatsItor = m_numericFormats.begin();
   for (; numericFormatsItor != m_numericFormats.end(); numericFormatsItor++) {
      CExcelNumericFormat *nf = numericFormatsItor->second;
      int formatID = nf->index();

      if (formatID < MAX_BIFF8FORMAT) {
         if (formatID < 5 || (formatID > 8 && formatID < 0x29) || formatID > 0x2C)
            continue;
      }

      int formatLength = nf->format().length();
      char *buffer = new char[ formatLength + 5 ];
      CExcelRecord::setshort(buffer,0,formatID);
      buffer[2] = char(formatLength);
      CExcelRecord::setshort(buffer,3,0);
      memcpy(buffer + 5,nf->format().c_str(),formatLength);
      records.append(FORMAT,formatLength + 5,buffer);
      delete buffer;
   }

   unsigned xfCount = m_formatList.size();
   for (unsigned xfIndex = 0; xfIndex < xfCount; xfIndex++) {
      const CExcelCellFormat& xfFormat = m_formatList[xfIndex];
      records.append(XF,20,xfFormat.data());
   }
}
