#!/usr/bin/python -Es
# Copyright (C) 2014-2015 Red Hat
# AUTHOR: Dan Walsh <dwalsh@redhat.com>
# see file 'COPYING' for use and warranty information
#
# atomic is a tool for managing Atomic Systems and Containers
#
#    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, write to the Free Software
#    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
#    02110-1301 USA.
#
#
import sys
import os
import argparse
import gettext
import docker
import subprocess

import Atomic

PROGNAME = "atomic"
gettext.bindtextdomain(PROGNAME, "/usr/share/locale")
gettext.textdomain(PROGNAME)
try:
    # pylint: disable=unexpected-keyword-arg
    gettext.install(PROGNAME, unicode=True, codeset='utf-8')
except TypeError:
    # Failover to python3 install
    gettext.install(PROGNAME, codeset='utf-8')
except IOError:
    import builtins
    builtins.__dict__['_'] = str


class HelpByDefaultArgumentParser(argparse.ArgumentParser):

    def error(self, message):
        sys.stderr.write('%s: %s\n' % (sys.argv[0], message))
        sys.stderr.write("Try '%s --help' for more information.\n" % self.prog)
        sys.exit(2)


if __name__ == '__main__':
    atomic = Atomic.Atomic()
    parser = HelpByDefaultArgumentParser(description=atomic.help())
    parser.add_argument('-v', '--version', action='version', version=Atomic.__version__)
    subparser = parser.add_subparsers(help=_("commands"))

    if os.path.exists("/usr/bin/rpm-ostree"):
        # atomic host
        hostp = subparser.add_parser("host", help=_("execute Atomic host "
                                                    "commands"))
        host_subparser = hostp.add_subparsers(help=_("host commands"))

        # atomic host rollback
        rollbackp = host_subparser.add_parser(
            "rollback", help=_("switch to alternate tree at boot"))
        rollbackp.set_defaults(func=atomic.host_rollback)
        rollbackp.add_argument("-r", "--reboot", dest="reboot",
                               action="store_true",
                               help=_("initiate a reboot after rollback is "
                                      "prepared"))

        # atomic host status
        statusp = host_subparser.add_parser(
            "status", help=_("list information about all deployments"))
        statusp.set_defaults(func=atomic.host_status)

        # atomic host upgrade
        upgradep = host_subparser.add_parser(
            "upgrade", help=_("upgrade to the latest Atomic tree if one "
                              "is available"))
        upgradep.set_defaults(func=atomic.host_upgrade)
        upgradep.add_argument("-r", "--reboot", dest="reboot",
                              action="store_true",
                              help=_("if an upgrade is available, reboot "
                                     "after deployment is complete"))

        # atomic host rebase
        rebasep = host_subparser.add_parser(
            "rebase", help=_("Download and deploy a new origin refspec"))
        rebasep.set_defaults(func=atomic.host_rebase)
        rebasep.add_argument("refspec",
                             help=_("Origin refspec for new deployment"))

    # atomic info
    infop = subparser.add_parser(
        "info", help=_("display label information about an image"),
        epilog="atomic info attempts to read and display the LABEL "
        "information about an image")
    infop.set_defaults(func=atomic.info)
    infop.add_argument("--remote", dest="force_remote_info",
                       action='store_true', default=False,
                       help=_('ignore local images and only scan registries'))
    infop.add_argument("image", help=_("container image"))

    # atomic install
    installp = subparser.add_parser(
        "install", help=_("execute container image install method"),
        epilog="atomic install attempts to read the LABEL INSTALL field "
        "in the image, if it does not exist atomic will just pull "
        "the image on to your machine.  You could add a LABEL "
        "INSTALL command to your Dockerfile like: 'LABEL INSTALL "
        "%s'" % atomic.print_install())
    installp.set_defaults(func=atomic.install)
    installp.add_argument("--opt1", dest="opt1",
                          help="additional arguments to be passed as ${OPT1} "
                               "for install command.")
    installp.add_argument("--opt2", dest="opt2",
                          help="additional arguments to be passed as ${OPT2} "
                               "for install command.")
    installp.add_argument("--opt3", dest="opt3",
                          help="additional arguments to be passed as ${OPT3} "
                               "for install command.")
    installp.add_argument("-n", "--name", dest="name", default=None,
                          help=_("name of container"))
    installp.add_argument(
        "--display",
        default=False,
        action="store_true",
        help=_("preview the command that %s would execute") % sys.argv[0])
    installp.add_argument("image", help=_("container image"))
    installp.add_argument("args", nargs=argparse.REMAINDER,
                          help=_("Additional arguments appended to the image "
                                 "uninstall method"))

    # atomic images
    imagesp = subparser.add_parser(
        "images", help=_("list container images on your system"),
        epilog="atomic images by default will list all installed "
        "container images on your system.  Using the --prune "
        "option, will free up disk space deleting unused "
        "'dangling' images")
    imagesp.set_defaults(func=atomic.images)
    imagesp.add_argument("--prune", action="store_true",
                         help=_("prune dangling images"))

    # atomic mount
    mountp = subparser.add_parser(
        "mount", help=_("mount container image to a specified directory"),
        epilog="atomic mount attempts to mount a container image to a "
        "specified directory so that its contents may be "
        "inspected.")
    mountp.set_defaults(func=atomic.mount)
    mountp.add_argument("-o", "--options", dest="options", default="",
                        help=_("comma-separated list of mount options, "
                               "defaults are 'ro,nodev,nosuid'"))
    mountp.add_argument("--live", dest="live", action="store_true",
                        help=_("mount a running container 'live', allowing "
                               "modification of the contents."))
    mountp.add_argument("image", help=_("image/container id"))
    mountp.add_argument("mountpoint", help=_("filesystem location to mount "
                                             "the image/container"))

    # atomic stop
    stopp = subparser.add_parser(
        "stop", help=_("execute container image stop method"),
        epilog="atomic will just stop the container if it is running, if "
        "image does not specify LABEL STOP")
    stopp.set_defaults(func=atomic.stop)
    stopp.add_argument("-n", "--name", dest="name", default=None,
                       help=_("name of container"))
    stopp.add_argument("image", help=_("container image"))

    # atomic run
    runp = subparser.add_parser(
        "run", help=_("execute container image run method"),
        epilog="atomic run defaults to the following command, if image "
        "does not specify LABEL run\n'%s'" % atomic.print_run())
    runp.set_defaults(func=atomic.run)
    runp.add_argument("--opt1", dest="opt1",
                      help="additional arguments to be passed as ${OPT1} for "
                      "run command.")
    runp.add_argument("--opt2", dest="opt2",
                      help="additional arguments to be passed as ${OPT2} for "
                           "run command.")
    runp.add_argument("--opt3", dest="opt3",
                      help="additional arguments to be passed as ${OPT3} for "
                           "run command.")
    runp.add_argument("-n", "--name", dest="name", default=None,
                      help=_("name of container"))
    runp.add_argument("--spc", default=False, action="store_true",
                      help=_("use super privileged container mode: '%s'" %
                             atomic.print_spc()))
    runp.add_argument("image", help=_("container image"))
    runp.add_argument("command", nargs=argparse.REMAINDER,
                      help=_("command to execute within the container. "
                             "If container is not running, command is appended"
                             "to the image run method"))

    runp.add_argument(
        "--display",
        default=False,
        action="store_true",
        help=_("preview the command that %s would execute") % sys.argv[0])

    # atomic uninstall
    uninstallp = subparser.add_parser(
        "uninstall", help=_("execute container image uninstall method"),
        epilog="atomic uninstall attempts to read the LABEL UNINSTALL "
        "field in the image, if it does not exist atomic will "
        "remove the image from your machine.  You could add a "
        "LABEL UNINSTALL command to your Dockerfile like: 'LABEL "
        "UNINSTALL %s'" % atomic.print_uninstall())
    uninstallp.set_defaults(func=atomic.uninstall)
    uninstallp.add_argument("--opt1", dest="opt1",
                            help="additional arguments to be passed as "
                                 "${OPT1} for uninstall command.")
    uninstallp.add_argument("--opt2", dest="opt2",
                            help="additional arguments to be passed as "
                                 "${OPT2} for uninstall command.")
    uninstallp.add_argument("--opt3", dest="opt3",
                            help="additional arguments to be passed as "
                                 "${OPT3} for uninstall command.")
    uninstallp.add_argument("-n", "--name", dest="name", default=None,
                            help=_("name of container"))
    uninstallp.add_argument("-f", "--force", default=False, dest="force",
                            action="store_true",
                            help=_("remove all containers based on this "
                                   "image"))
    uninstallp.add_argument("image", help=_("container image"))
    uninstallp.add_argument("args", nargs=argparse.REMAINDER,
                            help=_("Additional arguments appended to the "
                                   "image uninstall method"))

    # atomic unmount
    unmountp = subparser.add_parser(
        "unmount", help=_("unmount container image"),
        epilog="atomic unmount will unmount a container image previously "
        "mounted with atomic mount")
    unmountp.set_defaults(func=atomic.unmount)
    unmountp.add_argument("mountpoint",
                          help=_("filesystem location of image/container to "
                                 "be unmounted"))

    # atomic update
    updatep = subparser.add_parser(
        "update", help=_("pull latest container image from repository"),
        epilog="atomic update downloads the latest container image. If a "
        "previously created  container based on this image exists, "
        "the container will continue to use the old image.  Use "
        "--force to remove the outdated container.")
    updatep.set_defaults(func=atomic.update)
    updatep.add_argument("-f", "--force", default=False, dest="force",
                         action="store_true",
                         help=_("remove all containers based on this image"))
    updatep.add_argument("image", help=_("container image"))

    # atomic upload
    uploadp = subparser.add_parser(
        "upload", help=_("upload latest image to repository"),
        epilog="atomic upload uploads the latest specified image.")
    uploadp.set_defaults(func=atomic.upload)

    # making it so we cannot call both the --pulp and --satellite commands
    # at the same time (mutually exclusive)
    upgroup = uploadp.add_mutually_exclusive_group()
    upgroup.add_argument("--pulp",
                         default=False,
                         action="store_true",
                         help=_("upload image using pulp"))
    upgroup.add_argument("--satellite",
                         default=False,
                         action="store_true",
                         help=_("upload image using Satellite"))

    uploadp.add_argument("--verify_ssl",
                         default=None,
                         action="store_true",
                         help=_("flag to verify ssl of registry"))
    uploadp.add_argument("--debug",
                         default=None,
                         action="store_true",
                         help=_("debug mode"))
    uploadp.add_argument("-U", "--url",
                         dest="url",
                         default=None,
                         help=_("URL for remote registry"))
    uploadp.add_argument("-u", "--username",
                         default=None,
                         dest="username",
                         help=_("Username for remote registry"))
    uploadp.add_argument("-p", "--password",
                         default=None,
                         dest="password",
                         help=_("Password for remote registry"))
    uploadp.add_argument("image", help=_("container image"))
    uploadp.add_argument("-a", "--activation_key",
                         default=None,
                         dest="activation_key",
                         help=_("Activation Key"))
    uploadp.add_argument("--repo_id", "--repository_id",
                         default=None,
                         dest="repo_id",
                         help=_("Repository ID"))
    # uploadp.add_argument("--activation_key_name",
    #                      default=None,
    #                      dest="activation_key_name",
    #                      help=_("Activation Key Name"))
    # uploadp.add_argument("--repo_name", "--repository_name",
    #                      default=None,
    #                      dest="repo_name",
    #                      help=_("Repository Name"))
    # uploadp.add_argument("--org_name", "--organization_name",
    #                      default=None,
    #                      dest="org_name",
    #                      help=_("Organization Name"))
    versionp = subparser.add_parser("version",
                                    help=_("display image 'Name Version Release' label"),
                                    epilog="atomic version displays the image version information, if it is provided")
    versionp.add_argument("-r", "--recurse",
                         default=False,
                         dest="recurse",
                         action="store_true",
                         help=_("recurse through all layers"))
    # atomic version
    versionp = subparser.add_parser(
        "version", help=_("display image 'Name Version Release' label"),
        epilog="atomic version displays the image version information, if "
        "it is provided")
    versionp.add_argument("-r", "--recurse", default=False, dest="recurse",
                          action="store_true",
                          help=_("recurse through all layers"))
    versionp.set_defaults(func=atomic.print_version)
    versionp.add_argument("image", help=_("container image"))

    # atomic verify
    verifyp = subparser.add_parser(
        "verify", help=_("verify image is fully updated"),
        epilog="atomic verify checks whether there is a newer image "
        "available and scans through all layers to see if any of "
        "the sublayers have a new version available")
    verifyp.set_defaults(func=atomic.print_verify)
    verifyp.add_argument("image", help=_("container image"))

    try:
        args = parser.parse_args()
        atomic.set_args(args)
        sys.exit(args.func())
    except ValueError as e:
        sys.stderr.write("%s\n" % str(e))
        sys.exit(1)
    except IOError as e:
        sys.stderr.write("%s\n" % str(e))
        sys.exit(1)
    except KeyboardInterrupt:
        sys.exit(0)
    except subprocess.CalledProcessError as e:
        sys.stderr.write("\n")
        sys.exit(e.returncode)
    except docker.errors.DockerException as e:
        sys.stderr.write("%s\n" % str(e))
        sys.exit(1)
