# Copyright (c) 2022 Intel and/or its affiliates.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at:
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from vpp_papi.vpp_papi import VPPApiClient
import sys, getopt
import packetforge
import fnmatch
import os

# Get VPP json API file directory
CLIENT_ID = "Vppclient"
VPP_JSON_DIR = (
    os.path.abspath("../..") + "/build-root/install-vpp-native/vpp/share/vpp/api/core"
)
VPP_JSON_DIR_PLUGIN = (
    os.path.abspath("../..")
    + "/build-root/install-vpp-native/vpp/share/vpp/api/plugins"
)
API_FILE_SUFFIX = "*.api.json"


def load_json_api_files(suffix=API_FILE_SUFFIX):
    jsonfiles = []
    json_dir = VPP_JSON_DIR
    for root, dirnames, filenames in os.walk(json_dir):
        for filename in fnmatch.filter(filenames, suffix):
            jsonfiles.append(os.path.join(json_dir, filename))
    json_dir = VPP_JSON_DIR_PLUGIN
    for root, dirnames, filenames in os.walk(json_dir):
        for filename in fnmatch.filter(filenames, suffix):
            jsonfiles.append(os.path.join(json_dir, filename))
    if not jsonfiles:
        raise RuntimeError("Error: no json api files found")
    else:
        print("load json api file done")
    return jsonfiles


def connect_vpp(jsonfiles):
    vpp = VPPApiClient(apifiles=jsonfiles)
    r = vpp.connect("CLIENT_ID")
    print("VPP api opened with code: %s" % r)
    return vpp


def Main(argv):
    file_flag = False
    operation = None
    actions = ""
    iface = ""
    try:
        opts, args = getopt.getopt(
            argv,
            "hf:p:a:i:I:",
            [
                "help",
                "add",
                "del",
                "show",
                "file=",
                "pattern=",
                "actions=",
                "interface=",
                "flow-index=",
            ],
        )
    except getopt.GetoptError:
        print(
            "flow_create.py --add|del|show -f <file> -p <pattern> -a <actions> -i <interface> -I <flow-index>"
        )
        sys.exit()
    for opt, arg in opts:
        if opt == "-h":
            print(
                "flow_create.py --add|del|show -f <file> -p <pattern> -a <actions> -i <interface> -I <flow-index>"
            )
            sys.exit()
        elif opt == "--add":
            operation = "add"
        elif opt == "--del":
            operation = "del"
        elif opt == "--show":
            operation = "show"
        elif opt in ("-f", "--file"):
            json_file = arg
            file_flag = True
        elif opt in ("-p", "--pattern") and not file_flag:
            pattern = arg
        elif opt in ("-a", "--actions"):
            actions = arg
        elif opt in ("-i", "--interface"):
            iface = arg
        elif opt in ("-I", "--flow-index"):
            flow_index = arg

    if operation == None:
        print("Error: Please choose the operation: add or del")
        sys.exit()

    if operation == "show":
        if not file_flag:
            result = packetforge.Forge(pattern, actions, False, True)
        else:
            result = packetforge.Forge(json_file, actions, True, True)
        return result, None, operation, None, None

    # Python API need json definitions to interpret messages
    vpp = connect_vpp(load_json_api_files())

    # set inteface states
    vpp.api.sw_interface_set_flags(sw_if_index=int(iface), flags=1)

    if operation == "add":
        if not file_flag:
            result = packetforge.Forge(pattern, actions, False, False)
        else:
            result = packetforge.Forge(json_file, actions, True, False)
        return result, int(iface), operation, None, vpp
    elif operation == "del":
        return None, int(iface), operation, int(flow_index), vpp


if __name__ == "__main__":
    # Parse the arguments
    my_flow, iface, operation, del_flow_index, vpp = Main(sys.argv[1:])

    # if operation is show, just show spec and mask, then exit
    if operation == "show":
        print(my_flow)
        sys.exit()

    if operation == "add":
        # add flow
        rv = vpp.api.flow_add_v2(flow=my_flow)
        flow_index = rv[3]
        print(rv)

        # enable added flow
        rv = vpp.api.flow_enable(flow_index=flow_index, hw_if_index=iface)
        ena_res = rv[2]
        # if enable flow fail, delete added flow
        if ena_res:
            print("Error: enable flow failed, delete flow")
            rv = vpp.api.flow_del(flow_index=flow_index)
        print(rv)

    elif operation == "del":
        # disable flow
        rv = vpp.api.flow_disable(flow_index=del_flow_index, hw_if_index=iface)
        dis_res = rv[2]
        if dis_res:
            print("Error: disable flow failed")
            sys.exit()
        print(rv)

        # delete flow
        rv = vpp.api.flow_del(flow_index=del_flow_index)
        print(rv)

# command example:
# python flow_create.py --add -p "mac()/ipv4(src=1.1.1.1,dst=2.2.2.2)/udp()" -a "redirect-to-queue 3" -i 1
# python flow_create.py --del -i 1 -I 0
# python flow_create.py --show -p "mac()/ipv4(src=1.1.1.1,dst=2.2.2.2)/udp()"