#!/usr/bin/env python

import argparse

import peachtree
from peachtree import machine_description
from peachtree import writers
from peachtree import arggroup


def main():
    parser = argparse.ArgumentParser()
    parser.add_argument("--qemu-data-dir", help=argparse.SUPPRESS)
    parser.add_argument(
        "--output-format",
        choices=writers.writer_names(),
        default="human",
    )
    
    subparsers = parser.add_subparsers()
    
    for command_name, command_builder in _commands.iteritems():
        command = command_builder()
        subparser = subparsers.add_parser(command_name)
        subparser.set_defaults(func=command.execute)
        command.create_parser(subparser)
    
    args = parser.parse_args()
    writer = writers.find_writer_by_name(args.output_format)
    
    provider = peachtree.qemu_provider(data_dir=args.qemu_data_dir)
    args.func(provider, writer, args)


class RunCommand(object):
    def create_parser(self, subparser):
        subparser.add_argument('image')
        subparser.add_argument('--public-port', action='append', default=[])
    
    def execute(self, provider, writer, args):
        public_ports = map(int, args.public_port)
        machine = provider.start(args.image, public_ports=public_ports)
        writer.write_result(_describe_machine(machine))


class RunManyCommand(object):
    def create_parser(self, subparser):
        request_parser = arggroup.add_repeatable_argument_group(subparser, "--request")
        request_parser.add_argument("--name", required=True)
        request_parser.add_argument("--image", required=True)
        request_parser.add_argument('--public-port', action='append', default=[])
    
    def execute(self, provider, writer, args):
        requests_arg = args.request
        
        def create_request(request_arg):
            return peachtree.request_machine(
                name=request_arg.name,
                image_name=request_arg.image,
                public_ports=map(int, request_arg.public_port),
            )
            
        requests = map(create_request, requests_arg)
        
        machine_set = provider.start_many(requests)
        
        writer.write_result(map(_describe_machine, machine_set))


class DescribeCommand(object):
    def create_parser(self, subparser):
        subparser.add_argument('identifier')
    
    def execute(self, provider, writer, args):
        machine = provider.find_running_machine(args.identifier)
        writer.write_result(_describe_machine(machine))


class DescribeAllCommand(object):
    def create_parser(self, subparser):
        pass
    
    def execute(self, provider, writer, args):
        machines = provider.list_running_machines()
        writer.write_result(map(_describe_machine, machines))


class ListCommand(object):
    def create_parser(self, subparser):
        pass
    
    def execute(self, provider, writer, args):
        machines = provider.list_running_machines()
        # TODO: use writer to format result
        for machine in machines:
            print machine.identifier, machine.image_name
            
            
class StopCommand(object):
    def create_parser(self, subparser):
        subparser.add_argument('identifier')
    
    def execute(self, provider, writer, args):
        machine = provider.find_running_machine(args.identifier)
        if machine is not None:
            machine.destroy()
        
            
class CronCommand(object):
    def create_parser(self, subparser):
        pass
    
    def execute(self, provider, writer, args):
        provider.cron()


class PublicPortCommand(object):
    def create_parser(serlf, subparser):
        subparser.add_argument('identifier')
        subparser.add_argument("port", type=int)
    
    def execute(self, provider, writer, args):
        machine = provider.find_running_machine(args.identifier)
        if machine is None:
            external_port = None
        else:
            external_port = machine.public_port(args.port)
        writer.write_result(external_port)
        

_commands = {
    "run": RunCommand,
    "run-many": RunManyCommand,
    "describe": DescribeCommand,
    "describe-all": DescribeAllCommand,
    "list-running": ListCommand,
    "stop": StopCommand,
    "cron": CronCommand,
    "public-port": PublicPortCommand,
}


def _describe_machine(machine):
    if machine is None:
        return None
    else:
        return machine_description.describe_machine(machine)


if __name__ == "__main__":
    main()
