#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Copyright 2010 California Institute of Technology. ALL RIGHTS RESERVED.
# 
# 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.
# 
# United States Government Sponsorship acknowledged. This software is subject to
# U.S. export control laws and regulations and has been classified as 'EAR99 NLR'
# (No [Export] License Required except when exporting to an embargoed country,
# end user, or in support of a prohibited end use). By downloading this software,
# the user agrees to comply with all applicable U.S. export laws and regulations.
# The user has the responsibility to obtain export licenses, or other export
# authority as may be required before exporting this software to any 'EAR99'
# embargoed foreign country or citizen of those countries.
#
# Author: Giangi Sacco
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~



from __future__ import print_function
from iscesys.ImageApi import DataAccessor as DA
import os
## If you finalize more than once, do you get an error?
ERROR_CHECK_FINALIZE = False

class DataAccessor(object):
    _accessorType = ''

    @staticmethod
    def getTypeSizeS(type_):
        return DA.getTypeSize(type_)
    def __init__(self):
        self._accessor = None
        self._factory = None
        self.scheme = ''
        self.caster = ''
        self.width = None
        self.bands = None
        self.length = None
        self.accessMode = ''
        self.filename = ''
        self.dataType = ''
        self._size = None
        #instead of creating a new function for each type of Accessor to be created
        #in the c bindings, pass a dictionary which contains the key 'type' to know the accessor that
        #needs to be instanciated
        self._extraInfo = {}
        self._extra_reader = 'vrt'
        return None

    ## Experimental
    def __int__(self):
        return self.getAccessor()

    def initAccessor(self, filename, filemode, width,
                     type=None, bands=None, scheme=None, caster=None):
        self.filename = filename
        self.accessMode = filemode
        self.width = int(width)
        if type:
            self.dataType = type
        if bands:
            self.bands = int(bands)
        if scheme:
            self.scheme = scheme
        if caster:
            self.caster = caster
        return None
    def getGDALDataTypeId(self,type_):
        #from GDALDataType enum
        map = {'byte':1,'ciqbyte':1,'short':3,'int':4,'float':6,'double':7,
               'cshort':8,'cint':9,'cfloat':10,'cdouble':11}
        try:
            return map[type_.lower()]
        except:
            print('Unsupported  datatype',type_)
            raise Exception

    def checkLocation(self):
        from iscesys.Parsers.FileParserFactory import createFileParser
        parser = createFileParser('xml')
        #get the properties from the file
        prop, fac, misc = parser.parse(self.metadatalocation)
        #first check if it exists as it is
        filename = ''

        if not (os.path.exists(prop['file_name'])):
            name = os.path.basename(prop['file_name'])
            #check the path relative to the xml file
            filename = os.path.join(os.path.split(self.metadatalocation)[0],name)
            #check if relative to cwd
            if not (os.path.exists(filename)):
                filename = os.path.join(os.getcwd(),name)
                if not (os.path.exists(filename)):
                    filename = ''
        else:
            filename = prop['file_name']
        if not filename:
            paths = self.uniquePath([os.path.split(prop['file_name'])[0],os.path.split(self.metadatalocation)[0],
                  os.getcwd()])
            toptr = '\n'.join(paths)
            print('The image file',name,'specified in the metadata file',self.metadatalocation,
                  'cannot be found in', 'any of the following default locations:' if len(paths) > 1 else 'in the following location:' ,
                  toptr)
            raise Exception

        return filename
    def uniquePath(self,paths):
        ret = []
        for pth in paths:
            if not pth in ret:
                ret.append(pth)
        return ret

    def methodSelector(self):
        selection = ''
        if self._accessorType.lower() == 'api':
            selection = 'api'
        elif self._accessorType.lower() == self._extra_reader:
            selection = self._extra_reader
        elif self.accessMode.lower() == 'write':
            selection='api'
        elif self.accessMode.lower() == 'read':
            selection = self._extra_reader

        return selection

    def createAccessor(self):
        if(not self.filename and hasattr(self,'metadatalocation') and self.metadatalocation and not self.accessMode.lower().count('write')):
            #it will only keep going if all ok
            self.filename = self.checkLocation()
        caster = '' or self.caster
        filename = self.filename
        scheme = self.scheme
        self.extraFilename = self.filename + '.' + self._extra_reader

        if self._accessor is None:#to avoid creating duplicates
            selection = self.methodSelector()
            if selection == 'api':
                size = DA.getTypeSize(self.dataType)
                #to optimize bip access per band we read in memory all bands and then
                #set the right band and write the content back leaving the other bands untouched
                #this requires a read and write which only works if the file is opened in
                #writeread (or readwrite) mode and not just write
                if(self.accessMode.lower() == 'write'):
                #if(self.scheme.lower() == 'bip' and self.accessMode.lower() == 'write'):
                    self.accessMode = 'writeread'
            elif selection == self._extra_reader:
                size = self.getGDALDataTypeId(self.dataType)
                filename = self._extraFilename
                #GDALAccessor handles all the different scheme in the same way since it reads
                #always in BSQ scheme regardless of the under laying scheme
                scheme = 'GDAL'
            else:
                print('Cannot select appropruiate image API')
                raise Exception
            self._accessor, self._factory = DA.createAccessor(
                filename, self.accessMode, size, self.bands,
                self.width,scheme,caster,self._extraInfo
                )
        return None

    def finalizeAccessor(self):
        try:
            DA.finalizeAccessor(self._accessor, self._factory)
        except TypeError:
            message = "Image %s is already finalized" % str(self)
            if ERROR_CHECK_FINALIZE:
                raise RuntimeError(message)
            else:
                print(message)

        self._accessor = None
        self._factory = None
        return None

    def getTypeSize(self):
        return DA.getTypeSize(self.dataType)
    def rewind(self):
        DA.rewind(self._accessor)

    def createFile(self, lines):
        DA.createFile(self._accessor, lines)

    def getFileLength(self):
        openedHere = False

        if self._accessor is None:
            openedHere = True
            self.initAccessor(self.filename, 'read', int(self.width),
                              self.dataType, int(self.bands), self.scheme)
            self.createAccessor()
        length = DA.getFileLength(self._accessor)

        if openedHere:
            self.finalizeAccessor()

        return length

    def getAccessor(self):
        return self._accessor

    def getFilename(self):
        return self.filename

    def getAccessMode(self):
        return self.accessMode

    def getSize(self):
        return self.size

    def getBands(self):
        return self.bands

    ## Get the width associated to the DataAccessor.DataAccessor object created.
    #@return \c int width of the DataAccessor.DataAccessor object.
    def getWidth(self):
        return self.width

    def getInterleavedScheme(self):
        return self.scheme

    def getCaster(self):
        return self.caster

    def getDataType(self):
        return self.dataType

    def setFilename(self, val):
        self.filename = str(val)

    def setAccessMode(self, val):
        self.accessMode = str(val)

    def setBands(self, val):
        self.bands = int(val)

    def setWidth(self, val):
        self.width = int(val)

    def setInterleavedScheme(self, val):
        self.scheme = str(val)

    def setCaster(self, val):
        self.caster = val

    def setDataType(self, val):
        self.dataType = val

    def setExtraInfo(self,ei):
        self._extraInfo = ei
    pass
