# 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 2015-2017. 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.
#
from .rma_api import RmaApi
from allensdk.api.cache import cacheable, Cache
from allensdk.core.obj_utilities import read_obj
import allensdk.core.sitk_utilities as sitk_utilities
import numpy as np
import nrrd
import six


class ReferenceSpaceApi(RmaApi):

    AVERAGE_TEMPLATE = 'average_template'
    ARA_NISSL = 'ara_nissl'
    MOUSE_2011 = 'annotation/mouse_2011'
    DEVMOUSE_2012 = 'annotation/devmouse_2012'
    CCF_2015 = 'annotation/ccf_2015'
    CCF_2016 = 'annotation/ccf_2016'
    CCF_2017 = 'annotation/ccf_2017'
    CCF_VERSION_DEFAULT = CCF_2017

    VOXEL_RESOLUTION_10_MICRONS = 10
    VOXEL_RESOLUTION_25_MICRONS = 25
    VOXEL_RESOLUTION_50_MICRONS = 50
    VOXEL_RESOLUTION_100_MICRONS = 100


    def __init__(self, base_uri=None):
        super(ReferenceSpaceApi, self).__init__(base_uri=base_uri)


    @cacheable(strategy='create',
               reader=nrrd.read,
               pathfinder=Cache.pathfinder(file_name_position=3,
                                           path_keyword='file_name'))
    def download_annotation_volume(self,
                                   ccf_version,
                                   resolution,
                                   file_name):
        '''
        Download the annotation volume at a particular resolution.

        Parameters
        ----------
        ccf_version: string
            Which reference space version to download. Defaults to "annotation/ccf_2017"
        resolution: int
            Desired resolution to download in microns.
            Must be 10, 25, 50, or 100.
        file_name: string
            Where to save the annotation volume.
        
        Note: the parameters must be used as positional parameters, not keywords
        '''

        if ccf_version is None:
            ccf_version = ReferenceSpaceApi.CCF_VERSION_DEFAULT

        self.download_volumetric_data(ccf_version,
                                      'annotation_%d.nrrd' % resolution, 
                                      save_file_path=file_name)


    @cacheable(strategy='create', reader=sitk_utilities.read_ndarray_with_sitk, 
               pathfinder=Cache.pathfinder(file_name_position=3,
                                           path_keyword='file_name'))
    def download_mouse_atlas_volume(self, age, volume_type, file_name):
        '''Download a reference volume (annotation, grid annotation, atlas volume) 
        from the mouse brain atlas project

        Parameters
        ----------
        age : str
            Specify a mouse age for which to download the reference volume
        volume_type : str
            Specify the type of volume to download
        file_name : str
            Specify the path to the downloaded volume
        '''

        remote_file_name = '{}_{}.zip'.format(age, volume_type)
        url = '/'.join([ self.informatics_archive_endpoint, 
                         'current-release', 'mouse_annotation', 
                         remote_file_name ])

        self.retrieve_file_over_http(url, file_name, zipped=True)


    @cacheable(strategy='create',
               reader=nrrd.read,
               pathfinder=Cache.pathfinder(file_name_position=2,
                                           path_keyword='file_name'))
    def download_template_volume(self, resolution, file_name):
        '''
        Download the registration template volume at a particular resolution.

        Parameters
        ----------

        resolution: int
            Desired resolution to download in microns.  Must be 10, 25, 50, or 100.

        file_name: string
            Where to save the registration template volume.
        '''
        self.download_volumetric_data(ReferenceSpaceApi.AVERAGE_TEMPLATE,
                                      'average_template_%d.nrrd' % resolution, 
                                      save_file_path=file_name)

    @cacheable(strategy='create', 
               reader=nrrd.read, 
               pathfinder=Cache.pathfinder(file_name_position=4, 
                                           path_keyword='file_name'))
    def download_structure_mask(self, structure_id, ccf_version, resolution, file_name):
        '''Download an indicator mask for a specific structure.

        Parameters
        ----------
        structure_id : int
            Unique identifier for the annotated structure
        ccf_version : string
            Which reference space version to download. Defaults to "annotation/ccf_2017"
        resolution : int
            Desired resolution to download in microns.  Must be 10, 25, 50, or 100.
        file_name : string
             Where to save the downloaded mask.

        '''

        if ccf_version  is None:
            ccf_version = ReferenceSpaceApi.CCF_VERSION_DEFAULT

        structure_mask_dir = 'structure_masks_{0}'.format(resolution)
        data_path = '{0}/{1}/{2}'.format(ccf_version, 'structure_masks', structure_mask_dir)        
        remote_file_name = 'structure_{0}.nrrd'.format(structure_id)

        try:
            self.download_volumetric_data(data_path, remote_file_name, save_file_path=file_name)
        except Exception as e:
            self._file_download_log.error('''We weren't able to download a structure mask for structure {0}. 
                                             You can instead build the mask locally using 
                                             ReferenceSpace.many_structure_masks''')
            raise


    @cacheable(strategy='create', 
               reader=read_obj, 
               pathfinder=Cache.pathfinder(file_name_position=3, 
                                           path_keyword='file_name'))
    def download_structure_mesh(self, structure_id, ccf_version, file_name):
        '''Download a Wavefront obj file containing a triangulated 3d mesh built 
        from an annotated structure.

        Parameters
        ----------
        structure_id : int
            Unique identifier for the annotated structure
        ccf_version : string
            Which reference space version to download. Defaults to "annotation/ccf_2017"
        file_name : string
             Where to save the downloaded mask.

        '''

        if ccf_version  is None:
            ccf_version = ReferenceSpaceApi.CCF_VERSION_DEFAULT

        data_path = '{0}/{1}'.format(ccf_version, 'structure_meshes')        
        remote_file_name = '{0}.obj'.format(structure_id)

        try:
            self.download_volumetric_data(data_path, remote_file_name, save_file_path=file_name)
        except Exception as e:
            self._file_download_log.error('unable to download a structure mesh for structure {0}.'.format(structure_id))
            raise


    def build_volumetric_data_download_url(self,
                                           data_path,
                                           file_name,
                                           voxel_resolution=None,
                                           release=None,
                                           coordinate_framework=None):
        '''Construct url to download 3D reference model in NRRD format.

        Parameters
        ----------
        data_path : string
            'average_template', 'ara_nissl', 'annotation/ccf_{year}', 
            'annotation/mouse_2011', or 'annotation/devmouse_2012'
        voxel_resolution : int
            10, 25, 50 or 100
        coordinate_framework : string
            'mouse_ccf' (default) or 'mouse_annotation'

        Notes
        -----
        See: `3-D Reference Models <http://help.brain-map.org/display/mouseconnectivity/API#API-3DReferenceModels>`_
        for additional documentation.
        '''

        if voxel_resolution is None:
            voxel_resolution = ReferenceSpaceApi.VOXEL_RESOLUTION_10_MICRONS

        if release is None:
            release = 'current-release'

        if coordinate_framework is None:
            coordinate_framework = 'mouse_ccf'

        url = ''.join([self.informatics_archive_endpoint,
                       '/%s/%s/' % (release, coordinate_framework),
                       data_path,
                       '/',
                       file_name])

        return url


    def download_volumetric_data(self,
                                 data_path,
                                 file_name,
                                 voxel_resolution=None,
                                 save_file_path=None,
                                 release=None,
                                 coordinate_framework=None):
        '''Download 3D reference model in NRRD format.

        Parameters
        ----------
        data_path : string
            'average_template', 'ara_nissl', 'annotation/ccf_{year}', 
            'annotation/mouse_2011', or 'annotation/devmouse_2012'
        file_name : string
            server-side file name. 'annotation_10.nrrd' for example.
        voxel_resolution : int
            10, 25, 50 or 100
        coordinate_framework : string
            'mouse_ccf' (default) or 'mouse_annotation'

        Notes
        -----
        See: `3-D Reference Models <http://help.brain-map.org/display/mouseconnectivity/API#API-3DReferenceModels>`_
        for additional documentation.
        '''
        url = self.build_volumetric_data_download_url(data_path,
                                                      file_name,
                                                      voxel_resolution,
                                                      release,
                                                      coordinate_framework)

        if save_file_path is None:
            save_file_path = file_name

        if save_file_path is None:
            save_file_path = 'volumetric_data.nrrd'

        self.retrieve_file_over_http(url, save_file_path)

