#   %W%  %G% CSS
#
#   "spec" Release %R%
#
"""
class DEigerClient provides an interface to the EIGER API

Author: Volker Pilipp
Contact: support@dectris.com
Version: 1.0
Date: 13/11/2014
Copyright See General Terms and Conditions (GTC) on http://www.dectris.com

"""
import threading

import sys
import time
import base64
import os.path

if sys.version_info[0] >= 3:
    import http.client as http
    from urllib.request import urlopen
    from io import IOBase
else:
    import httplib as http
    from urllib2 import urlopen

import json
import re
import fnmatch
import shutil

from pyspec.hardware.server import log

Version = '1.5.0'

TRIGGER_NO_MAX = 1000000
#MAX_TIMEOUT = 60 # sixty seconds
MAX_TIMEOUT = 10*60 # ten minutes
# MAX_TIMEOUT = 24*3600 # one full day

class CommandHandlerThread(threading.Thread):

    commandTimeout = MAX_TIMEOUT

    def __init__(self, host, port, url, command, data, mimeType):
        log.log(2,"creating command thread for command %s" % command)
        self._connection = http.HTTPConnection(host,port,timeout= self.commandTimeout)
        self.url = url
        self.cmd_name = command
        self.data = data
        self.mimeType = mimeType 
        threading.Thread.__init__(self)
        self.answer = None

    def getCommandName(self):
        return self.cmd_name

    def getCommandAnswer(self):
        return self.answer

    def run(self):
        self.answer = self.run_command(self.cmd_name, self.url)

        if self.answer is not None:
            return self.answer
        else:
            return self.error()

    def run_command(self, cmdname, url):
        log.log(2,"command %s - (%s) is running" % (self.cmd_name, cmdname))

        headers = {}
        headers['Content-type'] = self.mimeType
        
        self._connection.request('PUT', url, body = self.data, headers = headers)
        response = self._connection.getresponse()
        status = response.status
        data = response.read()

        if status == 200:
            data_l = json.loads(data)
            if isinstance(data_l,dict) and cmdname in ['arm', 'trigger']:
                log.log(2, "answer to %s is : %s" % (cmdname, str(data_l)))
                if 'sequence_id' in data_l.keys():
                    answer = data_l['sequence id']
                else:
                    log.log(3, "answer to %s contains keys: %s" % (cmdname, data_l.keys()))
                    answer = data_l
            else: 
                answer = data_l
            log.log(2, "response from %s command :  %s" % (cmdname, str(answer)))
            return answer
        else:
            log.log(1, "cannot get response from %s command - status: %s" % (cmdname,status))
            return None

    def error(self):
        self.answer = None
        return '.error.'

class StartAcquisitionThread(CommandHandlerThread):

    cmd_name = 'startacq'

    def __init__(self, host, port, urls):

        CommandHandlerThread.__init__(self, host, port, None, 'startacq', data='', mimeType='text/html')

        self.urls = urls

        self.trigger_mode = None
        self.ntrigger = None
        self.count_time = None

    def set_arm_needed(self, needed):
        self.arm_needed = needed

    def set_trigger_mode(self, trigger_mode):
        self.trigger_mode = trigger_mode

    def set_ntrigger(self, value):
        self.ntrigger = value

    def set_count_time(self, value):
        self.count_time = value

    def set_par(self, name, value):
        headers = {}
        headers['Content-type'] = 'application/json; charset=utf-8'
        data = json.dumps({'value':value})
        log.log(2,"start thread - setting %s to %s" % (name,value))
        url = self.urls['config'] + '{0}'.format(name)

        numberOfTries = 0
        response = None

        try:
            while response is None:
                try:
                    self._connection.request('PUT',url, body = data, headers = headers)
                    response = self._connection.getresponse()
                except Exception as e:
                    numberOfTries += 1
                    if numberOfTries == 50:
                        log.log(2,"Terminate after {0} tries\n".format(numberOfTries))
                        raise e
                    log.log(2,"Failed to connect to host. Retrying\n")
                    self._connection = http.HTTPConnection(self._host,self._port, timeout = self._connectionTimeout)
                    continue

            status = response.status
            reason = response.reason
            log.log(2,"response arrived status:%s / reason. %s" % (status,reason))
            data = response.read()
        except:
            log.log(2,'setpar error')
        finally:
            pass

        log.log(2,"setting %s returns %s " % (name,str(data)))

    def send_cmd(self, cmd):
        log.log(2, "start thread - starting command %s" % cmd)
        url = self.urls['command'] + '{0}'.format(cmd)
        aret = self.run_command(cmd, url)
        return aret

    def run(self):
        try:
            if self.trigger_mode in ['ints', 'inte']:
                if self.arm_needed: 
                    if self.ntrigger == 1:
                        log.log(2,"programming big trigger no")
                        self.set_par("ntrigger",TRIGGER_NO_MAX)
                    elif self.ntrigger == TRIGGER_NO_MAX:
                        log.log(3,"ntrigger is big")
                    else:
                        log.log(2,"programming ntrigger to %s" % self.ntrigger)
                        self.set_par(self.ntrigger)

                    if self.count_time is not None:
                        self.set_par("count_time", self.count_time)
                        self.set_par("frame_time", self.count_time+0.00002)

                    aret = self.send_cmd("arm")
                    log.log(2, "arm command finished. returned: %s" % aret)
    
                    if aret is None:
                        return self.error()

                else:
                    aret = None

                tret = self.send_cmd("trigger")
                log.log(2, "trigger command finished. returned: %s" % aret)

                if tret is not None:
                    if aret is None:
                        self.answer = tret
                        log.log(2, "startacq finished. returning t -  %s " % tret)
                        return
                    else:
                        log.log(2, "startacq finished. returning a -  %s " % aret)
                        self.answer = aret
                        return

            else:  # external trigger
                aret = self.send_cmd("arm")
                log.log(2, "arm command finished. returned: %s" % aret)
                self.set_par("ntrigger", self.ntrigger)
    
                if aret is None:
                    return self.error()

        except BaseException as exc:
            import traceback
            log.log(1,"error starting acquisition %s" % traceback.format_exc())
            
        return self.error()

class FileWriterThread(threading.Thread):
   
    commandTimeout = MAX_TIMEOUT

    def __init__(self, host, port, urlprefix, filename, targetDir):
        self.target_path = os.path.join(targetDir,filename)
        self.url = 'http://{0}:{1}/{2}data/{3}'.format(host, port, urlprefix, filename)
        threading.Thread.__init__(self)
    
    def run(self):
        req = urlopen(self.url, timeout = self.commandTimeout)

        try:
            with open(self.target_path, 'wb') as fp:
                log.log(2,'writing ' + self.target_path)
                shutil.copyfileobj(req, fp, 512*1024)
                assert os.access(self.target_path,os.R_OK)
            return "ok"
        except:
            import traceback
            log.log(1, 'cannot write ' + self.target_path)
            log.log(1,  traceback.format_exc() )
            return ".error."

# noinspection PyInterpreter
class DEigerClient(object):
    """
    class DEigerClient provides a low level interface to the EIGER API
    """

    def __init__(self, host = '127.0.0.1', port = 80, verbose = False, urlPrefix = None, user = None):
        """
        Create a client object to talk to the EIGER API.
        Args:
            host: hostname of the detector computer
            port: port usually 80 (http)
            verbose: bool value
            urlPrefix: String prepended to the urls. Should be None. Added for future convenience.
            user: "username:password". Should be None. Added for future convenience.
        """
        super(DEigerClient,self).__init__()
        self._host = host
        self._port = port
        self._version = Version
        self._verbose = verbose
        self._urlPrefix = ""
        self._user = None
        self._connectionTimeout = MAX_TIMEOUT

        log.log(2, "DEigerClient created - host: %s / port: %s (verbose=%s)" % (self._host, self._port, self._verbose))

        #pingret = os.system("ping -c 1 -w 1 -W 10 %s > /dev/null 2>&1" % self._host)
        #if pingret != 0:
            #raise BaseException("Cannot access DCU")

        try:
            self._connection = http.HTTPConnection(self._host,self._port, timeout = self._connectionTimeout)
        except BaseException as exc:
            log.log(1,"Exception while connecting to Eiger DCU %s" % str(exc))
            raise(e)

        self._serializer = None
        
        self.detector_command = None
        self.cmd_answer = {}

        self.read_thread = None
        self.read_value = {}
        self.setUrlPrefix(urlPrefix)
        self.setUser(user)
        
    def serializer(self):
        """
        The serializer object shall have the methods loads(string) and dumps(obj), which load
        the string from json into a python object or store a python object into a json string
        """
        return self._serializer
        
    def setSerializer(self,serializer):
        """
        Set an explicit serializer object that converts native python objects to json string and vice versa.
        The serializer object shall have the methods loads(string) and dumps(obj), which load
        the string from json into a python object or store a python object into a json string
        """
        self._serializer = serializer

    def setVerbose(self,verbose):
        """ Switch verbose mode on and off.
        Args:
            verbose: bool value
        """
        self._verbose = bool(verbose)
        log.log(2, "DEigerClient verbose level set to %s" % (verbose))
        
    def setConnectionTimeout(self, timeout):
        """
        If DEigerClient has not received an reply from EIGER after 
        timeout seconds, the request is aborted. timeout should be at 
        least as long as the triggering command takes.
        Args:
            timeout timeout in seconds
        """
        self._connectionTimeout = timeout
        self._connection = http.HTTPConnection(self._host,self._port, timeout = self._connectionTimeout)
        
    def setUrlPrefix(self, urlPrefix):
        """Set url prefix, which is the string that is prepended to the 
        urls. There is usually no need to call the command explicitly.
        Args:
           urlPrefix: String
        """
        if urlPrefix is None:
            self._urlPrefix = ""
        else:
            self._urlPrefix = str(urlPrefix)
            if len(self._urlPrefix) > 0 and self._urlPrefix[-1] != "/":
                self._urlPrefix += "/"
                
    def setUser(self, user):
        """
        Set username and password for basic authentication.
        There is usually no need to call the command explicitly.
        Args:
           user: String of the form username:password 
        """
        if user is None:
            self._user = None
        else:
            self._user = base64.encodestring(user).replace('\n', '')
       
    def set_verbose(self, verbose):
        self._verbose = verbose

    def set_version(self, version):
        self._log("eigerclient version set to %s" % version)
        self._version = version

    def version(self,module = 'detector'):
        """
        Get version of a api module (i.e. 'detector', 'filewriter')
        Args:
            module: 'detector' or 'filewriter'
        """
        return self._getRequest(url = '/{0}{1}/api/version/'.format(self._urlPrefix,module))['value']

    def sendSystemCommand(self, command):
        """
        Sending command "restart" restarts the SIMPLON API on the EIGER control unit
        """
        return self._putRequest(self._url('system','command',command), dataType = 'native', data = None)

    def listDetectorConfigParams(self):
        """Get list of all detector configuration parameters (param arg of configuration() and setConfiguration()).
        Convenience function, that does detectorConfig(param = 'keys')
        Returns:
            List of parameters.
        """
        return self.detectorConfig('keys')

    def detectorConfig(self,param = None, dataType = None):
        """Get detector configuration parameter
        Args:
            param: query the configuration parameter param, if None get full configuration, if 'keys' get all configuration parameters.
            dataType: None (= 'native'), 'native' ( return native python object) or 'tif' (return tif data).
        Returns:
            If param is None get configuration, if param is 'keys' return list of all parameters, else return the value of
            the parameter. If dataType is 'native' a dictionary is returned that may contain the keys: value, min, max,
            allowed_values, unit, value_type and access_mode. If dataType is 'tif', tiff formated data is returned as a python
            string.
        """
        return self._getRequest(self._url('detector','config',param),dataType)

    def setDetectorConfig(self, param, value, dataType = None):
        """
        Set detector configuration parameter param.
        Args:
            param: Parameter
            value: Value to set. If dataType is 'tif' value may be a string containing the tiff data or
                   a file object pointing to a tiff file.
            dataType: None, 'native' or 'tif'. If None, the data type is auto determined. If 'native' value
                      may be a native python object (e.g. int, float, str), if 'tif' value shell contain a
                      tif file (python string or file object to tif file).
        Returns:
            List of changed parameters.
        """
        return self._putRequest(self._url('detector','config',param), dataType, value)

    def setDetectorConfigMultiple(self,*params):
        """
        Convenience function that calls setDetectorConfig(param,value,dataType = None) for
        every pair param, value in *params.
        Args:
            *params: List of successive params of the form param0, value0, param1, value1, ...
                     The parameters are set in the same order they appear in *params.
        Returns:
            List of changed parameters.
        """
        changeList = []
        p = None
        for x in params:
            if p is None:
                p = x
            else:
                data = x
                changeList += self.setDetectorConfig(param = p, value = data, dataType = None)
                p = None
        return list(set(changeList))

    def listDetectorCommands(self):
        """
        Get list of all commands that may be sent to EIGER via sendDetectorCommand().
        Returns:
            List of commands
        """
        return self._getRequest(self._url('detector','command','keys'))

    def sendDetectorCommand(self,  command, parameter = None):
        """
        Send command to EIGER. The list of all available commands is obtained via listCommands().
        Args:
            command: Detector command 
            parameter: Call command with parameter. If command = "trigger" a float parameter may be passed
        Returns:
            The commands 'arm' and 'trigger' return a dictionary containing 'sequence id'.
        """
        if command not in ['cancel', 'abort']:
            self.wait_command_end()

        url = self._url('detector','command',command)
        data, mimeType = self._prepareData(parameter,'native')
        self.detector_command = CommandHandlerThread(self._host, self._port, url, command, data, mimeType)
        self.startDetectorCommand()
        return 

    def startAcquisition(self, need_arm = True, trigger_mode='ints', ntrigger=1, count_time=None):
        if self.isExecutingCommand():
            log.log(1,"cannot start acquisition while a command is running")
            return False

        urls = {
                'config': self._url('detector','config'),
                'command': self._url('detector','command'),
               }

        self.detector_command = StartAcquisitionThread(
                                    self._host, self._port, urls)

        self.detector_command.set_arm_needed(need_arm)
        self.detector_command.set_trigger_mode(trigger_mode)
        self.detector_command.set_ntrigger(ntrigger)
        self.detector_command.set_count_time(count_time)

        self.startDetectorCommand()

        return True

    def startDetectorCommand(self):
        cmd_name = self.detector_command.getCommandName()
        self.cmd_answer[cmd_name] = None
        self.detector_command.start()

    def wait_command_end(self):
        while self.isExecutingCommand():
            self._log('waiting for running command (%s) to finish ', self.detector_command.getCommandName())
            time.sleep(0.1)

    def updateCommandStatus(self):
        if self.detector_command is None:
            return 
        
        cmd_alive = self.detector_command.is_alive()

        if cmd_alive is False:
            try:
                cmd_name = self.detector_command.getCommandName()
                ret_value = self.detector_command.getCommandAnswer()
                if cmd_name == 'check_connections':
                    try:
                        ans = ret_value['value']    
                        ret_value = ""
                        for conn in ans: 
                            ret_value += '{name}={up},'.format(**conn)
                    except:
                        ret_value = "cannot parse result"
                else:
                    ret_value = str(ret_value)
                self.cmd_answer[cmd_name] = ret_value
                log.log(4,"command %s finished execution. answer is: %s" % (cmd_name, ret_value))
            except BaseException as e:
                log.log(1,"error update command: %s" % str(e))

            # self.detector_command = None


    def isExecutingCommand(self):
        self.updateCommandStatus()
        if self.detector_command is not None:
            return self.detector_command.is_alive()
        else:
            return False

    def getSequenceID(self):
        return self.cmd_answer.get('startacq', None)

    def getCommandName(self):
        if self.detector_command is None:
            return "none"
        
        return self.detector_command.getCommandName()

    def getAnswer(self, command=None):
        if self.detector_command is None:
            return "none"
        
        latest_command = self.getCommandName()

        if command is None or command == latest_command: 
            if self.detector_command.is_alive():
                return "busy"

        if command is None:
            command = latest_command
         
        return self.cmd_answer.get(command,"none")

    def detectorStatus(self, param = 'keys'):
        """Get detector status information
        Args:
            param: query the status parameter param, if 'keys' get all status parameters.
        Returns:
            If param is None get configuration, if param is 'keys' return list of all parameters, else return dictionary
            that may contain the keys: value, value_type, unit, time, state, critical_limits, critical_values
        """
        if param == 'state' and self.isExecutingCommand():
            return {'value':'running'}

        return self._getRequest(self._url('detector','status',parameter = param))


    def fileWriterConfig(self,param = 'keys'):
        """Get filewriter configuration parameter
        Args:
            param: query the configuration parameter param, if 'keys' get all configuration parameters.
        Returns:
            If param is None get configuration, if param is 'keys' return list of all parameters, else return dictionary
            that may contain the keys: value, min, max, allowed_values, unit, value_type and access_mode
        """
        return self._getRequest(self._url('filewriter','config',parameter = param))

    def setFileWriterConfig(self,param,value):
        """
        Set file writer configuration parameter param.
        Args:
            param: parameter
            value: value to set
        Returns:
            List of changed parameters.
        """
        return self._putRequest(self._url('filewriter','config',parameter = param), dataType = 'native', data = value)

    def sendFileWriterCommand(self, command):
        """
        Send filewriter command to EIGER.
        Args:
            command: Command to send (up to now only "clear")
        Returns:
            Empty string
        """
        return self._putRequest(self._url("filewriter","command",parameter = command), dataType = "native")


    def fileWriterStatus(self,param = 'keys'):
        """Get filewriter status information
        Args:
            param: query the status parameter param, if 'keys' get all status parameters.
        Returns:
            If param is None get configuration, if param is 'keys' return list of all parameters, else return dictionary
            that may contain the keys: value, value_type, unit, time, state, critical_limits, critical_values
        """
        return self._getRequest(self._url('filewriter','status',parameter = param))

    def fileWriterFiles(self, filename = None, method = 'GET'):
        """
        Obtain file from detector.
        Args:
             filename: Name of file on the detector side. If None return list of available files
             method: 'GET' (get the content of the file) or 'DELETE' (delete file from server)
        Returns:
            List of available files if 'filename' is None,
            else if method is 'GET' the content of the file.
        """
        if method == 'GET':
            if filename is None:
                return self._getRequest(self._url('filewriter','files'))

            return self._getRequest(url = '/{0}data/{1}'.format(self._urlPrefix, filename), dataType = 'hdf5')
        elif method == 'DELETE':
            return self._delRequest(url = '/{0}data/{1}'.format(self._urlPrefix,filename))
        else:
            raise RuntimeError('Unknown method {0}'.format(method))

    def fileWriterSave(self,filename,targetDir,regex = False):
        """
        Saves filename in targetDir. If regex is True, filename is considered to be a regular expression.
        Save all files that match filename
        Args:
            filename: Name of source file, may contain the wildcards '*' and '?' or regular expressions
            targetDir: Directory, where to store the files
        """
        if regex:
            pattern = re.compile(filename)
            for f in self.fileWriterFiles():
                if pattern.match(f):
                    self.fileWriterSave(f,targetDir)
        elif any([ c in filename for c in ['*','?','[',']'] ] ):
            for f in self.fileWriterFiles():
                if fnmatch.fnmatch(f,filename):
                    self.fileWriterSave(f,targetDir)
        else:
            worker = FileWriterThread(self._host, self._port, self._urlPrefix, filename, targetDir)
            worker.start()
        return

    def monitorConfig(self,param = 'keys'):
        """Get monitor configuration parameter
        Args:
            param: query the configuration parameter param, if 'keys' get all configuration parameters.
        Returns:
            If param is 'keys' return list of all parameters, else return dictionary
            that may contain the keys: value, min, max, allowed_values, unit, value_type and access_mode
        """
        return self._getRequest(self._url('monitor','config',parameter = param))

    def setMonitorConfig(self,param,value):
        """
        Set monitor configuration parameter param.
        Args:
            param: parameter
            value: value to set
        Returns:
            List of changed parameters.
        """
        return self._putRequest(self._url('monitor','config',parameter = param), dataType = 'native', data = value)

    def monitorImages(self, param = None):
        """
        Obtain file from detector.
        Args:
             param: Either None (return list of available frames) or "monitor" (return latest frame),
                    "next"  (next image from buffer) or tuple(sequence id, image id) (return specific image)
        Returns:
            List of available frames (param = None) or tiff content of image file (param = "next", "monitor", (seqId,imgId))
        """
        if param is None:
            return self._getRequest(self._url('monitor','images',parameter = None) )
        elif param == "next":
            return self._getRequest(self._url('monitor',"images", parameter = "next"), dataType = "tif")
        elif param == "monitor":
            return self._getRequest(self._url('monitor','images',parameter = "monitor"), dataType = "tif")
        else:
            try:
                if len(param) == 2:
                    seqId = int(param[0])
                    imgId = int(param[1])
                    return self._getRequest(self._url('monitor',"images", parameter = "{0}/{1}".format(seqId,imgId) ), dataType = 'tif')
                elif len(param) == 3:
                    seqId = int(param[0])
                    imgId = int(param[1])
                    threshId = int(param[2])
                    return self._getRequest(self._url('monitor',"images", parameter = "{0}/{1}/{2}".format(seqId,imgId,threshId) ), dataType = 'tif')
            except (TypeError, ValueError):
                pass
        raise RuntimeError('Invalid parameter {0}'.format(param))

    def monitorSave(self, param, path):
        """
        Save frame to path as tiff file.
        Args:
            param: Either None (return list of available frames) or "monitor" (return latest frame),
                   "next"  (next image from buffer) or tuple(sequence id, image id) (return specific image)        
        Returns:
            None
        """
        data = None
        if param in ["next","monitor"]:
            data = self.monitorImages(param)
        else :
            try:
                int(param[0])
                int(param[1])
                data = self.monitorImages(param)
            except (TypeError, ValueError):
                pass
        if data is None:
            raise RuntimeError('Invalid parameter {0}'.format(param))
        else:
            with open(path,'wb') as f:
                self._log('Writing ', path)
                f.write(data)
            assert os.access(path,os.R_OK)
        return

    def monitorStatus(self, param = "keys"):
        """
        Get monitor status information
        Args:
            param: query the status parameter param, if 'keys' get all status parameters.
        Returns:
            Dictionary that may contain the keys: value, value_type, unit, time, state,
            critical_limits, critical_values
        """
        return self._getRequest(self._url('monitor','status',parameter = param))

    def sendMonitorCommand(self, command):
        """
        Send monitor command to EIGER.
        Args:
            command: Command to send (up to now only "clear")
        Returns:
            Empty string
        """
        return self._putRequest(self._url("monitor","command",parameter = command), dataType = "native")
        
    def streamConfig(self,param = 'keys'):
        """
        Get stream configuration parameter
        Args:
            param: query the configuration parameter param, if 'keys' get all configuration parameters.
        Returns:
            If param is 'keys' return list of all parameters, else return dictionary
            that may contain the keys: value, min, max, allowed_values, unit, value_type and access_mode
        """
        return self._getRequest(self._url('stream','config',parameter = param))
        

    def setStreamConfig(self,param,value):
        """
        Set stream configuration parameter param.
        Args:
            param: parameter
            value: value to set
        Returns:
            List of changed parameters.
        """
        return self._putRequest(self._url('stream','config',parameter = param), dataType = 'native', data = value)
        
    def streamStatus(self, param):
        """Get stream status information
        Args:
            param: query the status parameter param, if 'keys' get all status parameters.
        Returns:
            Dictionary that may contain the keys: value, value_type, unit, time, state,
            critical_limits, critical_values
        """
        return self._getRequest(self._url('stream','status',parameter = param))
        

    def sendStreamInitialize(self):
        return self._putRequest(self._url("stream","command","initialize"), dataType = "native")

    #
    #
    #                Private Methods
    #
    #

    def _log(self,*args):
        if self._verbose:
            log.log(2,' '.join([ str(elem) for elem in args ]))

    def _url(self,module,task,parameter = None):
        url = "/{0}{1}/api/{2}/{3}/".format(self._urlPrefix,module,self._version,task)
        if not parameter is None:
            url += '{0}'.format(parameter)
        return url

    def _getRequest(self,url,dataType = 'native', fileId = None):
        if dataType is None:
            dataType = 'native'
        if dataType == 'native':
            mimeType = 'application/json; charset=utf-8'
        elif dataType == 'tif':
            mimeType = 'application/tiff'
        elif dataType == 'hdf5':
            mimeType = 'application/hdf5'
        return self._request(url,'GET',mimeType, fileId = fileId)

    def _isReadingBusy(self):
        if self.read_thread is None:
            return False
        
        is_alive = self.read_thread.is_alive()

        if is_alive is False:
            cmd_name = self.read_thread.getName()
            ret_value = self.read_thread.getAnswer()
            answer = ( cmd_name, ret_value )
            self.read_value[cmd_name] = ret_value
            self.read_thread = None

        return is_alive 

    def getRequestResponse(self, name):
        if self._isReadingBusy():
            return "busy"
        else:
            if name in self.read_value:
                return self.read_value[name]
            else:
                return ".error."

    def _putRequest(self,url,dataType,data = None):
        data, mimeType = self._prepareData(data,dataType)
        return self._request(url,'PUT',mimeType, data)

    def _delRequest(self,url):
        self._request(url,'DELETE',mimeType = None)
        return None

    def _request(self, url, method, mimeType, data = None, fileId = None):
        if data is None:
            body = ''
        else:
            body = data
        headers = {}
        if method == 'GET':
            headers['Accept'] = mimeType
        elif method == 'PUT':
            headers['Content-type'] = mimeType
        if not self._user is None:
            headers["Authorization"] = "Basic {0}".format(self._user)

        self._log('sending request to {0}'.format(url))
        numberOfTries = 0
        response = None

        try:
            while response is None:
                try:
                    self._connection.request(method,url, body = data, headers = headers)
                    response = self._connection.getresponse()
                except Exception as e:
                    numberOfTries += 1
                    if numberOfTries == 50:
                        self._log("Terminate after {0} tries\n".format(numberOfTries))
                        raise e
                    self._log("Failed to connect to host. Retrying\n")
                    self._connection = http.HTTPConnection(self._host,self._port, timeout = self._connectionTimeout)
                    continue
    
            status = response.status
            reason = response.reason
            self._log("response arrived status:%s / reason. %s" % (status,reason))
            if fileId is None:
                data = response.read()
            else:
                bufferSize = 8*1024
                while True:
                    data = response.read(bufferSize)
                    if len(data) > 0:
                        fileId.write(data)
                    else:
                        break
        except:
            self._log('request error')
        finally:
            pass
            
        mimeType = response.getheader('content-type','text/plain')
        self._log('Return status: ', status, reason)
        if not response.status in range(200,300):
            raise RuntimeError((reason,data))

        if 'json' in mimeType:
            if self._serializer is None:    
                return json.loads(data)
            else:
                return self._serializer.loads(data)
        else:
            return data

    def _parseResponse(self, response):
        status = response.status
        reason = response.reason
        self._log("parsing response status:%s / reason. %s" % (status,reason))

        data = response.read()
            
        mimeType = response.getheader('content-type','text/plain')
        
        if not response.status in range(200,300):
            raise RuntimeError((reason,data))

        if 'json' in mimeType:
            if self._serializer is None:    
                return json.loads(data)
            else:
                return self._serializer.loads(data)
        else:
            return data

    def isfile(self,obj):
        if sys.version_info[0] >= 3:
            return isinstance(obj,IOBase)
        else:
            return isinstance(obj,file)

    def _prepareData(self,data, dataType):
        if data is None:
            return '', 'text/html'

        if dataType != 'native':
            if self.isfile(data):
                data = data.read()
            if dataType is None:
                mimeType = self._guessMimeType(data)
                if not mimeType is None:
                    return data, mimeType
            elif dataType == 'tif':
                return data, 'application/tiff'

        mimeType = 'application/json; charset=utf-8'

        if self._serializer is None:
            return json.dumps({'value':data}), mimeType

        return self._serializer.dumps({"value":data}), mimeType

    def _guessMimeType(self,data):
        if type(data) == str:
            if data.startswith('\x49\x49\x2A\x00') or data.startswith('\x4D\x4D\x00\x2A'):
                self._log('Determined mimetype: tiff')
                return 'application/tiff'
            if data.startswith('\x89\x48\x44\x46\x0d\x0a\x1a\x0a'):
                self._log('Determined mimetype: hdf5')
                return 'application/hdf5'
        return None


