#!/usr/bin/python3
# Filename:      grml-paste
# Purpose:       XmlRpc interface client to paste.grml.org
# Author:        Michael Gebetsroither <gebi@grml.org>
# License:       This file is licensed under the GPL v2.
################################################################################

import getpass
import inspect
import optparse
import sys
from xmlrpc.client import ServerProxy

# program defaults
DEFAULT_SERVER = "https://paste.debian.net/server.pl"


class ActionFailedException(Exception):
    """Thrown if server returned an error"""

    def __init__(self, errormsg, ret):
        Exception.__init__(self, errormsg, ret)

    def what(self):
        """Get errormessage"""
        return self.args[0]

    def dwhat(self):
        """Get more verbose errormessage"""
        return self.args[1]


def _paste(opts):
    """Get paste proxy object"""
    return ServerProxy(opts.server, verbose=False).paste


def _check_success(return_data):
    """Check if call was successful, raise ActionFailedException otherwise"""
    if return_data["rc"] != 0:
        raise ActionFailedException(return_data["statusmessage"], return_data)
    return return_data


def action_add_paste(opts, code):
    """Add paste to the server: <1.line> <2.line> ...

    default     Read paste from stdin.
    [text]      Every argument on the commandline will be interpreted as
                a seperate line of paste.
    """
    if len(code) == 0:
        code = [line.rstrip() for line in sys.stdin.readlines()]
    code = "\n".join(code)
    result = _check_success(
        _paste(opts).addPaste(
            code, opts.name, opts.expire * 3600, opts.lang, opts.private
        )
    )
    return result["statusmessage"], result


def action_del_paste(opts, args):
    """Delete paste from server: <digest>

    <digest>    Digest of paste you want to remove.
    """
    digest = args.pop(0)
    result = _check_success(_paste(opts).deletePaste(digest))
    return result["statusmessage"], result


def action_get_paste(opts, args):
    """Get paste from server: <id>

    <id>        Id of paste you want to receive.
    """
    paste_id = args.pop(0)
    result = _check_success(_paste(opts).getPaste(paste_id))
    return result["code"], result


def action_get_langs(opts, args):
    """Get supported language highlighting types from server"""
    result = _check_success(_paste(opts).getLanguages())
    return "\n".join(result["langs"]), result


def action_add_short_url(opts, args):
    """Add short-URL: <url>

    <url>        Short-URL to add
    """
    url = args.pop(0)
    result = _check_success(_paste(opts).addShortURL(url))
    return result["url"], result


def action_get_short_url(opts, args):
    """Resolve short-URL: <url>

    <url>        Short-URL to get clicks of
    """
    url = args.pop(0)
    result = _check_success(_paste(opts).resolveShortURL(url))
    return result["url"], result


def action_get_short_url_clicks(opts, args):
    """Get clicks of short-URL: <url>

    <url>        Short-URL to get clicks of
    """
    url = args.pop(0)
    result = _check_success(_paste(opts).ShortURLClicks(url))
    return result["count"], result


def action_help(opts, args):
    """Print more verbose help about specific action: <action>

    <action>    Topic on which you need more verbose help.
    """
    if len(args) < 1:
        alias = "help"
    else:
        alias = args.pop(0)

    if alias in actions:
        function_name = actions[alias]
        print(inspect.getdoc(globals()[function_name]))
        print(
            "\naliases: "
            + " ".join([i for i in actions_r[function_name] if i != alias])
        )
    else:
        print("Error: No such command - %s" % (alias))
        OPT_PARSER.print_usage()
    sys.exit(0)


# action_add_paste -> [add, a]
actions_r = {}

# add -> action_add_paste
# a   -> action_add_paste
actions = {}

# option parser
OPT_PARSER = None


##
# MAIN
##
if __name__ == "__main__":
    action_specs = [
        "action_add_paste add a",
        "action_del_paste del d rm",
        "action_get_paste get g",
        "action_get_langs getlangs gl langs l",
        "action_add_short_url addurl",
        "action_get_short_url geturl",
        "action_get_short_url_clicks getclicks",
        "action_help     help",
    ]
    for action_spec in action_specs:
        aliases = action_spec.split()
        cmd = aliases.pop(0)
        actions_r[cmd] = aliases
    for (action_name, v) in actions_r.items():
        for i in v:
            actions[i] = action_name

    usage = (
        "usage: %prog [options] ACTION <args>\n\n"
        + "actions:\n"
        + "\n".join(
            [
                "%12s\t%s"
                % (v[0], inspect.getdoc(globals()[action_name]).splitlines()[0])
                for (action_name, v) in actions_r.items()
            ]
        )
    )
    running_user = getpass.getuser()
    parser = optparse.OptionParser(usage=usage)
    parser.add_option("-n", "--name", default=running_user, help="Name of poster")
    parser.add_option(
        "-e",
        "--expire",
        type=int,
        default=72,
        metavar="HOURS",
        help="Time at which paste should expire",
    )
    parser.add_option(
        "-l", "--lang", default="Plain", help="Type of language to highlight"
    )
    parser.add_option(
        "-p",
        "--private",
        action="count",
        dest="private",
        default=0,
        help="Create hidden paste",
    ),
    parser.add_option("-s", "--server", default=DEFAULT_SERVER, help="Paste server")
    parser.add_option("-v", "--verbose", action="count", default=0, help="More output")
    (opts, args) = parser.parse_args()
    OPT_PARSER = parser

    if len(args) == 0:
        parser.error("Please provide me with an action")
    elif args[0] in actions:
        cmd = args.pop(0)
        action = actions[cmd]
        try:
            (msg, ret) = globals()[action](opts, args)
            if opts.verbose == 0:
                print(msg)
            else:
                print(ret)
        except ActionFailedException as except_inst:
            sys.stderr.write("Server Error: %s\n" % except_inst.what())
            if opts.verbose > 0:
                print(except_inst.dwhat())
            sys.exit(1)
    else:
        parser.error("Unknown action: %s" % args[0])
