/**
 * Returns template function (function that accepts template parameters and returns a string)
 */
module.exports = (options) => {
    let templateFn = aggregationQueryTemplates[options.placement];
    if (!templateFn) {
        throw new Error("Invalid Aggregation placement: '" + options.placement + "'");
    }
    return templateFn;
};

const SUPPORTED_AGGREGATE_FUNCTIONS = {
    'count': {
        sql: (column_name, params) => `count(${params.aggregated_column || '*'})`
    },
    'avg': {
        sql: (column_name, params) => `avg(${params.aggregated_column || column_name})`
    },
    'sum': {
        sql: (column_name, params) => `sum(${params.aggregated_column || column_name})`
    },
    'min': {
        sql: (column_name, params) => `min(${params.aggregated_column || column_name})`
    },
    'max': {
        sql: (column_name, params) => `max(${params.aggregated_column || column_name})`
    }
};

const aggregateColumns = ctx => {
    let columns = ctx.columns || {};
    if (Object.keys(columns).length === 0) {
        // default aggregation
        columns = {
            _cdb_feature_count: {
                aggregate_function: 'count'
            }
        };
    }
    return Object.keys(columns).map(column_name => {
        const aggregate_function = columns[column_name].aggregate_function || 'count';
        const aggregate_definition = SUPPORTED_AGGREGATE_FUNCTIONS[aggregate_function];
        if (!aggregate_definition) {
            throw new Error("Invalid Aggregate function: '" + aggregate_function + "'");
        }
        const aggregate_expression = aggregate_definition.sql(column_name, columns[column_name]);
        return `${aggregate_expression} AS ${column_name}`;
    }).join(', ');
};

// Notes:
// * ${ctx.res*0.00028/256}*!scale_denominator! is equivalent to
//   ${ctx.res/256}*CDB_XYZ_Resolution(CDB_ZoomFromScale(!scale_denominator!))
// * We need to filter spatially using !bbox! to make the queries efficient because
//   the filter added by Mapnik (wrapping the query)
//   is only applied after the aggregation.
// * This queries are used for rendering and the_geom is omitted in the results for better performance

const aggregationQueryTemplates = {
    'centroid': ctx => `
        WITH _cdb_params AS (
            SELECT
            (${ctx.res*0.00028/256}*!scale_denominator!)::double precision AS res,
            !bbox! AS bbox
        )
        SELECT
            row_number() over() AS cartodb_id,
            ST_SetSRID(
                ST_MakePoint(
                    AVG(ST_X(_cdb_query.the_geom_webmercator)),
                    AVG(ST_Y(_cdb_query.the_geom_webmercator))
                ), 3857
            ) AS the_geom_webmercator,
            ${aggregateColumns(ctx)}
        FROM (${ctx.sourceQuery}) _cdb_query, _cdb_params
        WHERE _cdb_query.the_geom_webmercator && _cdb_params.bbox
        GROUP BY
            Floor(ST_X(_cdb_query.the_geom_webmercator)/_cdb_params.res),
            Floor(ST_Y(_cdb_query.the_geom_webmercator)/_cdb_params.res)
    `,

    'point-grid': ctx => `
        WITH _cdb_params AS (
            SELECT
            (${ctx.res*0.00028/256}*!scale_denominator!)::double precision AS res,
            !bbox! AS bbox
        ),
        _cdb_clusters AS (
            SELECT
            ST_SetSRID(
                ST_MakePoint(
                    AVG(ST_X(_cdb_query.the_geom_webmercator)),
                    AVG(ST_Y(_cdb_query.the_geom_webmercator))
                ),
                3857
            ) AS the_geom_webmercator,
            Floor(ST_X(_cdb_query.the_geom_webmercator)/_cdb_params.res)::int AS _cdb_gx,
            Floor(ST_Y(_cdb_query.the_geom_webmercator)/_cdb_params.res)::int AS _cdb_gy,
            ${aggregateColumns(ctx)}
            FROM (${ctx.sourceQuery}) _cdb_query, _cdb_params
            WHERE the_geom_webmercator && _cdb_params.bbox
            GROUP BY _cdb_gx, _cdb_gy
        )
        SELECT
            ST_SetSRID(ST_MakePoint(_cdb_gx*(res+0.5), _cdb_gy*(res*0.5)), 3857) AS the_geom_webmercator,
            _cdb_feature_count
            FROM _cdb_clusters, _cdb_params
    `,

    'point-sample': ctx => `
        WITH _cdb_params AS (
            SELECT

            (${ctx.res*0.00028/256}*!scale_denominator!)::double precision AS res,
            !bbox! AS bbox
        ), _cdb_clusters AS (
            SELECT
                MIN(cartodb_id) AS cartodb_id,
                ${aggregateColumns(ctx)}
            FROM (${ctx.sourceQuery}) _cdb_query, _cdb_params
            WHERE _cdb_query.the_geom_webmercator && _cdb_params.bbox
            GROUP BY
                Floor(ST_X(_cdb_query.the_geom_webmercator)/_cdb_params.res),
                Floor(ST_Y(_cdb_query.the_geom_webmercator)/_cdb_params.res)
        ) SELECT
            _cdb_clusters.cartodb_id,
            the_geom, the_geom_webmercator,
            _cdb_feature_count
        FROM
            _cdb_clusters INNER JOIN (${ctx.sourceQuery}) _cdb_query
            ON (_cdb_clusters.cartodb_id = _cdb_query.cartodb_id)
    `
};

@@ -3064,36 +3064,32 @@
 grid': ctx =%3E %60%0A
-    
     WITH _cdb_pa
@@ -3102,39 +3102,31 @@
 S (%0A        
-    
 SELECT%0A
-    
         ($%7Bc
@@ -3192,36 +3192,32 @@
 AS res,%0A        
-    
 !bbox! AS bbox%0A 
@@ -3211,28 +3211,24 @@
 ox! AS bbox%0A
-    
     ),%0A     
@@ -3222,28 +3222,24 @@
 %0A    ),%0A    
- 
-   
 _cdb_cluster
@@ -3257,283 +3257,15 @@
     
-    
 SELECT%0A
-            ST_SetSRID(%0A                ST_MakePoint(%0A                    AVG(ST_X(_cdb_query.the_geom_webmercator)),%0A                    AVG(ST_Y(_cdb_query.the_geom_webmercator))%0A                ),%0A                3857%0A            ) AS the_geom_webmercator,%0A    
     
@@ -3346,20 +3346,16 @@
 cdb_gx,%0A
-    
         
@@ -3432,36 +3432,32 @@
 cdb_gy,%0A        
- 
-   
 $%7BaggregateColum
@@ -3465,36 +3465,32 @@
 s(ctx)%7D%0A        
-  
-  
 FROM ($%7Bctx.sour
@@ -3527,28 +3527,24 @@
 ams%0A        
-    
 WHERE the_ge
@@ -3570,36 +3570,32 @@
 cdb_params.bbox%0A
-    
         GROUP BY
@@ -3620,33 +3620,21 @@
     
-    )%0A  
+)%0A
     
-  
 SELECT%0A
-    
     
@@ -3731,36 +3731,32 @@
 rcator,%0A        
-    
 _cdb_feature_cou
@@ -3754,28 +3754,24 @@
 ature_count%0A
-    
         FROM

Fix point-grid aggregation query