"""
Name: automataConverter.py
Author: Radovan Klembara

Main file of the program. Converts one type of deep pushdown automata to another depending on given parameters. Can
convert between deep pushdown automaton and parallel deep pushdown automaton or between deep pushdown automaton and
tuple parallel deep pushdown automaton.
"""

import sys
import getopt
from ConversionMode import ConversionMode
from Converter import convert_pda_to_ppda, convert_ppda_to_pda, convert_tppda_to_pda, convert_pda_to_tppda
from InputReader import read_from_stdin, read_from_file
from Error import Error


def wrong_arguments(msg: str):
    """
    Prints out error message and ends program with wrong arguments error value.

    :param msg: Error message.
    """
    print(msg)
    exit(Error.ARGUMENTS)


def main(argv):
    """
    Main function of program, reads automata from standard input or input file and converts it to other automata, which
    is then printed to either standard output or given output file.

    :param argv: Arguments of the program.
    """
    input_file = ""
    output_file = ""
    conversion_mode = None
    automata_arg_types = ["pda2ppda", "ppda2pda", "pda2tppda", "tppda2pda"]
    opts, args = getopt.getopt(argv, "hi:o:1234", ["help", "input=", "output="] + automata_arg_types)
    for opt, arg in opts:
        if opt in ("-h", "--help"):
            print("This program can convert between two pairs of automata types. First pair is deep pushdown automaton"
                  " and parallel deep pushdown automaton. Second pair is deep pushdown automaton and tuple parallel "
                  "deep pushdown automaton \n\nUsage: automataConverter.py [options]\n\nAccepted parameters:\n"
                  "\t-h or -help => Shows this help.\n\t-i {file} or -input {file} => Read from input file "
                  "instead of reading standard input.\n\t-o {file} or -output {file} => Write output to output file "
                  "instead of writing to standard output.\n\t-1 or -pda2ppda => Convert deep pushdown automaton to"
                  " parallel deep pushdown automaton.\n\t-2 or -ppda2pda => Convert parallel deep pushdown "
                  "automaton to deep pushdown automaton.\n\t-3 or -pda2tppda => Convert deep pushdown automaton to "
                  "tuple parallel deep pushdown automaton.\n\t-4 or -tppda2pda => Convert tuple parallel deep pushdown "
                  "automaton to deep pushdown automaton.")
            exit(0)
        elif opt in ("-i", "--input"):
            if input_file == "":
                input_file = arg
            else:
                wrong_arguments("Error: output file can be specified only once!")
        elif opt in ("-o", "--output"):
            if output_file == "":
                output_file = arg
            else:
                wrong_arguments("Error: output file can be specified only once!")
        elif opt in ("-1", "--pda2ppda"):
            conversion_mode = ConversionMode.PDA2PPDA
        elif opt in ("-2", "--ppda2pda"):
            conversion_mode = ConversionMode.PPDA2PDA
        elif opt in ("-3", "--pda2tppda"):
            conversion_mode = ConversionMode.PDA2TPPDA
        elif opt in ("-4", "--tppda2pda"):
            conversion_mode = ConversionMode.TPPDA2PDA
    if conversion_mode is None:
        print("Error: Conversion type is not specified!")
        exit(Error.AUTOMATA_TYPE)
    if input_file == "":
        automaton = read_from_stdin(conversion_mode)
    else:
        automaton = read_from_file(conversion_mode, input_file)
    new_automaton = convert_automaton(automaton, conversion_mode)
    if output_file == "":
        print(new_automaton)
    else:
        try:
            with open(output_file, 'w') as file:
                sys.stdout = file
                print(new_automaton)
        except IOError as _:
            print("Error: couldn't open output file")
            exit(Error.FILE)


def convert_automaton(automaton, conversion_mode: ConversionMode):
    """
    Converts read automaton to different type based on given conversion mode.

    :param automaton: The source automaton.
    :param conversion_mode: The conversion mode.
    :return: Goal automaton.
    """
    if conversion_mode == ConversionMode.PDA2PPDA:
        return convert_pda_to_ppda(automaton)
    elif conversion_mode == ConversionMode.PPDA2PDA:
        return convert_ppda_to_pda(automaton)
    elif conversion_mode == ConversionMode.PDA2TPPDA:
        return convert_pda_to_tppda(automaton)
    elif conversion_mode == ConversionMode.TPPDA2PDA:
        return convert_tppda_to_pda(automaton)
    else:
        print("Error: Unknown automata type!")
        exit(Error.AUTOMATA_TYPE)


if __name__ == '__main__':
    main(sys.argv[1:])
