#!/usr/bin/env python
# -*- coding: utf-8 -*-
#PYTHON_ARGCOMPLETE_OK
"""braise - a dynamic firewall

"""

import argparse
import pickle
import json
from sys import argv
from servicewall import parser_helpers
from servicewall.network_helpers import get_realm_id
try:
    import argcomplete
    ARGCOMPLETE = True
except ImportError:
    ARGCOMPLETE = False


conf_dir = "/etc/servicewall/"
lib_dir = "/usr/lib/servicewall/"
realm_defs_dict = conf_dir + "realms.json"
service_defs_pickle = lib_dir + "services.p"
definitions_dir = "/etc/gufw/app_profiles"

with open(realm_defs_dict, "rb") as fd:
    realm_defs = json.load(fd)
with open(service_defs_pickle, "rb") as fd:
    service_defs = pickle.load(fd)


# Find allowed services to help autocompletion know what to delete. - TODO doesn't
# work when the realm is unknown
try:
    realm_id = get_realm_id()
    online = True
except KeyError:
    # No network (but servicewall might still be enabled).
    realm_id = None
    online = False
if online:
    try:
        allowed_services = [ service_def for service_def in realm_defs[realm_id].keys() ]
    except KeyError:
        allowed_services = []
else:
    allowed_services = []

def parser_helper(function_name):
    """This function will return a pointer to a function named "function_name"
    in parser_helpers. This pointer will only be evaluated when it is called,
    ie after parser is done.
    """
    def wrapper(args):
        eval("parser_helpers." + function_name)(args)
    return wrapper

parser = argparse.ArgumentParser(
    description="ServiceWall CLI frontend"
)

# parser fails badly when invoked without argument ; let's invoke basic help
# instead (see def no_arg_provided(args) in parser_helpers.py)
#parser.set_defaults(func=parser_helper("no_arg_provided"))
#parser.set_defaults(func=parser.print_help())
#defarg = parser.add_argument("-s", action='store_true')
#defarg.set_defaults(func=parser.print_help())

subparser = parser.add_subparsers()


#
# SUBPARSERS : enable | disable | status | reload
#

parser_enable = subparser.add_parser(
    "enable",
    help="enable the firewall",
    description="enable the firewall",
)
parser_enable.set_defaults(func=parser_helper("enable"))

parser_disable = subparser.add_parser(
    "disable",
    help="disable the firewall",
    description="disable the firewall",
)
parser_disable.set_defaults(func=parser_helper("disable"))
parser_enable = subparser.add_parser(
    "start",
    help="start the firewall",
    description="start the firewall",
)
parser_enable.set_defaults(func=parser_helper("start"))

parser_disable = subparser.add_parser(
    "stop",
    help="stop the firewall",
    description="stop the firewall",
)
parser_disable.set_defaults(func=parser_helper("stop"))

parser_show_status = subparser.add_parser(
    "status",
    help="tell if the firewall is enabled and what is the current realm",
    description="tell if the firewall is enabled and what is the current realm",
)
parser_show_status.set_defaults(func=parser_helper("status"))

parser_reload = subparser.add_parser(
    "reload",
    help="reload rules",
    description="reload rules",
)
parser_reload.set_defaults(func=parser_helper("reload"))


#
# SUBPARSERS : show { logs | realm | realms | service | services | port }
#

parser_show = subparser.add_parser(
    "show",
    help="show different useful things",
    description="show different useful things",
)
parser_show.set_defaults(func=parser_helper("show"))
show_subparser = parser_show.add_subparsers()

parser_show_logs = show_subparser.add_parser(
    "logs",
    help="show a digest of logs of the firewall",
    description="show dropped packets. Columns are : source_ip destination_ip protocol age",
)
parser_show_logs.set_defaults(func=parser_helper("show_logs"))
parser_show_logs.add_argument(
    "-w",
    "--with-names",
    action="store_true",
    help="display hostname along source_ip",
)
show_logs_subparser = parser_show_logs.add_subparsers()

parser_show_logs_time = show_logs_subparser.add_parser(
    "since",
    help="restrict logs to those since [period] seconds",
)
parser_show_logs_time.add_argument("period", type=int)
parser_show_logs_time.set_defaults(func=parser_helper("show_logs"))
parser_show_logs_limit = show_logs_subparser.add_parser(
    "last",
    help="shorten logs to last [limit] hits",
)
parser_show_logs_limit.add_argument("limit", type=int)
parser_show_logs_limit.set_defaults(func=parser_helper("show_logs"))

parser_show_realm = show_subparser.add_parser(
    "realm",
    help="show current realm details",
    description="show current realm details",
)
parser_show_realm.set_defaults(func=parser_helper("show_realm"))
parser_show_realms = show_subparser.add_parser(
    "realms",
    help="show realm definitions",
    description="show realm definitions",
)
parser_show_realms.set_defaults(func=parser_helper("show_realms"))

parser_show_table = show_subparser.add_parser(
    "table",
    help="show services allowed in current realm",
    description="show services allowed in current realm",
)
parser_show_table.set_defaults(func=parser_helper("show_table"))

# For "show service", don't define help ; instead overwrite usage because it
# prints the possible arguments twice, which is way too long.
# TODO could tell to search for services with braise show services
# and then don't display "positional arguments" - see
# https://docs.python.org/3.2/library/argparse.html#formatter-class
parser_show_service = show_subparser.add_parser(
    "service",
    usage="usage: %s %s service_name" % (parser_show.prog, "service"),
    help="print service_name's service definition",
    description="print service_name's service definition",
)
parser_show_service.add_argument("service_name", choices=service_defs)
parser_show_service.set_defaults(func=parser_helper("show_service"))
parser_show_services = show_subparser.add_parser(
    "services",
    help="show services list",
    description="show services list",
)
parser_show_services.set_defaults(func=parser_helper("show_services"))

parser_show_port = show_subparser.add_parser(
    "port",
    help="show services associated to port",
    description="show services associated to port",
)
parser_show_port.add_argument("port_name")
parser_show_port.set_defaults(func=parser_helper("show_port"))


#
# SUBPARSERS : { allow | disallow } service
#

parser_allow = subparser.add_parser("allow")
allow_subparser = parser_allow.add_subparsers()
parser_allow_service = allow_subparser.add_parser(
    "service",
    usage="usage: %s service service_name" % parser_allow.prog,
    help="add service service_name to authorized list",
    description="add a service to authorized list",
    
)
parser_allow_service.add_argument("service_name", choices=service_defs)
gl = parser_allow_service.add_subparsers()
glp = gl.add_parser("globally")
glp.set_defaults(func=parser_helper("allow_service_globally"))
parser_allow_service.set_defaults(func=parser_helper("allow_service"))

parser_disallow = subparser.add_parser("disallow")
disallow_subparser = parser_disallow.add_subparsers()
# We could have used parser_add_service, but the choices aren't the same :
parser_disallow_service = disallow_subparser.add_parser(
    "service",
    usage="usage: %s service service_name" % parser_disallow.prog,
    help="remove service service_name from authorized list",
    description="remove service service_name from authorized list",
)
parser_disallow_service.add_argument("service_name", choices=allowed_services)
parser_disallow_service.set_defaults(func=parser_helper("disallow_service"))

if ARGCOMPLETE:
    argcomplete.autocomplete(parser)

if len(argv) == 1:
    # display help message when no args are passed.
    print("\n! no argument provided.\n")
    parser.print_help()
    raise SystemExit(1)

# Here, argcomplete stops.
args = parser.parse_args()
# Here, helper callbacks get evaluated.
args.func(args)

