var nodeExcel = require('excel-export');

var transform = function(json,config) {
    var conf = transform.prepareJson(json,config);
    var result = nodeExcel.execute(conf);
    return result;
};

//get a xls type based on js type
function getType(obj,type) {
    if (type) {
        return type;
    }
    var t = typeof obj;
    switch (t) {
        case 'string':
        case 'number':
            return t;
        case 'boolean':
            return 'bool';
        default:
            return 'string';
    }
}

//get a nested property from a JSON object given its key, i.e 'a.b.c'
function getByString(object, path) {
    path = path.replace(/\[(\w+)\]/g, '.$1'); // convert indexes to properties
    path = path.replace(/^\./, '');           // strip a leading dot
    var a = path.split('.');
    while (a.length) {
        var n = a.shift();
        if (n in object) {
            object = (object[n]==undefined)?null:object[n];
        } else {
            return null;
        }
    }
    return object;
}


//prepare json to be in the correct format for excel-export
transform.prepareJson = function(json,config) {
    var res = {};
    var conf = config||{};
    var jsonArr = [].concat(json);
    var fields = conf.fields || Object.keys(jsonArr[0]||{});
    var types = [];
    if (!(fields instanceof Array)) {
        types = Object.keys(fields).map(function(key) {
            return fields[key];
        });
        fields = Object.keys(fields);
    }
    //cols
    res.cols = fields.map(function(key,i) {
        return {
            caption: key,
            type: getType(jsonArr[0][key],types[i]),
            beforeCellWrite: function(row, cellData, eOpt){
                eOpt.cellType = getType(cellData,types[i]);
                return cellData;
            }
        };
    });
    //rows
    res.rows = jsonArr.map(function(row) {
        return fields.map(function(key) {
            var value = getByString(row,key);
            //stringify objects
            if(value && value.constructor == Object) value = JSON.stringify(value);
            //replace illegal xml characters with a square
            //see http://www.w3.org/TR/xml/#charsets
            //#x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF]
            if (typeof value === 'string') {
                value = value.replace(/[^\u0009\u000A\u000D\u0020-\uD7FF\uE000-\uFFFD\u10000-\u10FFFF]/g,'');
            }
            return value;
        });
    });
    //add style xml if given
    if (conf.style) {
        res.stylesXmlFile = conf.style;
    }
    return res;
};

transform.middleware = function(req,res,next) {
    res.xls = function(fn,data,config) {
        var xls = transform(data,config);
        res.setHeader('Content-Type', 'application/vnd.openxmlformats');
        res.setHeader("Content-Disposition", "attachment; filename=" + fn);
        res.end(xls, 'binary');
    };
    next();
};

module.exports = transform;
