var queue = require('queue-async');
var _ = require('underscore');

function MapConfigOverviewsAdapter(overviewsMetadataBackend, filterStatsBackend) {
    this.overviewsMetadataBackend = overviewsMetadataBackend;
    this.filterStatsBackend = filterStatsBackend;
}

module.exports = MapConfigOverviewsAdapter;

MapConfigOverviewsAdapter.prototype.getMapConfig = function (user, requestMapConfig, params, context, callback) {
    var layers = requestMapConfig.layers;
    var analysesResults = context.analysesResults;

    if (!layers || layers.length === 0) {
        return callback(null, requestMapConfig);
    }

    var augmentLayersQueue = queue(layers.length);

    layers.forEach(function (layer) {
        augmentLayersQueue.defer(
            augmentLayer,
            this.overviewsMetadataBackend,
            this.filterStatsBackend,
            user,
            layer,
            analysesResults
        );
    });

    augmentLayersQueue.awaitAll(function layersAugmentQueueFinish (err, layers) {
        if (err) {
            return callback(err);
        }

        if (!layers || layers.length === 0) {
            return callback(new Error('Missing layers array from layergroup config'));
        }

        requestMapConfig.layers = layers;

        return callback(null, requestMapConfig);
    });
};

function augmentLayer(overviewsMetadataBackend, filterStatsBackend, user, layer, analysesResults, callback) {
    if (layer.type !== 'mapnik' && layer.type !== 'cartodb') {
        return callback(null, layer);
    }

    overviewsMetadataBackend.getOverviewsMetadata(user, layer.options.sql, function (err, metadata) {
        if (err) {
            return callback(err, layer);
        }

        if (_.isEmpty(metadata)) {
            return callback(null, layer);
        }

        var filters = getFilters(analysesResults, layer);

        if (!filters) {
            layer.options = Object.assign({}, layer.options, getQueryRewriteData(layer, analysesResults, {
                overviews: metadata
            }));

            return callback(null, layer);
        }

        var unfilteredQuery = getUnfilteredQuery(analysesResults, layer);

        filterStatsBackend.getFilterStats(user, unfilteredQuery, filters, function (err, stats) {
            if (err) {
                return callback(null, layer);
            }

            layer.options = Object.assign({}, layer.options, getQueryRewriteData(layer, analysesResults, {
                overviews: metadata,
                filter_stats: stats
            }));

            return callback(null, layer);
        });
    });
}

function getRootNode (analysesResults, sourceId) {
    var node = _.find(analysesResults, function (a) {
        return a.rootNode.params.id === sourceId;
    });

    return node ? node.rootNode : undefined;
}

function getFilters (analysesResults, layer) {
    if (layer.options.source && analysesResults && !layer.options.sql_wrap) {
        var sourceId = layer.options.source.id;
        var node = getRootNode(analysesResults, sourceId);

        if (node) {
            return node.getFilters();
        }
    }
}

function getUnfilteredQuery (analysesResults, layer) {
    if (layer.options.source && analysesResults && !layer.options.sql_wrap) {
        var sourceId = layer.options.source.id;
        var node = getRootNode(analysesResults, sourceId);

        if (node) {
            var filters = node.getFilters();
            var filters_disabler = Object.keys(filters).reduce(function (disabler, filter_id) {
                disabler[filter_id] = false;
                return disabler;
            }, {});

            return node.getQuery(filters_disabler);
        }
    }
}

function getQueryRewriteData (layer, analysesResults, extend = {}) {
    var queryRewriteData = {};

    if (layer.options.source && analysesResults && !layer.options.sql_wrap) {
        queryRewriteData.filters = getFilters(analysesResults, layer);
        queryRewriteData.unfiltered_query = getUnfilteredQuery(analysesResults, layer);
    }

    queryRewriteData = Object.assign({}, queryRewriteData, extend);

    return { query_rewrite_data: queryRewriteData };
}

@@ -685,248 +685,102 @@
 ach(
-function (layer) %7B%0A        augmentLayersQueue.defer(%0A            augmentLayer,%0A            this.overviewsMetadataBackend,%0A            this.filterStatsBackend,%0A            user,%0A            layer,%0A            analysesResults%0A        );%0A    %7D
+layer =%3E augmentLayersQueue.defer(this._augmentLayer.bind(this), user, layer, analysesResults)
 );%0A%0A
@@ -1177,76 +1177,70 @@
 %7D;%0A%0A
-function augmentLayer(overviewsMetadataBackend, filterStatsBackend, 
+MapConfigOverviewsAdapter.prototype._augmentLayer = function (
 user
@@ -1385,24 +1385,29 @@
 %0A    %7D%0A%0A    
+this.
 overviewsMet
@@ -1464,25 +1464,16 @@
 ons.sql,
- function
  (err, m
@@ -1480,16 +1480,19 @@
 etadata)
+ =%3E
  %7B%0A     
@@ -2021,24 +2021,29 @@
 );%0A%0A        
+this.
 filterStatsB
@@ -2464,17 +2464,18 @@
     %7D);%0A
-
 %7D
+;
 %0A%0Afuncti

Move function to class as private method