/*
 * Copyright 2022 VMware, Inc.
 * SPDX-License-Identifier: MIT
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

package org.dbsp.sqlCompiler.compiler.visitors.outer;

import org.dbsp.sqlCompiler.circuit.DBSPCircuit;
import org.dbsp.sqlCompiler.compiler.DBSPCompiler;
import org.dbsp.sqlCompiler.compiler.ICompilerComponent;
import org.dbsp.sqlCompiler.compiler.backend.dot.ToDot;
import org.dbsp.sqlCompiler.ir.DBSPNode;
import org.dbsp.util.IWritesLogs;
import org.dbsp.util.Linq;
import org.dbsp.util.Logger;
import org.dbsp.util.Utilities;

import java.util.List;

public class Passes implements IWritesLogs, CircuitTransform, ICompilerComponent {
    final DBSPCompiler compiler;
    public final List<CircuitTransform> passes;
    // Generate a new name for each dumped circuit.
    static int dumped = 0;
    final long id;
    final String name;

    public Passes(String name, DBSPCompiler compiler, CircuitTransform... passes) {
        this(name, compiler, Linq.list(passes));
    }

    public Passes(String name, DBSPCompiler compiler, List<CircuitTransform> passes) {
        this.compiler = compiler;
        this.passes = passes;
        this.id = CircuitVisitor.crtId++;
        this.name = name;
    }

    @Override
    public DBSPCompiler compiler() {
        return this.compiler;
    }

    public void add(CircuitTransform pass) {
        this.passes.add(pass);
    }

    /** Insert a call to dump the current circuit as a png at this point */
    public void dump(int details) {
        String fileName = "start";
        if (!this.passes.isEmpty())
            fileName = Utilities.last(this.passes).getName().replace(" ", "_");
        this.add(ToDot.dumper(compiler, fileName + ".png", details));
    }

    @Override
    public DBSPCircuit apply(DBSPCircuit circuit) {
        int details = Math.max(0, this.getDebugLevel() - 2);
        if (this.getDebugLevel() >= 3) {
            String name = String.format("%02d-", dumped++) + "before" +
                    this.toString().replace(" ", "_") + ".png";
            ToDot.dump(this.compiler, name, details, "png", circuit);
        }
        long begin = System.currentTimeMillis();
        Logger.INSTANCE.belowLevel(this, 1)
                .append(this.toString())
                .append(" starting ")
                .append(this.passes.size())
                .append(" passes")
                .increase();
        for (CircuitTransform pass: this.passes) {
            long start = System.currentTimeMillis();
            long startId = DBSPNode.outerId;
            circuit = pass.apply(circuit);
            if (this.compiler.messages.exitCode != 0)
                break;
            long endId = DBSPNode.outerId;
            long end = System.currentTimeMillis();
            Logger.INSTANCE.belowLevel(this, 1)
                    .append(pass.toString())
                    .append(" took ")
                    .append(end - start)
                    .append("ms, created ")
                    .append(String.format("%,d", endId - startId))
                    .append(" node")
                    .append((endId - startId != 1) ? "s" : "")
                    .newline();
            if (this.getDebugLevel() >= 3) {
                String name = String.format("%02d-", dumped++) + pass.toString().replace(" ", "_") + ".png";
                ToDot.dump(this.compiler, name, details, "png", circuit);
            }
        }
        long finish = System.currentTimeMillis();
        Logger.INSTANCE.belowLevel(this, 1)
                .decrease()
                .append(this.toString())
                .append(" took ")
                .append(finish - begin)
                .append("ms.")
                .newline();
        return circuit;
    }

    @Override
    public String toString() {
        return this.id +
                " " +
                this.name +
                this.passes.size();
    }

    @Override
    public String getName() {
        return this.name;
    }
}
