Exports a set of dimensions and measures + applies filters

@param options
@param params
@param task
@returns {Task.<Array.<Row>>} a task resolving to the cube rows
function exportMixin(dimensions, measures, filters) {
        
        var app = this;
        var task = new Task();

        promise().then(function() {

            // Start

            task.running('info', 'Starting export!');

        }).then(function() {

            // Get app dimension & measure list

            return promise.all([
                app.getDimensions().then(function(dims) {
                    var dimensionMap = values(mapObj(undef.if(dimensions, {}), function(dimCode, dim) {
                        return [dimCode, {
                            dimensionCode: dimCode,
                            dimensionName: dim.name,
                            dimension: dim,
                            qlikDimension: ((dim.dimensionType == 'MASTER' || dim.dimensionType == 'AUTO') ?
                                dims.filter(function(masterDim) {
                                    return masterDim.qMeta.title == dim.name;
                                }).map(function(item) {
                                    return {
                                        qLabel: dim.name,
                                        qLibraryId: item.qInfo.qId,
                                        qNullSuppression: true
                                    };
                                }) : []).concat((dim.dimensionType == 'FIELD' || dim.dimensionType == 'AUTO') ? [{
                                qDef: {
                                    qFieldDefs: [dim.name]
                                },
                                qNullSuppression: true
                            }] : []).concat((dim.dimensionType == 'IGNORE') ? [{
                                qDef: {
                                    qFieldDefs: ['=null()']
                                },
                                qNullSuppression: false
                            }] : [])
                        }];
                    }));

                    dimensionMap = undef.if(dimensionMap, []);

                    var dimensionNotFound = dimensionMap.filter(function(item) {
                        return item.qlikDimension.length == 0;
                    });

                    if (dimensionNotFound.length != 0) {
                        task.running('dim-not-found', dimensionNotFound);
                        return promise.reject('Dimension(s) not found!');
                    } else {
                        return dimensionMap;
                    }

                }),
                app.getMeasures().then(function(meas) {
                    var measureMap = values(mapObj(undef.if(measures, {}), function(meaCode, mea) {
                        return [meaCode, {
                            measureCode: meaCode,
                            measureName: mea.name,
                            measure: mea,
                            qlikMeasure: ((mea.measureType == 'MASTER' || mea.measureType == 'AUTO') ?
                                meas.filter(function(masterMea) {
                                    return masterMea.qMeta.title == mea.name;
                                }).map(function(item) {
                                    return {
                                        qLabel: mea.name,
                                        qLibraryId: item.qInfo.qId
                                    };
                                }) : []).concat((mea.measureType == 'FIELD' || mea.measureType == 'AUTO') ? [{
                                qDef: {
                                    qDef: mea.formula,
                                    qLabel: mea.name
                                }
                            }] : []).concat((mea.measureType == 'IGNORE') ? [{
                                qDef: {
                                    qDef: '=null()'
                                }
                            }] : [])
                        }];
                    }));

                    measureMap = undef.if(measureMap, []);

                    var measureNotFound = measureMap.filter(function(item) {
                        return item.qlikMeasure.length == 0;
                    });

                    if (measureNotFound.length != 0) {
                        task.running('mea-not-found', measureNotFound);
                        return promise.reject('Measure(s) not found!');
                    } else {
                        return measureMap;
                    }

                })
            ]);

        }).then(function([dimensionMap, measureMap]) {

            // Create cube

            task.running('info', 'Got measures & dimensions from app');

            var cubeDef = {
                qInitialDataFetch: [{qHeight: 0, qWidth: 0}]
            };

            if (dimensionMap.length > 0) {
                cubeDef.qDimensions = dimensionMap.map(function(item) {
                    return item.qlikDimension[0];
                });
            }

            if (measureMap.length > 0) {
                cubeDef.qMeasures = measureMap.map(function(item) {
                    return item.qlikMeasure[0];
                });
            }

            var next = promise();
            if (typeof filters !== 'undefined') {

                next = next.then(function() {
                    return app.clearAll();
                }).then(function() {
                    var step = promise.resolve([]);
                    filters.filter(function(item) {
                        return item.filters.length != 0;
                    }).map(function(filter, index) {
                        
                        function promiseFactory() {
                            return promise().then(function() {
                                return app.getField(filter.field);
                            }).then(function(field) {
                                return field.selectValues(
                                    filter.filters.map(function(item) {
                                        return {qText: item};
                                    }),
                                    false,
                                    false
                                );
                            }).then(function() {
                                return app.createListBox({field: filter.field});
                            });
                        }

                        step = step.then(function(layouts) {
                            return promiseFactory().then(function(layout) {
                                layouts.push(layout);
                                return layouts;
                            });
                        });
                    });

                    return step.then(function(layouts) {
                        var states = layouts.map(function(layout) {
                            return layout.qListObject.qDimensionInfo.qStateCounts.qSelected != 0;
                        });
                        
                        if (!states.every(function(state) { return state; })) {
                            return promise.reject('Not possible to apply desired filters');
                        } else {
                            return promise.resolve('Filtering done');
                        }
                    }).fail(function(err) {
                        task.running('warning', err);
                    });
                });
            }

            return next.then(function() {
                return Rx.Observable.from(app.exportCube(cubeDef)).toPromise(promise.promise);
            });

        }).then(function(reply) {

            // Call callback function

            var pages = reply.map(function(row) {
                return row.map(function(cell) {
                    return cell;
                });
            });

            task.done(pages);
            return pages;

        }).fail(function(err) {

            task.failed(err);
            return promise.reject(err);

        });
        
        if (returnObservable) {
            return task;
        } else {
            return task.toPromise(extPromise);
        }
    }