#!/usr/bin/env python
# encoding: UTF-8

import asyncio
from collections import deque
from collections import namedtuple
import concurrent.futures
import datetime
import functools
import logging
import operator
import re
import textwrap
import uuid
import xml.etree.ElementTree as ET
import xml.sax.saxutils

import aiohttp
from chameleon import PageTemplateFile
import pkg_resources
from sqlalchemy import desc

from cloudhands.burst.agent import Agent
from cloudhands.burst.agent import Job
from cloudhands.burst.control import create_node
from cloudhands.burst.control import describe_node
from cloudhands.burst.control import destroy_node
from cloudhands.burst.utils import find_xpath
from cloudhands.burst.utils import unescape_script
from cloudhands.common.discovery import providers
from cloudhands.common.discovery import settings
from cloudhands.common.schema import Appliance
from cloudhands.common.schema import CatalogueChoice
from cloudhands.common.schema import Component
from cloudhands.common.schema import IPAddress
from cloudhands.common.schema import Label
from cloudhands.common.schema import NATRouting
from cloudhands.common.schema import Node
from cloudhands.common.schema import OSImage
from cloudhands.common.schema import Provider
from cloudhands.common.schema import ProviderReport
from cloudhands.common.schema import ProviderToken
from cloudhands.common.schema import Touch
from cloudhands.common.states import ApplianceState

__doc__ = """


.. graphviz::

   digraph appliance {
    center = true;
    compound = true;
    nodesep = 0.6;
    edge [decorate=true,labeldistance=3,labelfontname=helvetica,
        labelfontsize=10,labelfloat=false];

    subgraph cluster_states {
        label = "Static";
        configuring -> pre_provision [style=invis];
        pre_check -> pre_operational [style=invis];
        pre_operational -> pre_delete [style=invis];
        pre_delete -> deleted [style=invis];
        deleted -> pre_stop [style=invis];
        stopped -> pre_start [style=invis];
        pre_start -> running [style=invis];

    subgraph cluster_super {
        label = "Active";
        node [height=1,width=2];
        provisioning -> operational [style=invis];
    }

    }

    pre_provision -> provisioning [style=invis];
    operational -> pre_check [style=invis];

    configuring -> pre_provision [taillabel="user"];
    operational -> pre_check [ltail=cluster_super,taillabel="user"];
    operational -> pre_stop [taillabel="user"];

    subgraph cluster_agents {
        label = "Burst controller";
        style = filled;
        node [shape=box];
        pre_provision_agent -> provisioning_agent [style=invis];
        provisioning_agent -> pre_check_agent [style=invis];
        pre_check_agent -> pre_operational_agent [style=invis];
        pre_operational_agent -> pre_delete_agent [style=invis];
        pre_delete_agent -> pre_stop_agent [style=invis];
        pre_stop_agent -> pre_start_agent [style=invis];
    }

    pre_provision -> pre_provision_agent[style=dashed,arrowhead=none];
    pre_provision_agent -> provisioning;

    provisioning -> provisioning_agent [taillabel="delay",style=dashed,arrowhead=none];
    provisioning_agent -> pre_check;
    pre_check -> pre_check_agent [style=dashed,arrowhead=none];
    pre_check_agent -> operational;
    pre_check_agent -> pre_operational;
    pre_operational -> pre_operational_agent [style=dashed,arrowhead=none];
    pre_operational -> pre_stop [taillabel="out of resource"];
    pre_operational_agent -> operational;
    pre_delete -> pre_delete_agent [style=dashed,arrowhead=none];
    pre_delete_agent -> deleted;
    pre_stop -> pre_stop_agent [style=dashed,arrowhead=none];
    pre_stop_agent -> stopped;
    stopped -> pre_delete [taillabel="user"];
    stopped -> pre_start [taillabel="user"];
    pre_start -> pre_start_agent [style=dashed,arrowhead=none,weight=2];
    pre_start_agent -> running [tailport=w,weight=6];
    running -> pre_stop [taillabel="user"];
   }
"""

customizationScript = """#!/bin/sh
if [ x$1 == x"precustomization" ]; then
echo "Precustomisation"
elif [ x$1 == x"postcustomization" ]; then
echo "Postcustomisation"
/usr/local/bin/activator.sh {host}/appliance/{uuid}
fi
"""

find_catalogueitems = functools.partial(
    find_xpath, ".//*[@type='application/vnd.vmware.vcloud.catalogItem+xml']",
    namespaces={"": "http://www.vmware.com/vcloud/v1.5"})

find_catalogues = functools.partial(
    find_xpath, "./*/[@type='application/vnd.vmware.vcloud.catalog+xml']")

def find_customizationsection(tree): 
    elems = find_xpath(
        ".//*[@type='application/vnd.vmware.vcloud.guestCustomizationSection+xml']",
        tree, namespaces={"": "http://www.vmware.com/vcloud/v1.5"})
    return (i for i in elems if i.tag.endswith("CustomizationSection"))

def find_customizationscript(tree): 
    return (i for s in find_customizationsection(tree) for i in s
            if i.tag.endswith("CustomizationScript"))

find_gatewayserviceconfiguration = functools.partial(
    find_xpath,
    ".//*[@type='application/vnd.vmware.admin.edgeGatewayServiceConfiguration+xml']")

def find_ipranges(tree, namespace="http://www.vmware.com/vcloud/v1.5"): 
    ranges = tree.iter("{{{}}}IpRange".format(namespace))
    for r in ranges:
        yield (
            r.find("{{{}}}StartAddress".format(namespace)),
            r.find("{{{}}}EndAddress".format(namespace)))

def find_networkconnectionsection(tree): 
    elems = find_xpath(
        ".//*[@type='application/vnd.vmware.vcloud.networkConnectionSection+xml']",
        tree, namespaces={"": "http://www.vmware.com/vcloud/v1.5"})
    return (i for i in elems if i.tag.endswith("NetworkConnectionSection"))

def find_networkconnection(tree): 
    return (i for s in find_networkconnectionsection(tree) for i in s
            if i.tag.endswith("NetworkConnection"))

def find_networkconfigsection(tree): 
    elems = find_xpath(
        ".//*[@type='application/vnd.vmware.vcloud.networkConfigSection+xml']",
        tree, namespaces={"": "http://www.vmware.com/vcloud/v1.5"})
    return (i for i in elems if i.tag.endswith("NetworkConfigSection"))

def find_networkconfig(tree): 
    return (i for s in find_networkconfigsection(tree) for i in s
            if i.tag.endswith("NetworkConfig"))

find_networkinterface = functools.partial(
    find_xpath, ".//*[@type='application/vnd.vmware.admin.network+xml']",
    namespaces={"": "http://www.vmware.com/vcloud/v1.5"})

find_orgs = functools.partial(
    find_xpath, "./*/[@type='application/vnd.vmware.vcloud.org+xml']")

find_records = functools.partial(
    find_xpath, "./*/[@type='application/vnd.vmware.vcloud.query.records+xml']")

find_results = functools.partial(find_xpath, "./*")

find_templates = functools.partial(
    find_xpath, ".//*[@type='application/vnd.vmware.vcloud.vAppTemplate+xml']",
    namespaces={"": "http://www.vmware.com/vcloud/v1.5"})

find_vdcs = functools.partial(
    find_xpath, "./*/[@type='application/vnd.vmware.vcloud.vdc+xml']")

find_vms = functools.partial(
    find_xpath, ".//*[@type='application/vnd.vmware.vcloud.vm+xml']")

def find_catalogrecords(text):