/*
 * Copyright (C) 2015 Centro de Computacao Cientifica e Software Livre
 * Departamento de Informatica - Universidade Federal do Parana
 *
 * This file is part of blendb.
 *
 * blendb is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * blendb is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with blendb.  If not, see <http://www.gnu.org/licenses/>.
 */

import { expect } from "chai";

import { Engine } from "./engine";
import { Metric } from "./metric";
import { Dimension } from "./dimension";
import { View } from "./view";
import { AggregationType } from "../common/types";

describe("engine class", () => {
    const engine = new Engine();

    const met1 = new Metric({ name: "met:1", aggregation: AggregationType.SUM });
    const met2 = new Metric({ name: "met:2", aggregation: AggregationType.AVG });
    const met3 = new Metric({ name: "met:3", aggregation: AggregationType.AVG });
    const met4 = new Metric({ name: "met:4", aggregation: AggregationType.SUM });
    const met5 = new Metric({ name: "met:5", aggregation: AggregationType.SUM });
    const met6 = new Metric({ name: "met:6", aggregation: AggregationType.AVG });
    const met7 = new Metric({ name: "met:7", aggregation: AggregationType.COUNT });
    const met8 = new Metric({ name: "met:8", aggregation: AggregationType.COUNT });
    const met9 = new Metric({ name: "met:9", aggregation: AggregationType.SUM });
    const met10 = new Metric({ name: "met:10", aggregation: AggregationType.COUNT });
    const met11 = new Metric({ name: "met:11", aggregation: AggregationType.COUNT });

    const dim1 = new Dimension({ name: "dim:1" });
    const dim2 = new Dimension({ name: "dim:2" });
    const dim3 = new Dimension({ name: "dim:3" });
    const dim4 = new Dimension({ name: "dim:4" });
    const dim5 = new Dimension({ name: "dim:5" });
    const dim6 = new Dimension({ name: "dim:6" });
    const dim7 = new Dimension({ name: "dim:7" });
    const dim8 = new Dimension({ name: "dim:8" });
    const dim9 = new Dimension({ name: "dim:9" });
    const dim10 = new Dimension({ name: "dim:10" });
    const dim11 = new Dimension({ name: "dim:11" });

    engine.addMetric(met1);
    engine.addMetric(met2);
    engine.addMetric(met3);
    engine.addMetric(met4);
    engine.addMetric(met5);
    engine.addMetric(met6);
    engine.addMetric(met7);
    engine.addMetric(met8);
    engine.addMetric(met9);
    engine.addMetric(met10);

    engine.addDimension(dim1);
    engine.addDimension(dim2);
    engine.addDimension(dim3);
    engine.addDimension(dim4);
    engine.addDimension(dim5);
    engine.addDimension(dim6);
    engine.addDimension(dim7);
    engine.addDimension(dim8);
    engine.addDimension(dim9);
    engine.addDimension(dim10);

    let views: View[] = [
        new View({ metrics: [met1, met2, met3], dimensions: [dim1, dim2]}),
        new View({ metrics: [met1, met3, met5], dimensions: [dim1, dim2]}),
        new View({ metrics: [met3, met4, met7], dimensions: [dim4, dim5]}),
        new View({ metrics: [met6, met7], dimensions: [dim3, dim4, dim5, dim6]}),
        new View({ metrics: [met8, met2, met3], dimensions: [dim1, dim2, dim7]}),
        new View({ metrics: [met1, met2, met3], dimensions: [dim1, dim2]}),
        new View({ metrics: [met2, met4], dimensions: [dim1, dim2]}),
        new View({ metrics: [met8], dimensions: [dim8, dim9, dim10]}),
        new View({ metrics: [met9], dimensions: [dim8, dim9, dim10]}),
        new View({ metrics: [met10], dimensions: [dim8, dim9, dim10]})
    ];

    views.push(new View({
        metrics: [met1, met2, met3, met4, met5],
        dimensions: [dim1, dim2],
        materialized: false,
        childViews: [views[0], views[6]]
    }));

    views.push(new View({
        metrics: [met8, met9, met10],
        dimensions: [dim8, dim9, dim10],
        materialized: false,
        childViews: [views[7], views[8], views[9]]
    }));

    views.forEach((view) => engine.addView(view));

    it("should be create a fill that cover the query and has 4 children", () => {
        let query = {
            metrics : [met1, met2, met3, met4, met5, met6, met7, met8, met9, met10]
            , dimensions : [dim1, dim2, dim3, dim4, dim5, dim6, dim7, dim8, dim9, dim10]
        };
        let optimalView = engine.query(query);
        expect(optimalView).to.be.an("object");
        expect(optimalView).to.have.property("metrics");
        expect(optimalView).to.have.property("dimensions");
        expect(optimalView).to.have.property("childViews");
        expect(optimalView.metrics).to.be.an("array");
        expect(optimalView.dimensions).to.be.an("array");
        expect(optimalView.childViews).to.be.an("array");
        expect(optimalView.metrics.length === 10);
        expect(optimalView.dimensions.length === 10);
        expect(optimalView.childViews.length === 4);
        let metAux: number[] = optimalView.metrics.sort().map((met: Metric) => {
            return Number(met.name.split(":")[1]);
        });
        let dimAux: number[] = optimalView.dimensions.sort().map((dim: Dimension) => {
            return Number(dim.name.split(":")[1]);
        });
        for (let i: number = 1; i <= 10; ++i) {
            expect(dimAux[i] === i);
            expect(metAux[i] === i);
        }
    });
    it("should throw an exception, query with non-existent metric", () => {
        let error: boolean = false;
        try {
            engine.query({metrics: [met11], dimensions: [dim1]});
        }
        catch (e){
            error = true;
            expect(e.message).to.be.equal("Engine views cannot cover the query");

        }
        expect(error);
    });

    it("should throw an exception, query with non-existent dimension", () => {
        let error: boolean = false;
        try {
            engine.query({metrics: [met1], dimensions: [dim11]});
        }
        catch (e){
            error = true;
            expect(e.message).to.be.equal("Engine views cannot cover the query");

        }
        expect(error);
    });
});