require('node-zip');
var fs = require('fs');

Date.prototype.getJulian = function() {
	return Math.floor((this / 86400000) -
	(this.getTimezoneOffset()/1440) + 2440587.5);
};

Date.prototype.oaDate = function() {
 return (this - new Date(Date.UTC(1899,11,30))) / (24 * 60 * 60 * 1000);
};

var templateXLSX = "[encoded data, 8408 characters]";
var sheetFront = '<?xml version="1.0" encoding="utf-8"?><x:worksheet xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet" xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:x="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="x14ac" xmlns:x14ac="http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac"><x:sheetPr><x:outlinePr summaryBelow="1" summaryRight="1" /></x:sheetPr><x:sheetViews><x:sheetView tabSelected="0" workbookViewId="0" /></x:sheetViews><x:sheetFormatPr defaultRowHeight="15" />';
var sheetBack = '<x:printOptions horizontalCentered="0" verticalCentered="0" headings="0" gridLines="0" /><x:pageMargins left="0.75" right="0.75" top="0.75" bottom="0.5" header="0.5" footer="0.75" /><x:pageSetup paperSize="1" scale="100" pageOrder="downThenOver" orientation="default" blackAndWhite="0" draft="0" cellComments="none" errors="displayed" /><x:headerFooter /><x:tableParts count="0" /></x:worksheet>';


var sharedStringsFront = '<?xml version="1.0" encoding="UTF-8"?><x:sst xmlns:x="http://schemas.openxmlformats.org/spreadsheetml/2006/main" uniqueCount="$count" count="$count">';
var sharedStringsBack = '</x:sst>';
var shareStrings, convertedShareStrings;

exports.executeAsync = function(config, callBack){
	return process.nextTick(function(){
		var r = exports.execute(config);		
		callBack(r);
	});
};

exports.execute = function(config){
	var cols = config.cols,
		data = config.rows,
    colsLength = cols.length,
	  xlsx = new JSZip(templateXLSX, { base64: true, checkCRC32: false }),
		sheet = xlsx.file("xl/worksheets/sheet.xml"),
		sharedStringsXml = xlsx.file("xl/sharedStrings.xml"),
		rows = "",
		row ="",
    colsWidth = "",
    styleIndex,
    k;

  if (config.stylesXmlFile){
    var path = config.stylesXmlFile;
    var styles = null;
    styles = fs.readFileSync(path, 'utf8');
    if (styles){
      xlsx.file("xl/styles.xml", styles);
    }
  }
  
	shareStrings = new Array();
  convertedShareStrings = "";
	//first row for column caption
	row = '<x:row r="1" spans="1:'+ colsLength + '">';
  var colStyleIndex;
	for (k=0; k < colsLength; k++)
	{
    colStyleIndex = cols[k].captionStyleIndex || 0;
		row += addStringCol(getColumnLetter(k+1)+1, cols[k].caption, colStyleIndex);
    if (cols[k].width){
      colsWidth += '<col customWidth = "1" width="' + cols[k].width + '" max = "' + (k+1) +'" min="' + (k+1) +'"/>';
    }
	}
	row += '</x:row>';
	rows += row;	
	
	//fill in data
  var i, j, r, cellData, currRow, cellType,
    dataLength = data.length;
    
	for (i=0;i<dataLength;i++)
	{
		r = data[i], currRow = i+2;
		row = '<x:row r="' + currRow +'" spans="1:'+ colsLength + '">';
		for (j=0; j < colsLength; j++)
		{
      styleIndex = null;
      cellData = r[j];
      cellType = cols[j].type;
      if (typeof cols[j].beforeCellWrite === 'function'){
        var e ={rowNum: currRow, styleIndex: null, cellType: cellType};
        cellData = cols[j].beforeCellWrite(r, cellData, e);
        styleIndex = e.styleIndex || styleIndex;
        cellType = e.cellType;
        delete e;
      }     
			switch(cellType)
			{
				case 'number':
					row += addNumberCol(getColumnLetter(j+1)+currRow, cellData, styleIndex);
					break;
				case 'date':
					row += addDateCol(getColumnLetter(j+1)+currRow, cellData, styleIndex);
					break;
				case 'bool':
					row += addBoolCol(getColumnLetter(j+1)+currRow, cellData, styleIndex);
					break;					
				default:
					row += addStringCol(getColumnLetter(j+1)+currRow, cellData, styleIndex);
			}
		}
		row += '</x:row>';
		rows += row;
	}	
  if (colsWidth !== "")
    sheetFront += '<cols>'+colsWidth+'</cols>';
	xlsx.file(sheet.name, sheetFront + '<x:sheetData>' + rows + '</x:sheetData>' + sheetBack);
  
  
	if (shareStrings.length >0)
	{
		sharedStringsFront = sharedStringsFront.replace(/\$count/g, shareStrings.length);
		xlsx.file(sharedStringsXml.name, (sharedStringsFront + convertedShareStrings + sharedStringsBack));
	}
	var results = xlsx.generate({ base64: false, compression: "DEFLATE" });
	delete xlsx;
	delete shareStrings;
	return results;
};

var startTag = function (obj, tagName, closed){
  var result = "<" + tagName, p;
  for (p in obj){
    result += " " + p + "=" + obj[p];
  }
  if (!closed)
    result += ">";
  else
    result += "/>";
  return result;
};

var endTag = function(tagName){
  return "</" + tagName + ">";
};

var addNumberCol = function(cellRef, value, styleIndex){
  styleIndex = styleIndex || 0;
	if (value===null)
		return "";
	else
		return '<x:c r="'+cellRef+'" s="'+ styleIndex +'" t="n"><x:v>'+value+'</x:v></x:c>';
};

var addDateCol = function(cellRef, value, styleIndex){
  styleIndex = styleIndex || 1;
	if (value===null)
		return "";
	else
		return '<x:c r="'+cellRef+'" s="'+ styleIndex +'" t="n"><x:v>'+value+'</x:v></x:c>';
};

var addBoolCol = function(cellRef, value, styleIndex){
  styleIndex = styleIndex || 0;
	if (value===null)
		return "";
	if (value){
	  value = 1;
	} else
	  value = 0;
	return '<x:c r="'+cellRef+'" s="'+ styleIndex + '" t="b"><x:v>'+value+'</x:v></x:c>';
};
var addStringCol = function(cellRef, value, styleIndex){
  styleIndex = styleIndex || 0;
	if (value===null)
		return "";
  if (typeof value ==='string'){
    value = value.replace(/&/g, "&amp;").replace(/'/g, "&apos;").replace(/>/g, "&gt;").replace(/</g, "&lt;");
  }
  var i = shareStrings.indexOf(value);
	if ( i< 0){
		i = shareStrings.push(value) -1;
    convertedShareStrings = convertedShareStrings+ "<x:si><x:t>"+value+"</x:t></x:si>";
	}
	return '<x:c r="'+cellRef+'" s="'+ styleIndex + '" t="s"><x:v>'+i+'</x:v></x:c>';
};

var getColumnLetter = function(col){
  if (col <= 0)
	throw "col must be more than 0";
  var array = new Array();
  while (col > 0)
  {
	var remainder = col % 26;
	col /= 26;
	col = Math.floor(col);
	if(remainder ===0)
	{
		remainder = 26;
		col--;
	}
	array.push(64 + remainder);
  }
  return String.fromCharCode.apply(null, array.reverse());
};