#!/usr/bin/python -Es
# -*- coding: utf-8 -*-
#
# Copyright (C) 2009-2014 Red Hat, Inc.
#
# Authors:
# Thomas Woerner <twoerner@redhat.com>
# Jiri Popelka <jpopelka@redhat.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
#

from gi.repository import GObject
import sys
sys.modules['gobject'] = GObject

import argparse
import dbus
import os
import json

from rolekit.client import *
from rolekit.errors import *
from rolekit.functions import joinArgs, splitArgs

def __print(msg=None):
    if msg and not a.quiet:
        print(msg)

def __print_and_exit(msg=None, exit_code=0):
    FAIL = '\033[91m'
    OK =   '\033[92m'
    END =  '\033[00m'
    if exit_code != 0:
        __print(FAIL + msg + END)
    else:
        __print(msg)
        #__print(OK + msg + END)
    sys.exit(exit_code)

def __fail(msg=None):
    __print_and_exit(msg, 2)

def __print_if_verbose(msg=None):
    if msg and a.verbose:
        print(msg)

def __print_query_result(value):
    if value:
        __print_and_exit("yes")
    else:
        __print_and_exit("no")

def __exception_handler(exception_message):
    if "NotAuthorizedException" in exception_message:
        msg = """Authorization failed.
Make sure polkit agent is running or run the application as superuser."""
        __print_and_exit(msg, NOT_AUTHORIZED)
    else:
        code = RolekitError.get_code(exception_message)
        __print_and_exit("Error: %s" % exception_message, code)

def instance_type(arg):
    if not "/" in arg:
        raise ValueError("Not a valid instance, use <role/instance>")
    return arg

def create_settings_from_file(filename):
    try:
        with open(filename, "r") as f:
            try:
                settings = json.load(f)
            except Exception as e:
                __print_and_exit("Error: '%s' is not a valid json settings file: %s" % (filename, e))
        return settings
    except IOError as e:
        __print_and_exit("Error: Failed to open file '%s'" % filename)

parser = argparse.ArgumentParser(description="Rolekit command line control utility")
subparsers = parser.add_subparsers(help="Commands")
#subparsers.required = True


grp_output = parser.add_mutually_exclusive_group()
grp_output.add_argument("-v", "--verbose", action="store_true")
grp_output.add_argument("-q", "--quiet", action="store_true")


grp_version = subparsers.add_parser("version", help="Show rolekit version")
grp_version.set_defaults(parser="version")


grp_list = subparsers.add_parser("list", help="List roles or instances")
grp_list.set_defaults(parser="list")
grp_list_subparsers = grp_list.add_subparsers(help="Commands")


grp_list_roles = grp_list_subparsers.add_parser("roles", help="List roles")
grp_list_roles.set_defaults(list="roles")
grp_list_roles.add_argument("--object-path", action="store_true")


grp_list_instances = grp_list_subparsers.add_parser("instances", help="List instances")
grp_list_instances.set_defaults(list="instances")
grp_list_instances.add_argument("--state", metavar="<state>")
grp_list_instances.add_argument("--object-path", action="store_true")


grp_settings = subparsers.add_parser("settings", help="Settings for roles or instances")
grp_settings.set_defaults(parser="settings")
grp_settings.add_argument("what", type=str, metavar="<role>|<role/instance>")
grp_settings.add_argument("--key", metavar="<key>")


grp_deploy = subparsers.add_parser("deploy", help="Deploy a role to create a role instance")
grp_deploy.set_defaults(parser="deploy")
grp_deploy.add_argument("role", type=str, action="store", metavar="<role>")
grp_deploy.add_argument("--name", metavar="<name>", default="")
grp_deploy.add_argument("--settings-file", type=str, metavar="<json settings file>", help="Not using a settings file will result in attempting to deploy without custom settings. This will fail if the role has mandatory settings.")

grp_redeploy = subparsers.add_parser("redeploy", help="Redeploy a role instance")
grp_redeploy.set_defaults(parser="redeploy")
grp_redeploy.add_argument("instance", type=instance_type, action="store", metavar="<role/instance>")
grp_redeploy.add_argument("--settings-file", type=str, metavar="<json settings file>")


grp_status = subparsers.add_parser("status", help="Get status for role/instance")
grp_status.set_defaults(parser="status")
grp_status.add_argument("instance", type=instance_type, metavar="<role/instance>")


grp_start = subparsers.add_parser("start", help="Start a role instance")
grp_start.set_defaults(parser="start")
grp_start.add_argument("instance", type=instance_type, action="store", metavar="<role/instance>")


grp_stop = subparsers.add_parser("stop", help="Stop a role instance")
grp_stop.set_defaults(parser="stop")
grp_stop.add_argument("instance", type=instance_type, action="store", metavar="<role/instance>")


grp_restart = subparsers.add_parser("restart", help="Restart a role instance")
grp_restart.set_defaults(parser="restart")
grp_restart.add_argument("instance", type=instance_type, action="store", metavar="<role/instance>")


grp_update = subparsers.add_parser("update", help="Update a role instance")
grp_update.set_defaults(parser="update")
grp_update.add_argument("instance", type=instance_type, action="store", metavar="<role/instance>")


grp_reset_error = subparsers.add_parser("reset-error", help="Reset error state in a role instance")
grp_reset_error.set_defaults(parser="reset-error")
grp_reset_error.add_argument("instance", type=instance_type, action="store", metavar="<role/instance>")


grp_decommission = subparsers.add_parser("decommission", help="Decommission a role instance")
grp_decommission.set_defaults(parser="decommission")
grp_decommission.add_argument("instance", type=instance_type, action="store", metavar="<role/instance>")
grp_decommission.add_argument("--force", action="store_true")


a = parser.parse_args()

# Check various impossible combinations of options

# use rolekit

rk = RolekitClient()
rk.setExceptionHandler(__exception_handler)
if rk.connected == False:
    __print_and_exit ("Rolekit is not running", NOT_RUNNING)

# version
if a.parser == "version":
    __print_and_exit(rk.get_property("version"))

#list
elif a.parser == "list":
    if a.list == "roles":
        if a.object_path:
            __print_and_exit("\n".join(sorted(rk.getRoles())))
        __print_and_exit("\n".join(sorted(rk.getRoleNames())))
    elif a.list == "instances":
        if a.state:
            instances = rk.getAllRoleInstancesByState(a.state)
        instances = rk.getAllRoleInstances()

        if a.object_path:
            __print_and_exit("\n".join(instances))

        names = [ ]
        for i in instances:
            o = rk.getRoleInstanceObj(i)
            names.append("%s/%s" % (o.get_property("type"),
                                    o.get_property("name")))
        __print_and_exit("\n".join(names))

# settings
elif a.parser == "settings":
    if "/" in a.what:
        splits = a.what.split("/")
        obj = rk.getNamedRoleObj(splits[0]).getNamedInstanceObj(splits[1])
    else:
        obj = rk.getNamedRoleObj(a.what)

    if a.key:
        if "/" in a.what:
            __print_and_exit(obj.get_property(a.key))
        else:
            props = obj.get_properties()["DEFAULTS"]
            if a.key not in props:
                __print_and_exit("Error: No property '%s' in role '%s'" % \
                                 (a.key, a.what))
            __print_and_exit(props[a.key])

    if "/" in a.what:
        props = obj.get_properties()
    else:
        props = obj.get_properties()["DEFAULTS"]

    str = ""
    for key,value in props.items():
        str += "%s = %s\n" % (key, value)
    __print_and_exit(str)


# deploy
elif a.parser == "deploy":
    obj = rk.getNamedRoleObj(a.role)

    # TODO: Implement progress meter
    __print("Deployment can take a long time. To monitor the progress, run \n"
            "journalctl -ef -u rolekit")

    if a.settings_file:
        obj.deploy(a.name, create_settings_from_file(a.settings_file))
    else:
        obj.deploy(a.name, { })

elif a.parser in [ "redeploy", "status", "start", "stop", "restart", "update",
                   "reset-error", "decommission" ]:

    splits = a.instance.split("/")
    obj = rk.getNamedRoleObj(splits[0]).getNamedInstanceObj(splits[1])


    # redeploy
    if a.parser == "redeploy":
        if a.settings_file:
            obj.redeploy(create_settings_from_file(a.settings_file))
        else:
            obj.redeploy({ })


    # status
    elif a.parser == "status":
        __print_and_exit(obj.get_property("state"))


    # start
    elif a.parser == "start":
        obj.start()


    # stop
    elif a.parser == "stop":
        obj.stop()


    # restart
    elif a.parser == "restart":
        obj.restart()


    # update
    elif a.parser == "update":
        obj.update()


    # reset-error
    elif a.parser == "reset-error":
        obj.resetError()

    # decommission
    elif a.parser == "decommission":
        # TODO: Implement progress meter
        __print("Decommissioning can take a long time. To monitor the "
                "progress, run \n"
                "journalctl -ef -u rolekit")
        obj.decommission(a.force)


else:
    __fail("Uncaught argument")


__print_and_exit("success")
