require('../../../support/test_helper');

var assert        = require('../../../support/assert');
var TestClient = require('../../../support/test-client');

describe('widgets', function() {

    describe('histograms', function() {

        afterEach(function(done) {
            if (this.testClient) {
                this.testClient.drain(done);
            } else {
                done();
            }
        });

        function histogramsMapConfig(widgets) {
            return {
                version: '1.5.0',
                layers: [
                    {
                        type: 'mapnik',
                        options: {
                            sql: 'select * from populated_places_simple_reduced',
                            cartocss: '#layer0 { marker-fill: red; marker-width: 10; }',
                            cartocss_version: '2.0.1',
                            widgets: widgets || {
                                scalerank: {
                                    type: 'histogram',
                                    options: {
                                        column: 'scalerank'
                                    }
                                },
                                pop_max: {
                                    type: 'histogram',
                                    options: {
                                        column: 'pop_max'
                                    }
                                }
                            }
                        }
                    }
                ]
            };
        }

        it('can be fetched from a valid histogram', function(done) {
            this.testClient = new TestClient(histogramsMapConfig());
            this.testClient.getWidget('scalerank', function (err, res, histogram) {
                assert.ok(!err, err);
                assert.ok(histogram);
                assert.equal(histogram.type, 'histogram');
                validateHistogramBins(histogram);

                assert.ok(histogram.bins.length);

                assert.deepEqual(histogram.bins[0], { bin: 0, freq: 179, min: 1, max: 1, avg: 1 });

                done();
            });
        });

        it('can be fetched from a valid histogram', function(done) {
            this.testClient = new TestClient(histogramsMapConfig());
            this.testClient.getWidget('pop_max', function (err, res, histogram) {
                assert.ok(!err, err);
                assert.ok(histogram);
                assert.equal(histogram.type, 'histogram');
                validateHistogramBins(histogram);

                assert.ok(histogram.bins.length);

                assert.deepEqual(
                    histogram.bins[histogram.bins.length - 1],
                    { bin: 47, freq: 1, min: 35676000, max: 35676000, avg: 35676000 }
                );

                done();
            });
        });

        it('can be fetched from a valid filtered histogram', function(done) {
            this.testClient = new TestClient(histogramsMapConfig());
            var popMaxFilter = {
                pop_max: {
                    min: 1e5,
                    max: 1e7
                }
            };
            var params = {
                own_filter: 1,
                filters: {
                    layers: [popMaxFilter]
                }
            };
            this.testClient.getWidget('pop_max', params, function (err, res, histogram) {
                assert.ok(!err, err);
                assert.ok(histogram);
                assert.equal(histogram.type, 'histogram');
                validateHistogramBins(histogram);

                assert.ok(histogram.bins.length);

                assert.deepEqual(
                    histogram.bins[histogram.bins.length - 1],
                    { bin: 7, min: 8829000, max: 9904000, avg: 9340914.714285715, freq: 7 }
                );

                done();
            });
        });

        it('returns array with freq=0 entries for empty bins', function(done) {
            var histogram20binsMapConfig = {
                version: '1.5.0',
                layers: [
                    {
                        type: 'mapnik',
                        options: {
                            sql: 'select * from populated_places_simple_reduced',
                            cartocss: '#layer0 { marker-fill: red; marker-width: 10; }',
                            cartocss_version: '2.0.1',
                            widgets: {
                                pop_max: {
                                    type: 'histogram',
                                    options: {
                                        column: 'pop_max'
                                    }
                                }
                            }
                        }
                    }
                ]
            };

            this.testClient = new TestClient(histogram20binsMapConfig);
            this.testClient.getWidget('pop_max', { start: 0, end: 35676000, bins: 20 }, function (err, res, histogram) {
                assert.ok(!err, err);
                assert.equal(histogram.type, 'histogram');
                validateHistogramBins(histogram);
                assert.ok(histogram.bins.length);
                assert.deepEqual(
                    histogram.bins[histogram.bins.length - 1],
                    { bin: 19, freq: 1, min: 35676000, max: 35676000, avg: 35676000 }
                );

                var emptyBin = histogram.bins[18];
                assert.ok(!emptyBin);

                done();
            });
        });

        it('can use a fixed number of bins', function(done) {
            var fixedBinsHistogramMapConfig = histogramsMapConfig({
                pop_max: {
                    type: 'histogram',
                    options: {
                        column: 'pop_max'
                    }
                }
            });

            this.testClient = new TestClient(fixedBinsHistogramMapConfig);
            this.testClient.getWidget('pop_max', { bins: 5 }, function (err, res, histogram) {
                assert.ok(!err, err);
                assert.equal(histogram.type, 'histogram');

                assert.equal(histogram.bins_count, 5);

                validateHistogramBins(histogram);

                assert.ok(histogram.bins.length);
                assert.deepEqual(
                    histogram.bins[0],
                    { bin: 0, min: 0, max: 7067423, avg: 280820.0057731959, freq: 7275 }
                );
                assert.deepEqual(
                    histogram.bins[histogram.bins.length - 1],
                    { bin: 4, freq: 1, min: 35676000, max: 35676000, avg: 35676000 }
                );

                done();
            });
        });

        function validateHistogramBins(histogram) {
            var binWidth = histogram.bin_width;
            var start = histogram.bins_start;
            var end = start + (histogram.bins_count * binWidth);

            var firstBin = histogram.bins[0];
            assert.equal(firstBin.min, start,
                'First bin does not match min and start ' + JSON.stringify({
                    min: firstBin.min,
                    start: start
                })
            );

            var lastBin = histogram.bins[histogram.bins.length - 1];
            assert.equal(lastBin.max, end,
                'Last bin does not match max and end ' + JSON.stringify({
                    max: lastBin.max,
                    end: end
                })
            );

            function getBinStartEnd(binIndex) {
                return {
                    start: start + (binIndex * binWidth),
                    end: start + ((binIndex + 1) * binWidth)
                };
            }

            histogram.bins.forEach(function(bin) {
                var binStartEnd = getBinStartEnd(bin.bin);

                assert.ok(binStartEnd.start <= bin.min,
                    'Bin start bigger than bin min ' + JSON.stringify({
                        bin: bin.bin,
                        min: bin.min,
                        start: binStartEnd.start
                    })
                );

                assert.ok(binStartEnd.end >= bin.max,
                    'Bin end smaller than bin max ' + JSON.stringify({
                        bin: bin.bin,
                        max: bin.max,
                        end: binStartEnd.end
                    })
                );

                assert.ok(bin.avg >= bin.min && bin.avg <= bin.max,
                        'Bin avg not between min and max values' + JSON.stringify({
                        bin: bin.bin,
                        avg: bin.avg,
                        min: bin.min,
                        max: bin.max
                    })
                );
            });
        }

        describe('datetime column', function() {
            afterEach(function(done) {
                if (this.testClient) {
                    this.testClient.drain(done);
                } else {
                    done();
                }
            });

            var updatedAtFilter = {
                updated_at: {
                    min: 0
                }
            };

            it('can use a datetime column', function(done) {
                this.testClient = new TestClient(histogramsMapConfig({
                    updated_at: {
                        type: 'histogram',
                        options: {
                            column: 'updated_at'
                        }
                    }
                }));
                this.testClient.getWidget('updated_at', function (err, res, histogram) {
                    assert.ok(!err, err);
                    assert.ok(histogram);
                    assert.equal(histogram.type, 'histogram');

                    assert.ok(histogram.bins.length);

                    done();
                });
            });

            it('can use a datetime filtered column', function(done) {
                this.testClient = new TestClient(histogramsMapConfig({
                    updated_at: {
                        type: 'histogram',
                        options: {
                            column: 'updated_at'
                        }
                    }
                }));
                var params = {
                    own_filter: 1,
                    filters: {
                        layers: [updatedAtFilter]
                    }
                };
                this.testClient.getWidget('updated_at', params, function (err, res, histogram) {
                    assert.ok(!err, err);
                    assert.ok(histogram);
                    assert.equal(histogram.type, 'histogram');

                    assert.ok(histogram.bins.length);

                    done();
                });
            });

            it('can use a datetime filtered column with no results', function(done) {
                this.testClient = new TestClient(histogramsMapConfig({
                    updated_at: {
                        type: 'histogram',
                        options: {
                            column: 'updated_at'
                        }
                    }
                }));
                var params = {
                    own_filter: 1,
                    filters: {
                        layers: [{
                            updated_at: {
                                // this will remove all results
                                max: -1
                            }
                        }]
                    }
                };
                this.testClient.getWidget('updated_at', params, function (err, res, histogram) {
                    assert.ok(!err, err);
                    assert.ok(histogram);
                    assert.equal(histogram.type, 'histogram');

                    assert.equal(histogram.bins.length, 0);

                    done();
                });
            });

            it('can getTile with datetime filtered column', function(done) {
                this.testClient = new TestClient(histogramsMapConfig({
                    updated_at: {
                        type: 'histogram',
                        options: {
                            column: 'updated_at'
                        }
                    }
                }));
                var params = {
                    own_filter: 1,
                    filters: {
                        layers: [updatedAtFilter]
                    }
                };
                this.testClient.getTile(0, 0, 0, params, function (err, res, tile) {
                    assert.ok(!err, err);
                    assert.ok(tile);

                    done();
                });
            });

            it('can use two columns with different types', function(done) {
                this.testClient = new TestClient(histogramsMapConfig({
                    updated_at: {
                        type: 'histogram',
                        options: {
                            column: 'updated_at'
                        }
                    },
                    pop_max: {
                        type: 'histogram',
                        options: {
                            column: 'pop_max'
                        }
                    }
                }));

                var popMaxFilter = {
                    pop_max: {
                        max: 1e7
                    }
                };

                var params = {
                    own_filter: 1,
                    filters: {
                        layers: [popMaxFilter]
                    }
                };

                this.testClient.getWidget('updated_at', params, function (err, res, histogram) {
                    assert.ok(!err, err);
                    assert.ok(histogram);
                    assert.equal(histogram.type, 'histogram');

                    assert.ok(histogram.bins.length);

                    done();
                });
            });
        });

    });

});
