#!/usr/bin/env python

# Cloudcraft
# Copyright 2013, Sudharshan S
# See COPYING for more.

from ConfigParser import ConfigParser
from optparse import OptionParser

import logging
import subprocess
import os
import errno
import json

import cloudcraft
lib_path = os.path.dirname(cloudcraft.__file__)

log = logging.getLogger("cloudcraft")

# http://stackoverflow.com/a/600612
def mkdir_p(path):
    try:
        os.makedirs(path)
    except OSError as exc: # Python >2.5
        if exc.errno == errno.EEXIST and os.path.isdir(path):
            pass
        else: raise


def initialize(cloudcraft_home):
    # Error out if we find a file of the same name?
    mkdir_p(cloudcraft_home)
    mkdir_p(os.path.join(cloudcraft_home, "instances"))
    mkdir_p(os.path.join(cloudcraft_home, "keys"))
    mkdir_p(os.path.join(cloudcraft_home, "archives"))
    config_template = os.path.join(lib_path, "config", "config.template")

    config = os.path.join(cloudcraft_home, "cloudcraft.conf")
    log.debug("Checking if {0} exists".format(config))
    if not os.path.exists(config):
        log.warning("{0} not found.".format(config))
        print
        print "Initalizing cloudcraft configuration at {0}".format(config)
        print "Cloudcraft needs your AWS keys to run"
        print "This can be accessed from https://portal.aws.amazon.com/gp/aws/securityCredentials#access_credentials"
        token, secret = "", ""
        try:
            while (token.strip() == ""):
                token = raw_input("AWS access key: ")
            while (secret.strip() == ""):
                secret = raw_input("AWS access secret: ")
            print
            print "Validating the AWS keys"
            api = cloudcraft.aws.AWS(token, secret)
            api.conn.get_all_instances()
        except EOFError, KeyboardInterrupt:
            print "Bye!"
            exit(-1)
        except:
            log.error("API Key validation failed! Config file was not written.")
            exit(-1)
        f = open(config_template).read()
        with open(config, "w") as c:
            c.write(f.format(token=token, secret=secret))
        print "Wrote config at {0}. Run the same command once again to continue.".format(config)
        exit(0)
    return config



# FIXME: Heavy refactor needed
def despatch(conf, mcserver_name, command, cloudcraft_home, command_args=[]):
    # Generate this usage information dynamically once we move on to a more
    # functional way of despatching commands
    """
Quickstart:
  spawn SERVER - Spawns an instance on the cloud and writes metadata to CLOUDCRAFT_HOME/instances/SERVER
  setup SERVER - Bootstraps, fetches and starts the minecraft server in SERVER

Minecraft server commands:
  start SERVER
  stop SERVER
  save SERVER
  logs SERVER    - Print the minecraft server logs
  cleanup SERVER - Stop the server and remove the minecraft directory

Bukkit plugin management:
  plugin SERVER install URL      - Installs plugin at URL
  plugin SERVER uninstall PLUGIN - <Not Implemented yet>
  plugin SERVER update URL       - Update plugin from URL

Machine commands:
  shutdown SERVER
  reboot SERVER
  boot SERVER
  destroy SERVER - Terminates the EC2 instance
  info SERVER    - Dumps the current machine and minecraft installation status

Others:
  bootstrap SERVER - Bootstraps the SERVER for use (e.g, installation of Java)
  list keys|instances - Lists the keys and the instances.
    """
    token = conf.get("amazon", "AWS_ACCESS_TOKEN")
    secret = conf.get("amazon", "AWS_ACCESS_SECRET")
    region = conf.get("amazon", "EC2_REGION")
    instance_type = conf.get("amazon", "INSTANCE_TYPE")
    security_group = conf.get("amazon", "SECURITY_GROUP")
    ami = conf.get("amazon", "AMI")
    user = "ubuntu"

    key_name = "cloudcraft-{0}".format(region)
    key_path = os.path.join(cloudcraft_home, "keys")

    aws_api = cloudcraft.aws.AWS(token, secret, region=region)

    metadata_path = os.path.join(cloudcraft_home, "instances", mcserver_name)
    mcs = cloudcraft.mcserver.load_server(metadata_path)

    if command == "spawn":
        if mcs:
            log.error("{0} already exists.".format(mcserver_name))
            exit(-1)
        else:
            log.info("Spawning a '{0}' '{1}' instance in region '{2}'".format(ami, instance_type, region))
            log.info("You can change these settings in '{0}'".format(config))
            print
            i = raw_input("Continue? (y/n) [n]: ").lower()
            if not i or (i == "n") or (i != "y"):
                return True
            key = aws_api.sync_keypair(key_name, key_path)
            if not key:
                return True
            aws_api.sync_security_group(security_group)
            mcs = aws_api.spawn(mcserver_name, ami=ami, instance_type=instance_type,
                                key_name=key_name, security_group=security_group,
                                user=user)
            if mcs:
                log.info("Instance {0} is up and running.".format(mcs.name))
                cloudcraft.mcserver.save_server(mcs, metadata_path)
            else:
                log.error("Couldn't spawn instance {0}".format(mcs.name))
                exit(-1)
    elif command in ["destroy", "reboot", "boot", "shutdown", "info"]:
        if not mcs:
            log.error("Instance {0} doesn't exist".format(mcserver_name))
            exit(-1)
        if command == "destroy":
            log.warning("'{0}' will be lost for ever".format(mcs.name))
            print
            i = raw_input("Are you sure? (y/n) [n]: ").lower()
            if not i or (i == "n") or (i != "y"):
                log.info("Not destroying '{0}'".format(mcs.name))
                return True
        res = getattr(aws_api, command)(mcs)
        if not res:
            log.error("Couldn't find instance '{0}'. Has it been terminated already?".format(mcs.name))
            return True
        if command == "destroy":
            log.debug("Deleting instance metadata")
            os.remove(metadata_path)

    elif command == "list":
        if args[1] in ["instances", "keys"]:
            print "\n".join(os.listdir(os.path.join(cloudcraft_home, args[1])))
        else:
            parser.error("list needs 'instances' or 'keys'. Invalid parameter {0}".format(mcserver_name))
            return False
    else:
        if not mcs:
            parser.error("Invalid instance")
        ec2_instance = aws_api.get_instance(mcs.server_id)
        remote_vars = dict(conf.items("minecraft"))
        remote_vars["server_name"] = mcs.name
        if ec2_instance.state != "running":
            log.error("Instance {0} is not running. If you had run 'boot' recently, give it a couple of minutes for {0} to boot up.".format(mcs.name))
            exit(-1)
        key_file = os.path.join(key_path, "{0}.pem".format(key_name))
        if command == "setup":
            command = ["bootstrap", "fetch", "start"]
        mcs.run_command(ec2_instance.public_dns_name,
                        command, key_file, remote_vars=remote_vars,
                        command_args=command_args)
    return True


def get_arg_parser():
    usage = "usage: %prog [options] command params\n" + despatch.__doc__
    cloudcraft_home = os.path.join(os.path.expanduser("~"), ".cloudcraft")
    if os.environ.get("CLOUDCRAFT_HOME"):
        cloudcraft_home = os.environ["CLOUDCRAFT_HOME"]
    log.debug("Setting CLOUDCRAFT_HOME to {0}".format(cloudcraft_home))

    parser = OptionParser(usage=usage)
    parser.add_option("-H", "--home", dest="cloudcraft_home", default=cloudcraft_home,
                      help="Cloudcraft home")
    parser.add_option("-l", "--log", dest="log", default="INFO",
                      help="Log level. Use DEBUG for verbose output")
    return parser


def get_logger(level):
    lvl = getattr(logging, level.upper(), logging.INFO)
    if not isinstance(lvl, int):
        raise ValueError('Invalid log level: %s' % level)
    logging.basicConfig(format="%(levelname)s: %(message)s")
    log.setLevel(lvl)
    return log


if __name__ == '__main__':
    parser = get_arg_parser()
    (options, args) = parser.parse_args()
    get_logger(options.log)

    config = initialize(options.cloudcraft_home)

    if len(args) < 2:
        parser.error("Missing parameter")

    if config:
        conf = ConfigParser()
        conf.read(config)
        command = args[0]
        mcserver_name = args[1]
        command_args = args[2:]
        if not despatch(conf, mcserver_name, command, options.cloudcraft_home,
                        command_args=command_args):
            parser.print_usage()
    else:
        log.error("Config file missing")
        exit(-1)
