'use strict';

var XLSX = require('xlsx');
var _ = {defaults: require('lodash.defaults'), map: require('lodash.map')};

function datenum(v, date1904) {
  if(date1904) v += 1462;
  var epoch = Date.parse(v);
  return (epoch - new Date(Date.UTC(1899, 11, 30))) / (24 * 60 * 60 * 1000);
}

function sheet_from_array_of_arrays(data) {
  var ws = {};
  var range = {s: {c:10000000, r:10000000}, e: {c:0, r:0}};
  for(var R = 0; R !== data.length; ++R) {
    for(var C = 0; C !== data[R].length; ++C) {
      if(range.s.r > R) range.s.r = R;
      if(range.s.c > C) range.s.c = C;
      if(range.e.r < R) range.e.r = R;
      if(range.e.c < C) range.e.c = C;
      var cell = {v: data[R][C] };
      if(cell.v === null) continue;
      var cell_ref = XLSX.utils.encode_cell({c:C,r:R});

      if(typeof cell.v === 'number') cell.t = 'n';
      else if(typeof cell.v === 'boolean') cell.t = 'b';
      else if(cell.v instanceof Date) {
        cell.t = 'n'; cell.z = XLSX.SSF._table[14];
        cell.v = datenum(cell.v);
      }
      else cell.t = 's';

      ws[cell_ref] = cell;
    }
  }
  if(range.s.c < 10000000) ws['!ref'] = XLSX.utils.encode_range(range);
  return ws;
}

function Workbook() {
  if(!(this instanceof Workbook)) return new Workbook();
  this.SheetNames = [];
  this.Sheets = {};
}

module.exports = {
  parse: function(mixed, options) {
    var ws;
    if(typeof mixed === 'string') ws = XLSX.readFile(mixed, options);
    else ws = XLSX.read(mixed, options);
    return _.map(ws.Sheets, function(sheet, name) {
      return {name: name, data: XLSX.utils.sheet_to_json(sheet, {header: 1, raw: true})};
    });
  },
  build: function(array, options) {
    var defaults = {
      bookType:'xlsx',
      bookSST: false,
      type:'binary'
    };
    var wb = new Workbook();
    array.forEach(function(worksheet) {
      var name = worksheet.name || 'Sheet';
      var data = sheet_from_array_of_arrays(worksheet.data || []);
      wb.SheetNames.push(name);
      wb.Sheets[name] = data;
    });
    var data = XLSX.write(wb, _.defaults(options || {}, defaults));
    if(!data) return false;
    var buffer = new Buffer(data, 'binary');
    return buffer;
  }
};
