"""
 SlipStream Client
 =====
 Copyright (C) 2015 SixSq Sarl (sixsq.com)
 =====
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
"""

import time

import slipstream.util as util
import slipstream.exceptions.Exceptions as Exceptions

from slipstream.util import override
from slipstream.cloudconnectors.BaseCloudConnector import BaseCloudConnector
from slipstream.utils.ssh import generate_keypair

from slipstream.UserInfo import UserInfo
from slipstream.ConfigHolder import ConfigHolder
import os

import xmlrpclib
import ssl
import urllib
import re
import base64

try:
    import xml.etree.cElementTree as eTree  # c-version, faster
except ImportError:
    import xml.etree.ElementTree as eTree  # python version


def getConnector(config_holder):
    return getConnectorClass()(config_holder)


def getConnectorClass():
    return OpenNebulaClientCloud


def searchInObjectList(list_, property_name, property_value):
    for element in list_:
        if isinstance(element, dict):
            if element.get(property_name) == property_value:
                return element
        else:
            if getattr(element, property_name) == property_value:
                return element
    return None


def instantiate_from_cimi(cimi_connector, cimi_cloud_credential):
    user_info = UserInfo(cimi_connector['instanceName'])

    cloud_params = {
        UserInfo.CLOUD_USERNAME_KEY: cimi_cloud_credential['key'],
        UserInfo.CLOUD_PASSWORD_KEY: cimi_cloud_credential['secret'],
        'endpoint': cimi_connector.get('endpoint')
    }

    user_info.set_cloud_params(cloud_params)

    config_holder = ConfigHolder(options={'verboseLevel': 0, 'retry': False})

    os.environ['SLIPSTREAM_CONNECTOR_INSTANCE'] = cimi_connector['instanceName']

    connector_instance = OpenNebulaClientCloud(config_holder)

    connector_instance._initialization(user_info)

    return connector_instance


class OpenNebulaClientCloud(BaseCloudConnector):
    VM_STATE = [
        'Init',       # 0
        'Pending',    # 1
        'Hold',       # 2
        'Active',     # 3
        'Stopped',    # 4
        'Suspended',  # 5
        'Done',       # 6
        '//Failed',   # 7
        'Poweroff',   # 8
        'Undeployed'  # 9
    ]

    VM_LCM_STATE = [
        'Lcm init',                         # 0
        'Prolog',                           # 1
        'Boot',                             # 2
        'Running',                          # 3
        'Migrate',                          # 4
        'Save stop',                        # 5
        'Save suspend',                     # 6
        'Save migrate',                     # 7
        'Prolog migrate',                   # 8
        'Prolog resume',                    # 9
        'Epilog stop',                      # 10
        'Epilog',                           # 11
        'Shutdown',                         # 12
        '//Cancel',                         # 13
        '//Failure',                        # 14
        'Cleanup resubmit',                 # 15
        'Unknown',                          # 16
        'Hotplug',                          # 17
        'Shutdown poweroff',                # 18
        'Boot unknown',                     # 19
        'Boot poweroff',                    # 20
        'Boot suspended',                   # 21
        'Boot stopped',                     # 22
        'Cleanup delete',                   # 23
        'Hotplug snapshot',                 # 24
        'Hotplug nic',                      # 25
        'Hotplug saveas',                   # 26
        'Hotplug saveas poweroff',          # 27
        'Hotplug saveas suspended',         # 28
        'Shutdown undeploy',                # 29
        'Epilog undeploy',                  # 30
        'Prolog undeploy',                  # 31
        'Boot undeploy',                    # 32
        'Hotplug prolog poweroff',          # 33
        'Hotplug epilog poweroff',          # 34
        'Boot migrate',                     # 35
        'Boot failure',                     # 36
        'Boot migrate failure',             # 37
        'Prolog migrate failure',           # 38
        'Prolog failure',                   # 39
        'Epilog failure',                   # 40
        'Epilog stop failure',              # 41
        'Epilog undeploy failure',          # 42
        'Prolog migrate poweroff',          # 43
        'Prolog migrate poweroff failure',  # 44
        'Prolog migrate suspend',           # 45
        'Prolog migrate suspend failure',   # 46
        'Boot undeploy failure',            # 47
        'Boot stopped failure',             # 48
        'Prolog resume failure',            # 49
        'Prolog undeploy failure',          # 50
        'Disk snapshot poweroff',           # 51
        'Disk snapshot revert poweroff',    # 52
        'Disk snapshot delete poweroff',    # 53
        'Disk snapshot suspended',          # 54
        'Disk snapshot revert suspended',   # 55
        'Disk snapshot delete suspended',   # 56
        'Disk snapshot',                    # 57
        'Disk snapshot revert',             # 58
        'Disk snapshot delete',             # 59
        'Prolog migrate unknown',           # 60
        'Prolog migrate unknown failure'    # 61
    ]

    IMAGE_STATE = [
        'Init',       # 0
        'Ready',      # 1
        'Used',       # 2
        'Disabled',   # 3
        'Locked',     # 4
        'Error',      # 5
        'Clone',      # 6
        'Delete',     # 7
        'Used_pers'   # 8
    ]

    def _resize(self, node_instance):
        raise Exceptions.ExecutionException(
            '{0} doesn\'t implement resize feature.'.format(self.__class__.__name__))

    def _detach_disk(self, node_instance):
        raise Exceptions.ExecutionException(
            '{0} doesn\'t implement detach disk feature.'.format(self.__class__.__name__))

    def _attach_disk(self, node_instance):
        raise Exceptions.ExecutionException(
            '{0} doesn\'t implement attach disk feature.'.format(self.__class__.__name__))

    cloudName = 'opennebula'

    def __init__(self, config_holder):

        super(OpenNebulaClientCloud, self).__init__(config_holder)

        self._set_capabilities(contextualization=True,
                               direct_ip_assignment=True,
                               orchestrator_can_kill_itself_or_its_vapp=True)
        self.user_info = None

    def _rpc_execute(self, command, *args):
        proxy = self._create_rpc_connection()
        remote_function = getattr(proxy, command)
        success, output_or_error_msg, err_code = \
            remote_function(self._create_session_string(), *args)
        if not success:
            raise Exceptions.ExecutionException(output_or_error_msg)
        return output_or_error_msg

    @override
    def _initialization(self, user_info, **kwargs):
        util.printStep('Initialize the OpenNebula connector.')
        self.user_info = user_info

        if self.is_build_image():