#! /usr/bin/env python3

import sys
import re
import os
import subprocess

from dataclasses import dataclass

recursive = False
files = []
env = {}
for arg in sys.argv[1:]:
    if arg.startswith("--"):
        arg = arg.removeprefix("--")
        if "=" in arg:
            arg, val = arg.split("=")
            env[arg] = val
        else:
            env[arg] = ""
    elif arg.startswith("-"):
        arg = arg.removeprefix("-")
        if arg == "R":
            recursive = True
        else:
            print(f"Unknown argument: -{arg}")
            sys.exit(1)
    else:
        files.append(arg)

def recursive_file_filter(f):
    if f.endswith(".png"): return False
    if f.endswith(".pdf"): return False
    if ".git" in f: return False
    return True

if recursive:
    root_files = files
    files = []
    for root in root_files:
        if os.path.isdir(root):
            for root, dirs, leaves in os.walk(root):
                for file in leaves:
                    full = root + "/" + file
                    if recursive_file_filter(full):
                        files.append(full)
        else:
            files.append(root)

def ansi_color(code):
    return lambda text: f"\x1b[{code}m{text}\x1b[0m"

red = ansi_color(31)
green = ansi_color(32)
yellow = ansi_color(33)
blue = ansi_color(34)
purple = ansi_color(35)
teal = ansi_color(36)
gray = ansi_color(37)

@dataclass
class File:
    lines: list[str]

    @staticmethod
    def from_lines(lines):
        return File(lines)

    def select(self, lineno):
        return Selection(self, lineno, 1)

    def len(self):
        return len(self.lines)

    def __getitem__(self, i):
        return self.lines[i]

@dataclass
class Selection:
    file: File
    start: int
    len: int

    def jump(self, pat):
        print(f"    Start cursor forward until {teal(pat)}", end="")
        while self.start < self.file.len():
            if "@scrybe" in self.file[self.start]:
                self.start += 1
            elif pat in self.file[self.start]:
                break
            else:
                self.start += 1
        self.len = 1
        print(f"\t\t selected: {blue(self.start + 1)}-{blue(self.start + self.len)}")

    def until(self, pat):
        print(f"    End cursor forward until {teal(pat)}", end="")
        self.len = 1
        while self.start + self.len < self.file.len():
            if pat in self.file[self.start + self.len]:
                break
            self.len += 1
        print(f"\t\t selected: {blue(self.start + 1)}-{blue(self.start + self.len)}")

    def extract(self):
        return self.file.lines[self.start:self.start + self.len]

print("Flags:")
for (flag, val) in env.items():
    print(f"  {blue(flag)}\t{teal(val)}")
print()

if files == []:
    print("No input files")
    sys.exit(1)

for file in files:
    print(f"Reading {teal(file)}")
    with open(file) as f:
        lines = f.readlines()
    _file = File.from_lines(lines)

    for (i, line) in enumerate(lines):
        match = re.search(r"@scrybe\((.*)\)", line)
        if match == None: continue
        instrs = match.groups()[0].split(";")
        _sel = _file.select(i + 1)
        print(f"  Checking command group at line {i + 1}")
        for instr in instrs:
            selection = _sel.extract()
            instr = instr.strip().split(" ")
            cmd = instr[0]
            rest = " ".join(instr[1:])

            if cmd == "if":
                found = rest in env
                sat = green("sat") if found else red("unsat")
                print(f"    Guarded by {teal(rest)} ({sat})", end="")
                if found:
                    print(f"\t\t proceed")
                else:
                    print(f"\t\t skip")
                    break

            elif cmd == "not":
                found = rest in env
                sat = red("sat") if found else green("unsat")
                print(f"    Guarded by {teal("not " + rest)} ({sat})", end="")
                if found:
                    print(f"\t\t skip")
                    break
                else:
                    print(f"\t\t proceed")

            elif cmd == "jump":
                _sel.jump(rest)

            elif cmd == "until":
                _sel.until(rest)

            elif cmd == "grep":
                for var,val in env.items():
                    rest = rest.replace("{{" + var + "}}", val)
                print(f"    Searching for pattern: {teal(rest)}", end="")
                proc = subprocess.Popen(f"grep {rest}", shell=True, text=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
                (out, err) = proc.communicate(input="".join(selection), timeout=1)
                if err != "":
                    print(f"\n      {red(f"ERROR: {err}")}")
                    continue
                if out == "":
                    print(f"\n      {red("ERROR: No match!")}")
                    continue
                print(f"\t\t {green("ok")}")

            elif cmd == "diff":
                print(f"    Diff to {teal(rest)}")
                proc = subprocess.Popen(f"diff {rest} -", shell=True, text=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
                (out, err) = proc.communicate(input="".join(selection), timeout=1)
                if err != "":
                    print(f"      {red(f"ERROR: {err}")}")
                    continue
                if out != "":
                    for lout in out.split("\n"):
                        if len(lout) == 0: continue
                        if lout[0] == ">":
                            print("      " + purple(lout))
                        elif lout[0] == "<":
                            print("      " + yellow(lout))

            elif cmd == "panic":
                print("    Panic")
                for line in rest.split("\n"):
                    print("      " + red(line))
                sys.exit(1)

            elif cmd == "print":
                print("    Info")
                for line in selection:
                    print("      " + gray(line.strip('\n')))

            else:
                print(f"Unknown instruction: {instr}")
                sys.exit(1)
        print()
    print()

