#!/usr/bin/env python

# Copyright (c) Barefoot Networks, Inc.
# Licensed under the Apache License, Version 2.0 (the "License")

# Compiler from P4 to EBPF
# (See http://www.slideshare.net/PLUMgrid/ebpf-and-linux-networking).
# This compiler in fact generates a C source file
# which can be compiled to EBPF using the LLVM compiler
# with the ebpf target.
#
# Main entry point.

import argparse
import os
import traceback
import sys
import target
from p4_hlir.main import HLIR
from ebpfProgram import EbpfProgram
from compilationException import *
from programSerializer import ProgramSerializer


def get_parser():
    parser = argparse.ArgumentParser(description='p4toEbpf arguments')
    parser.add_argument('source', metavar='source', type=str,
                        help='a P4 source file to compile')
    parser.add_argument('-g', dest='generated', default="router",
                        help="kind of output produced: filter or router")
    parser.add_argument('-o', dest='output_file', default="output.c",
                        help="generated C file name")
    return parser


def process(input_args):
    parser = get_parser()
    args, unparsed_args = parser.parse_known_args(input_args)

    has_remaining_args = False
    preprocessor_args = []
    for a in unparsed_args:
        if a[:2] == "-D" or a[:2] == "-I" or a[:2] == "-U":
            input_args.remove(a)
            preprocessor_args.append(a)
        else:
            has_remaining_args = True

    # trigger error
    if has_remaining_args:
        parser.parse_args(input_args)

    if args.generated == "router":
        isRouter = True
    elif args.generated == "filter":
        isRouter = False
    else:
        print("-g should be one of 'filter' or 'router'")

    print("*** Compiling ", args.source)
    return compileP4(args.source, args.output_file, isRouter, preprocessor_args)


class CompileResult(object):
    def __init__(self, kind, error):
        self.kind = kind
        self.error = error

    def __str__(self):
        if self.kind == "OK":
            return "Compilation successful"
        else:
            return "Compilation failed with error: " + self.error


def compileP4(inputFile, gen_file, isRouter, preprocessor_args):
    h = HLIR(inputFile)

    for parg in preprocessor_args:
        h.add_preprocessor_args(parg)
    if not h.build():
        return CompileResult("HLIR", "Error while building HLIR")

    try:
        basename = os.path.basename(inputFile)
        basename = os.path.splitext(basename)[0]

        config = target.BccConfig()
        e = EbpfProgram(basename, h, isRouter, config)
        serializer = ProgramSerializer()
        e.toC(serializer)
        f = open(gen_file, 'w')
        f.write(serializer.toString())
        return CompileResult("OK", "")
    except CompilationException as e:
        prefix = ""
        if e.isBug:
            prefix = "### Compiler bug: "
        return CompileResult("bug", prefix + e.show())
    except NotSupportedException as e:
        return CompileResult("not supported", e.show())
    except:
        return CompileResult("exception", traceback.format_exc())


# main entry point
if __name__ == "__main__":
    result = process(sys.argv[1:])
    if result.kind != "OK":
        print(str(result))
