# -*- coding: utf-8 -*-
"""
Created on Mon Mar 16 10:18:55 2020

@author: Sophie
"""

import ctypes
import weakref

from neticapy import environ as envrn
from neticapy import net as nt # Do we need this?
from neticapy import neticaerror as err
from neticapy.loaddll import Netica

class Stream:     

    def __init__(self, name, memory=False, access=None, environ=None):
        
        self.cptr = None # Initialize cptr for case where Netica raises an error during construction

        if environ is None:
            environ_cptr = envrn.env
        elif isinstance(environ, envrn.Environ):
            environ_cptr = environ.cptr
        else:
            raise TypeError('An Environ or None is required (got type {})'.format(type(environ).__name__))
        
        if access is not None:
            access = ctypes.c_char_p(access.encode())
        
        if memory:
            Netica.NewMemoryStream_ns.restype = ctypes.c_void_p
            cptr = Netica.NewMemoryStream_ns(ctypes.c_char_p(name.encode()), 
                                                      ctypes.c_void_p(environ_cptr), access)
            
        else:
            Netica.NewFileStream_ns.restype = ctypes.c_void_p
            cptr = Netica.NewFileStream_ns(ctypes.c_char_p(name.encode()), 
                                                    ctypes.c_void_p(environ_cptr), access) 
            
        err.checkerr()
                      
        self.cptr = cptr
        
        if envrn.dict_initialization:
            envrn.cptr_dict[cptr] = weakref.ref(self)
                                    
    def __del__(self):
        """Remove this Streamer, freeing all resources it consumes, including memory.
            
        Like Netica-C DeleteStream_ns.
        """
        if envrn.env is not None:
            Netica.DeleteStream_ns.restype = None
            Netica.DeleteStream_ns(ctypes.c_void_p(self.cptr))
            err.checkerr()
        if envrn.dict_initialization:
            del envrn.cptr_dict[self.cptr]
        self.cptr = None  
        
    def set_encryption(self, encryption):
        """Set the encryption that will be used to encrypt/decrypt anything 
        written/read to this stream.  
        
        Pass None to remove the encryption
        Like Netica-C SetStreamEncryption_ns.
        """
        if encryption is not None:
            encryption = ctypes.c_char_p(encryption.encode())
        Netica.SetStreamEncryption_ns.restype = None
        Netica.SetStreamEncryption_ns(ctypes.c_void_p(self.cptr), encryption)
        err.checkerr()
      
    def set_contents(self, buffer, copy=True):
        """Set the contents of this stream to 'buffer'.
        
        Buffer should be a string or an array of bytes, i.e. unsigned 1 byte 
        integers. 
        Like Netica-C SetStreamContents_ns.
        """
        length = len(buffer)
        if buffer is not None:
            if isinstance(buffer, str):
                buffer = buffer.encode()
            buffer = ctypes.c_char_p(buffer)
        Netica.SetStreamContents_ns.restype = None
        Netica.SetStreamContents_ns(ctypes.c_void_p(self.cptr), buffer,
                                    ctypes.c_longlong(length), ctypes.c_int(copy))
        err.checkerr()
    
    def get_contents(self, return_as_bytes=False):             
        """Return the contents as set from SetContents, read from file or 
        generated by Netica.  
        
        Like Netica-C GetStreamContents_ns.
        """
        clength = ctypes.c_longlong(0)
        Netica.GetStreamContents_ns.restype = ctypes.POINTER(ctypes.c_ubyte)
        contents_pointer = Netica.GetStreamContents_ns(ctypes.c_void_p(self.cptr), 
                                                       ctypes.byref(clength))
       
        err.checkerr()
        length = clength.value
        contents = bytearray()
        
        for i in range(length):
            contents.append(contents_pointer[i])
        
        if return_as_bytes is False:
            contents = contents.decode()

        return contents

    # ReadNet_bn is now called from the Net constructor - may want to 
    # make this available in addition someday
    # def read_net(self, options):
    #     """Read a BNet (Bayes net) from File.  
        
    #     'options' can be one of \"NoVisualInfo\", \"NoWindow\", or the empty 
    #     string (means create a regular window). 
    #     Like Netica-C ReadNet_bn.
    #     """
    #     Netica.ReadNet_bn.restype = ctypes.c_void_p
    #     cptr = Netica.ReadNet_bn(ctypes.c_void_p(self.cptr), ctypes.c_int(options.value))
    #     err.checkerr()
        
    #     return nt._create_net(cptr)