from xscontainer import api_helper
from xscontainer import ssh_helper
from xscontainer import util
from xscontainer.util import log

import re
import simplejson

DOCKER_SOCKET_PATH = '/var/run/docker.sock'
ERROR_CAUSE_NETWORK = (
    "Error: Cannot find a valid IP that allows SSH connections to "
    "the VM. Please make sure that Tools are installed, a "
    "network route is set up, there is a SSH server running inside "
    "the VM that is reachable from Dom0.")


def prepare_request_cmd():
    return ("ncat -U %s" % (DOCKER_SOCKET_PATH))


def prepare_request_stdin(request_type, request):
    return ("%s %s HTTP/1.0\r\n\r\n" % (request_type, request))


def _interact_with_api(session, vmuuid, request_type, request,
                       message_error=False):
    provided_stdin = prepare_request_stdin(request_type, request)
    stdout = ssh_helper.execute_ssh(session, vmuuid, prepare_request_cmd(),
                                    stdin_input=provided_stdin)
    headerend = stdout.index('\r\n\r\n')
    header = stdout[:headerend]
    body = stdout[headerend + 4:]
    # ToDo: Should use re
    headersplits = header.split('\r\n', 2)[0].split(' ')
    # protocol = headersplits[0]
    statuscode = headersplits[1]
    if statuscode[0] != '2':
        # this did not work
        status = ' '.join(headersplits[2:])
        failure_title = "Container Management Error"
        failure_body = body.strip()
        if failure_body == "":
            if statuscode == "304":
                # 304 does not have a body and is quite common.
                failure_body = ("The requested operation is currently not "
                                "possible. Please try again later.")
            else:
                failure_body = ("The requested operation failed.")
        failure_body = failure_body + " (" + statuscode + ")"
        if ":" in failure_body:
            (failure_title, failure_body) = failure_body.split(":", 1)
        if message_error:
            api_helper.send_message(session, vmuuid, failure_title,
                                    failure_body)
        message = ("Request '%s' led to status %s - %s: %s"
                   % (request, status, failure_title, failure_body))
        log.info(message)
        raise util.XSContainerException(message)
    return body


def _get_api_json(session, vmuuid, request):
    stdout = _interact_with_api(session, vmuuid, 'GET', request)
    results = simplejson.loads(stdout)
    return results


def _post_api(session, vmuuid, request):
    stdout = _interact_with_api(session, vmuuid, 'POST', request,
                                message_error=True)
    return stdout


def _verify_or_throw_container(container):
    if not re.match('^[a-z0-9_.-]+$', container):
        raise util.XSContainerException("Invalid container")


def patch_docker_ps_status(ps_dict):
    """
    Patch the status returned by Docker PS in order to avoid a stale
    value being presented to the user. Due to the fact that xscontainer
    only updates docker info when a docker event happens, it does not
    register the regular status updates for the increase in container
    run time. E.g. "Up 40 seconds" ... "Up 4 hours".

    The tempoary solution is to just return "Up", "Up (Paused)" or Exited (rc).
    """
    status = ps_dict["Status"]
    if status.startswith("Up"):
        if status.endswith("(Paused)"):
            ps_dict["Status"] = "Up (Paused)"
        else:
            ps_dict["Status"] = "Up"
    elif status.startswith("Exited ("):
        closing_bracket_index = status.rfind(')')
        if closing_bracket_index:
            ps_dict["Status"] = ps_dict["Status"][0:closing_bracket_index + 1]
    return


def get_ps_dict(session, vmuuid):
    container_results = _get_api_json(session, vmuuid,
                                      '/containers/json?all=1&size=1')
    return_results = []
    for container_result in container_results:
        container_result['Names'] = container_result['Names'][0][1:]
        # Do some patching for XC - ToDo: patch XC to not require these
        container_result['Container'] = container_result['Id'][:10]
        patch_docker_ps_status(container_result)
        patched_result = {}
        for (key, value) in container_result.iteritems():
            patched_result[key.lower()] = value
        return_results.append({'entry': patched_result})
    return return_results


def get_ps_xml(session, vmuuid):
    result = {'docker_ps': get_ps_dict(session, vmuuid)}
    return util.converttoxml(result)


def get_info_dict(session, vmuuid):
    return _get_api_json(session, vmuuid, '/info')


def get_info_xml(session, vmuuid):
    result = {'docker_info': get_info_dict(session, vmuuid)}