#    Copyright 2013 Mirantis, Inc.
#
#    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.

from collections import deque
import copy
import logging
import time

import six

from shotgun import settings


logger = logging.getLogger(__name__)


class Config(object):
    def __init__(self, data=None):
        self.data = data or {}
        self.time = time.localtime()
        self.offline_hosts = set()
        self.objs = deque()
        self.try_again = deque()
        for properties in six.itervalues(self.data.get('dump', {})):
            hosts = properties.get('hosts') or [{}]
            for obj in properties.get('objects', []):
                for h in hosts:
                    obj_new = copy.deepcopy(obj)
                    obj_new['host'] = h
                    self.objs.append(obj_new)

    def _timestamp(self, name):
        return "{0}-{1}".format(
            name,
            time.strftime('%Y-%m-%d_%H-%M-%S', self.time)
        )

    @property
    def target(self):
        target = self.data.get("target", settings.TARGET)
        if self.data.get("timestamp", settings.TIMESTAMP):
            target = self._timestamp(target)
        return target

    @property
    def compression_level(self):
        level = self.data.get("compression_level")
        if level is None:
            logger.info(
                'Compression level is not specified,'
                ' Default %s will be used', settings.COMPRESSION_LEVEL)

        level = settings.COMPRESSION_LEVEL

        return '-{level}'.format(level=level)

    @property
    def lastdump(self):
        return self.data.get("lastdump", settings.LASTDUMP)

    @staticmethod
    def get_network_address(obj):
        """Returns network address of object."""
        return obj['host'].get('address') or obj['host'].get('hostname')

    def on_network_error(self, obj):
        """Lets the object to have another attempt for being proccessed."""
        host = self.get_network_address(obj)
        logger.debug("Remote host %s is unreachable. "
                     "Processing of its objects postponed.", host)
        self.try_again.append(obj)
        self.offline_hosts.add(host)

    @property
    def objects(self):
        """Stateful generator for processing objects.

        It should be used in conjunction with on_network_error() to give
        another try for objects which threw NetworkError.
        """
        for _ in range(settings.ATTEMPTS):
            while self.objs:
                obj = self.objs.popleft()
                if self.get_network_address(obj) not in self.offline_hosts:
                    yield obj
                else:
                    self.try_again.append(obj)
            self.offline_hosts.clear()
            self.objs, self.try_again = self.try_again, deque()

        for obj in self.objs:
            obj["type"] = 'offline'
            host = self.get_network_address(obj)
            if host not in self.offline_hosts:
                self.offline_hosts.add(host)
                yield obj
            else:
                logger.debug("Skipping offline object processing: %s", obj)

    @property
    def timeout(self):
        """Timeout for executing commands."""
        return self.data.get("timeout", settings.DEFAULT_TIMEOUT)

    @property
    def self_log_object(self):
        return {
            "type": "file",
            "path": settings.LOG_FILE
        }
