# Allen Institute Software License - This software license is the 2-clause BSD
# license plus a third clause that prohibits redistribution for commercial
# purposes without further permission.
#
# Copyright 2014-2016. Allen Institute. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# 3. Redistributions for commercial purposes are not permitted without the
# Allen Institute's written permission.
# For purposes of this license, commercial purposes is the incorporation of the
# Allen Institute's software into anything for which you will charge fees or
# other compensation. Contact terms@alleninstitute.org for commercial licensing
# opportunities.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
import logging

from allensdk.config.manifest import Manifest


class Description(object):
    _log = logging.getLogger(__name__)

    def __init__(self):
        self.data = {}
        self.reserved_data = []
        self.manifest = Manifest()

    def update_data(self, data, section=None):
        '''Merge configuration data possibly from multiple files.

        Parameters
        ----------
        data : dict
            Configuration structure to add.
        section : string, optional
            What configuration section to read it into if the file does not specify.
        '''
        if section is None:
            for (section, entries) in data.items():
                if section not in self.data:
                    self.data[section] = entries
                else:
                    self.data[section].extend(entries)
        else:
            if section not in self.data:
                self.data[section] = []

            self.data[section].append(data)

    def is_empty(self):
        '''Check if anything is in the object.

        Returns
        -------
        boolean
            true if self.data is missing or empty
        '''
        if self.data:
            return False

        return True

    def unpack(self, data, section=None):
        '''Read the manifest and other stand-alone configuration structure,
        or insert a configuration object into a section of an existing configuration.

        Parameters
        ----------
        data : dict
            A configuration object including top level sections,
            or an configuration object to be placed within a section.
        section : string, optional.
            If this is present, place data within an existing section array.
        '''
        if section is None:
            self.unpack_manifest(data)
            self.update_data(data)
        else:
            self.update_data(data, section)

    def unpack_manifest(self, data):
        '''Pull the manifest configuration section into a separate place.

        Parameters
        ----------
        data : dict
            A configuration structure that still has a manifest section.
        '''
        data_manifest = data.pop("manifest", {})
        reserved_data = {"manifest": data_manifest}
        self.reserved_data.append(reserved_data)
        self.manifest.load_config(data_manifest)

    def fix_unary_sections(self, section_names=None):
        ''' Wrap section contents that don't have the proper
        array surrounding them in an array.

        Parameters
        ----------
        section_names : list of strings, optional
            Keys of sections that might not be in array form.
        '''
        if section_names is None:
            section_names = []

        for section in section_names:
            if section in self.data:
                if type(self.data[section]) is dict:
                    self.data[section] = [self.data[section]]
                    Description._log.warn(
                        "wrapped description section %s in an array." % (section))
