# AUTOGENERATED! DO NOT EDIT! File to edit: nbs/03_IOUtils.ipynb (unless otherwise specified).

__all__ = ['readAbstractMethodsFromFile', 'writeAbstractMethodsToFile', 'readCompoundOperationsFromFile',
           'writeCompoundOperationsToFile', 'getYamlParameter', 'runCommand']

# Cell
#hide
from typing import List, Iterable, Optional
import re
import subprocess

import sys
sys.path.append("..")

from .AbstractMethod import *
from .EditOperations import *

# Cell
def readAbstractMethodsFromFile(file: str) -> List[AbstractMethod]:
    """
    Returns a list of `AbstractMethods` read from the given `file`. The file should have one `AbstractMethod`
    per line with tokens separated by spaces.
    """

    abstractMethods = []

    with open(file, "r") as f:
        for line in f:
            abstractMethods.append(AbstractMethod(line.strip()))

    return abstractMethods

# Cell
def writeAbstractMethodsToFile(file: str, abstractMethods: List[AbstractMethod]) -> None:
    """
    Writes the given `abstractMethods` to the given `file` such that one `AbstractMethod` is written per line.
    """

    with open(file, "w") as f:

        for i in range(len(abstractMethods)):

            f.write(str(abstractMethods[i]))

            if i < len(abstractMethods) - 1:
                f.write("\n")

# Cell
def readCompoundOperationsFromFile(file: str) -> List[Optional[List[CompoundOperation]]]:
    """
    Returns a list of lists of CompoundOperations read from the given `file`. Each line in the file represents a
    list of CompoundOperations in machine string format, as in the file written by `writeCompoundOperationsToFile`.
    If a line is unable to be parsed into a list of CompoundOperations, then that list is instead represented by
    `None`. This is different from an empty list, which represents a line with no CompoundOperations.
    """

    operations = []

    with open(file, "r") as f:

        for line in f:

            lineOps = []

            # continue as long as there is stuff left in the line and there hasn't been a parse failure
            while line != "" and lineOps is not None:

                # match the beginning of the line to the machine string pattern
                line = line.strip()
                match = re.search(r"^<.+?>.*?</.+?>", line)

                # if the match succeeded, then attempt to remove the operation from the line and add it to lineOps
                if match:
                    try:
                        lineOps.append(CompoundOperation.FromMachineString(match.group(0)))
                        line = line[len(match.group(0)):]
                    except ValueError:
                        lineOps = None

                # if the match didn't succeed and there is stuff left on the line, then this line is invalid
                elif line != "":
                    lineOps = None

            operations.append(lineOps)

    return operations

# Cell
def writeCompoundOperationsToFile(file: str, operations: List[List[CompoundOperation]], form: str = "general") -> None:
    """
    Writes the given list of lists of CompoundOperations to the given `file` in the machine string form given by `form`
    (defaults to `"general"`). Each list of operations occupies one line.
    """

    with open(file, "w") as f:

        for i in range(len(operations)):

            for j in range(len(operations[i])):

                f.write(operations[i][j].getMachineString(form))

                if j < len(operations[i]) - 1:
                    f.write(" ")

            if i < len(operations) - 1:
                f.write("\n")

# Cell
#hide
def getYamlParameter(yamlFile: str, key: str) -> str:
    """
    Returns the value of the given `key` in the given `yamlFile`. If the parameter was not found,
    Returns None.
    """

    with open(yamlFile, "r") as file:
        for line in file:
            match = re.search(r"^\s*" + key + r"\s*:\s*(.+?)(?:\s+#|$)", line)
            if (match):
                return match.group(1).strip()

    return None

# Cell
#hide
def runCommand(command: Iterable[str]) -> str:
    """
    Runs the given command in the shell and continuously prints its output. If the command failed, raises
    a ProcessException. Returns the command output.
    """

    # run the command
    process = subprocess.Popen(command, shell = True, stdout = subprocess.PIPE, stderr = subprocess.STDOUT)
    output = ""

    while True:

        line = process.stdout.readline()

        # break out if the process has finished and there is no more output to print
        if process.poll() is not None and line == b"":
            break

        sys.stdout.write(line)
        sys.stdout.flush()
        output += line.decode()

    exitCode = process.returncode

    if exitCode == 0:
        return output

    raise subprocess.CalledProcessError(exitCode, command, output)