package com.github.thomasridd.flatsy;

import com.github.thomasridd.flatsy.operations.operators.OperatorCommandLineParser;
import com.github.thomasridd.flatsy.query.FlatsyCursor;
import com.github.thomasridd.flatsy.query.matchers.FlatsyMatcherBuilder;
import com.github.thomasridd.flatsy.query.matchers.MatcherCommandLineParser;
import com.github.thomasridd.flatsy.util.FlatsyUtil;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

/**
 * Created by thomasridd on 14/10/15.
 * <p/>
 * Syntax options
 * <p/>
 * FROM [path to root of FlatsyFlatFileDatabase]
 * <p/>
 * FILTER
 * <p/>
 * COPY
 * REPLACE
 * URI_LIST [File path to write list (optional)]
 * FILE_LIST [File path to write list (optional)]
 */
public class FlatsyCommandLine {
    FlatsyDatabase db;
    List<String> queryCommands = new ArrayList<>();

    public OutputStream defaultOut = System.out; // Convenience for tests

    /**
     * Create a new FlatsyCommandLine
     */
    public FlatsyCommandLine() {};

    /**
     * Create a new FlatsyCommandLine and run a command
     * @param command the command
     */
    public FlatsyCommandLine(String command) {
        runCommand(command);
    }

    /**
     * Create a new FlatsyCommandLine and run a list of commands
     * @param commands a list of commands
     */
    public FlatsyCommandLine(List<String> commands) {
        runScript(commands);
    }

    /**
     * Create a new FlatsyCommandLine and run a script of commands
     * @param script a path to a script
     */
    public FlatsyCommandLine(Path script) throws IOException {
        runScript(script);
    }

    /**
     * Create a new FlatsyCommandLine and run a script of commands
     * @param stream a stream for script commands
     */
    public FlatsyCommandLine(InputStream stream) {
        runScript(stream);
    }

    /**
     * Build a command using flatsy command line syntax
     *
     * @param command a Flatsy command
     * @return true if complete
     */
    public boolean runCommand(String command) {
        // skip out on blank lines
        if (command.trim().length() == 0) return true;

        // get the list of arguments
        List<String> args = FlatsyUtil.commandArguments(command);

        String action = args.get(0).trim();
        if (action.equalsIgnoreCase("from")) {

            // set the database root
            db = new FlatsyFlatFileDatabase(Paths.get(args.get(1).trim()));
            queryCommands = new ArrayList<>();
            return true;
        } else if (action.equalsIgnoreCase("filter")) {

            // add to the query
            queryCommands.add(command);
            return true;
        } else {

            // run a command
            return applyOperation(command);
        }
    }

    /**
     * Run a sequence of script commands
     *
     * @param commands a list of Flatsy query language commands
     * @return success
     */
    public boolean runScript(List<String> commands) {
        boolean result = true;
        for (String command: commands) {
            result = result && runCommand(command);
        }
        return result;
    }

    /**
     * Run a sequence of script commands from file
     *
     * @param path the file to run
     * @return success
     * @throws IOException
     */
    public boolean runScript(Path path) throws IOException {
        boolean result = true;
        try(Scanner scanner = new Scanner(Files.newInputStream(path))) {
            while (scanner.hasNextLine()) {
                result = result && runCommand(scanner.nextLine());
            }
        }
        return result;
    }

    /**
     * Run a sequence of script commands from stream
     *
     * @param stream the input stream
     * @return success
     */
    public boolean runScript(InputStream stream) {
        boolean result = true;
        Scanner scanner = new Scanner(stream);
        while(scanner.hasNextLine()) {
            result = result && runCommand(scanner.nextLine());
        }
        return result;
    }

    /**
     * Create a cursor from script filters on the specified path
     *
     * @param path
     * @return
     */
    public FlatsyCursor cursor(Path path) throws IOException {
        db = null;
        try(Scanner scanner = new Scanner(Files.newInputStream(path))) {
            while (scanner.hasNextLine()) {
                buildCursor(scanner.nextLine());
            }
        }
        return MatcherCommandLineParser.cursorFromFilterCommands(db, queryCommands);
    }

    /**
     * Create a cursor based on a command list
     * @param commands
     * @return
     */
    public FlatsyCursor cursor(List<String> commands) {
        for (String command: commands) {
            buildCursor(command);
        }
        return MatcherCommandLineParser.cursorFromFilterCommands(db, queryCommands);
    }

    /**
     * Add filters to the list of query commands ignoring any execute statements
     *
     * @param command a Flatsy command
     * @return true if complete
     */
    private boolean buildCursor(String command) {
        // skip out on blank lines
        if (command.trim().length() == 0) return true;

        // get the list of arguments
        List<String> args = FlatsyUtil.commandArguments(command);

        String action = args.get(0).trim();
        if (action.equalsIgnoreCase("from")) {

            // set the database root
            db = new FlatsyFlatFileDatabase(Paths.get(args.get(1).trim()));
            queryCommands = new ArrayList<>();
            return true;
        } else if (action.equalsIgnoreCase("filter")) {

            // add to the query
            queryCommands.add(command);
            return true;
        }
        return false;
    }

    /**
     * Apply an operation to the cursor currently defined by queryCommands
     *
     * @param command an operation in Flatsy syntax
     * @return
     */
    protected boolean applyOperation(String command) {
        // Simply chuck blank lines
        if (command.trim().equalsIgnoreCase("")) { return true;}

        // Generate the cursor for the file systems
        FlatsyCursor query = MatcherCommandLineParser.cursorFromFilterCommands(db, queryCommands);
        if (query == null) return false;

        // Generate the operator
        OperatorCommandLineParser.applyFromCommand(query, command, defaultOut);

        return true;
    }
}

@@ -2876,17 +2876,476 @@
  %7D else 
-%7B
+if (action.equalsIgnoreCase(%22clear%22)) %7B%0A            queryCommands = new ArrayList%3C%3E();%0A            return true;%0A%0A        %7D else if (action.equalsIgnoreCase(%22print%22)) %7B%0A            System.out.println(%22%22);%0A            System.out.println(%22FROM %22 + db.toString());%0A            for (String query: queryCommands) %7B%0A                System.out.println(query);%0A            %7D%0A            return true;%0A%0A        %7Delse %7B%0A            long start = System.currentTimeMillis();
 %0A%0A      
@@ -3379,22 +3379,32 @@
         
-return
+boolean result =
  applyOp
@@ -3412,32 +3412,211 @@
 ration(command);
+%0A%0A            long total = System.currentTimeMillis() - start;%0A%0A            System.out.println(command + %22 %3E%3E Applied in %22 +  total + %22 milliseconds%22);%0A%0A            return result;
 %0A        %7D%0A    %7D

Clear and console print of operations added to command line