import os
import sys
import _capi

class ArgumentError(Exception):
    def __init__(self, arg, msg):
        self.arg = arg
        self.msg = msg

    def __str__(self):
        return "\"%s\": %s" % (self.arg, self.msg)

class DLLException(Exception):
    def __init__(self, name, ext, lib=None):
        self.name = name
        self.ext = ext
        self.lib = lib


class DLLTypeUnknown(DLLException):
    def __str__(self):
        return '"%s": Unknown library type.' % (self.ext)


class DLLNotFound(DLLException):
    def __str__(self):
        return 'Vendor DLL "%s" could not be found' % (self.lib)


def _find_dll(library_name):
    
    known_paths = ["~/.neuroshare", "/usr/lib/neuroshare", "/usr/local/lib/neuroshare"];
    platform = sys.platform

    if platform.startswith('linux'):
        dll_suffix = "so"
    elif platform.startswith('darwin'):
        dll_suffix = "dylib"
    elif platform.startswith('win'):
        dll_suffix = "dll"
    else:
        dll_suffix = "error"
    
    dll_name = "%s.%s" % (library_name, dll_suffix)
    
    dt = os.path.join (os.getcwd (), dll_name)
    if os.path.lexists (dt):
        return dt

    for cur_path in known_paths:
        dt = os.path.join (os.path.expanduser (cur_path), dll_name)
        if os.path.lexists (dt):
            return dt
        
    return None

def find_library_for_file(filename):

    dll_map = {"mcd" : "nsMCDLibrary",
               "plx": "nsPlxLibrary",
               "map": "nsAOLibrary",
               "nev": "nsNEVLibrary",
               "nex": "NeuroExplorerNeuroShareLibrary"}

    (root, ext) = os.path.splitext(filename)
    if not ext or not ext.startswith('.'):
        raise ArgumentError (filename, "Could find file extension");

    ext = ext[1:]

    if not dll_map.has_key (ext):
        raise DLLTypeUnknown (root, ext)

    library_name = dll_map[ext]
    path = _find_dll (library_name)

    if not path:
        path = _find_dll ("nsWineLibrary")

    if not path:
        raise DLLNotFound (root, ext, library_name)

    return (library_name, path)


def load_library_for_file(filename):
    return Library.for_file (filename)

### Library Implementation

from File import File

class Library(object):

    _loaded_libs = {}

    @classmethod
    def for_file(cls, filename):
        (name, path) = find_library_for_file (filename)
        if not cls._loaded_libs.has_key (name):
            lib = Library (name, path)
            cls._loaded_libs[name] = lib

        return cls._loaded_libs[name]

    def __init__(self, name, path):
        self._name = name
        self._path = path
        self._handle = _capi.library_open (path)
        self._open_files = []
        self._info = _capi._get_library_info (self._handle)

    def _open_file(self, filename):
        (fh, file_info) = _capi._open_file (self._handle, filename)
        self._open_files.append (fh)
        return (fh, file_info)

    def _close_file(self, file):
        fh = file._handle
        _capi._close_file (self._handle, fh)
        self._open_files.remove (fh)

    def _get_entity_info(self, nsfile, entity_id):
        fh = nsfile._handle
        
        info = _capi._get_entity_info (self._handle, fh, entity_id)
        return info

    def _get_event_data(self, event, index):
        fh = event.file._handle
        entity_id = event.id
        
        event_type = event.event_type
        max_data_len = event.max_data_length
        
        data = _capi._get_event_data (self._handle, fh, entity_id, index, event_type, max_data_len)
        return data

    def _get_analog_data (self, analog, index, count):
        fh = analog.file._handle
        entity_id = analog.id
        
        data = _capi._get_analog_data (self._handle, fh, entity_id, index, count)
        return data 

    def _get_segment_data (self, segment, index):
        fh = segment.file._handle
        entity_id = segment.id

        source_count = segment.source_count
        max_sample_count = segment.max_sample_count
        
        data = _capi._get_segment_data (self._handle, fh, entity_id, index, source_count, max_sample_count)
        return data  

    def _get_neural_data (self, neural, index, count):
        fh = neural.file._handle
        entity_id = neural.id
        
        data = _capi._get_neural_data (self._handle, fh, entity_id, index, count)
        return data  

    def _get_time_by_index (self, entity, index):
        fh = entity.file._handle
        entity_id = entity.id

        t = _capi._get_time_by_index(self._handle, fh, entity_id, index)
        return t

    def _get_index_by_time (self, entity, time, position):
        fh = entity.file._handle
        entity_id = entity.id
        
        idx = _capi._get_index_by_time (self._handle, fh, entity_id, time, position)
        return idx

    def __del__(self):
        _capi.library_close(self._handle)

    @property
    def metadata_raw(self):
        return self._info

    @property
    def creator(self):
        return self._info['Creator']

    @property
    def description(self):
        return self._info['Description']

    @property
    def api_version(self):
        major = self._info['APIVersionMaj']
        minor = self._info['APIVersionMin']
        return (major, minor)

    @property
    def library_version(self):
        major = self._info['LibVersionMaj']
        minor = self._info['LibVersionMin']
        return (major, minor)

    @property
    def max_files(self):
        return self._info['MaxFiles']

    @property
    def mtime(self):
        from datetime import date
        year = self._info['Time_Year']
        month = self._info['Time_Month']
        day = self._info['Time_Day']
        return date(year, month, day)

    @property
    def name(self):
        return self._name

    @property
    def path(self):
        return self._path


