package datalog_ra;

import datalog_ra.Test.Test;
import datalog_ra.base.dataStructures.Instance;
import datalog_ra.base.dataStructures.Relation;
import datalog_ra.evaluation.Query;
import datalog_ra.evaluation.Rule;
import datalog_ra.parser.Parser;
import datalog_ra.programRA.programRA;

import java.io.ByteArrayInputStream;
import java.io.FileInputStream;
import java.nio.charset.Charset;
import java.util.List;
import java.util.ArrayList;
import java.util.Scanner;

/**
 *
 * @author Jakub
 */
public class Datalog_RA {
  public static void main(String[] args) throws Exception {
    Scanner scanner = new Scanner(System.in);
    Instance EDB = new Instance();
    List<Rule> rules = new ArrayList<>();
    Query modelQuery = null;
    boolean modelComputed = false;
    System.out.print("> "); System.out.flush();
    while (scanner.hasNextLine()) {
      String input = scanner.nextLine();
      String command[] = input.split(" +", 2);
      if (command.length == 0) continue;
      long startTime = System.nanoTime();
      switch (command[0]) {
        case "load":
          if (command[1] == null) {
            System.out.println("file path is required!");
            break;
          }
          try {
            Parser p = new Parser(new FileInputStream(command[1]));
            p.parse();
            EDB = p.getFacts();
            modelQuery = new Query(EDB);
            rules = p.getRules();
            for (Rule rule : rules) {
              modelQuery.addRule(rule);
            }
            modelComputed = false;
            System.out.println("load done!");
          } catch (Exception e) {
            System.out.println("error while loading file \"" + command[1] + "\": " + e.getMessage());
            break;
          }
          break;
        case "test":
          if (command[1] == null) {
            System.out.println("test name is required!");
            break;
          }
          Test.test(command[1], EDB);
          break;
        case "ra":
          if (command[1] == null) {
            System.out.println("file path is required!");
            break;
          }
          programRA prog;
          try {
            prog = new programRA(command[1]);
          }
          catch (Exception e) {
            System.out.println("invalid file: " + command[1]);
            break;
          }
          prog.run(EDB);
          break;
        case "?-":
        case "query":
          if (command[1] == null) {
            System.out.println("query string is required!");
            break;
          }
          try {
            Parser p = new Parser(new ByteArrayInputStream(command[1].getBytes(Charset.defaultCharset())));
            Rule query = p.parseSingleQuery();
            if (!modelComputed) {
              modelQuery.findWFModel();
              modelComputed = true;
            }
            modelQuery.setQuery(query);
            System.out.println(modelQuery.answer());
            System.out.println("query done!");
          } catch (Exception e) {
            System.out.println("query error: " + e.getMessage());
            break;
          }
          break;
        case "explain":
          if (command[1] == null) {
            System.out.println("rule name or string is required!");
            break;
          }
          try {
            if (command[1].matches("[0-9a-zA-Z_]+")) {
              for (Rule rule : rules) {
                if (rule.getName().equals(command[1])) {
                  rule.buildOperator();
                  System.out.println(rule.explain());
                }
              }
            } else {
              Parser p = new Parser(new ByteArrayInputStream(command[1].getBytes(Charset.defaultCharset())));
              p.parse();
              for (Rule rule : p.getRules()) {
                rule.buildOperator();
                System.out.println(rule.explain());
              }
            }
          } catch (Exception e) {
            System.out.println("explain error: " + e.getMessage());
            break;
          }
          break;
        case "print":
          System.out.println(EDB);
          break;
        case "q":
        case "quit":
          return;
        default:
          System.out.println(command[0] + " is not a valid command.");
      }
      System.out.println("command time: " + (System.nanoTime() - startTime) / (1000 * 1000) + "ms");
      System.out.print("> "); System.out.flush();
    }
  }
}
