# Work in Progress - Do not use

from fs.base import FS
from fs.expose.serve import packetstream

from collections import defaultdict
import threading
from threading import Lock, RLock
from json import dumps
import queue as queue
import socket

from six import b


class PacketHandler(threading.Thread):
    
    def __init__(self, transport, prelude_callback=None):
        super(PacketHandler, self).__init__()
        self.transport = transport
        self.encoder = packetstream.JSONFileEncoder(transport)
        self.decoder = packetstream.JSONDecoder(prelude_callback=None)
        
        self.queues = defaultdict(queue.Queue)            
        self._encoder_lock = threading.Lock()
        self._queues_lock = threading.Lock()
        self._call_id_lock = threading.Lock()
        
        self.call_id = 0
                
    def run(self):
        decoder = self.decoder
        read = self.transport.read
        on_packet = self.on_packet
        while True:
            data = read(1024*16)
            if not data:
                print("No data")
                break
            print("data", repr(data))            
            for header, payload in decoder.feed(data):
                print(repr(header))
                print(repr(payload))
                on_packet(header, payload)
             
    def _new_call_id(self):
        with self._call_id_lock:
            self.call_id += 1
            return self.call_id
             
    def get_thread_queue(self, queue_id=None):
        if queue_id is None:
            queue_id = threading.current_thread().ident
        with self._queues_lock:
            return self.queues[queue_id]
             
    def send_packet(self, header, payload=''):
        call_id = self._new_call_id()                
        queue_id = threading.current_thread().ident        
        client_ref = "%i:%i" % (queue_id, call_id)        
        header['client_ref'] = client_ref
        
        with self._encoder_lock:
            self.encoder.write(header, payload)
            
        return call_id
        
    def get_packet(self, call_id):
        
        if call_id is not None:
            queue_id = threading.current_thread().ident        
            client_ref = "%i:%i" % (queue_id, call_id)
        else:
            client_ref = None
        
        queue = self.get_thread_queue()
        
        while True:        
            header, payload = queue.get()
            print(repr(header))
            print(repr(payload))
            if client_ref is not None and header.get('client_ref') != client_ref:
                continue
            break                
        
        return header, payload
                
    def on_packet(self, header, payload):
        client_ref = header.get('client_ref', '')
        queue_id, call_id = client_ref.split(':', 1)
        queue_id = int(queue_id)
        #queue_id = header.get('queue_id', '')
        queue = self.get_thread_queue(queue_id)        
        queue.put((header, payload))
     
     
class _SocketFile(object):
    def __init__(self, socket):
        self.socket = socket
        
    def read(self, size):
        try:
            return self.socket.recv(size)
        except:           
            return b('')        
    
    def write(self, data):
        self.socket.sendall(data)
        
    def close(self):
        self.socket.shutdown(socket.SHUT_RDWR)
        self.socket.close()
     

class _RemoteFile(object):
    
    def __init__(self, path, connection):
        self.path = path
        self.connection = connection  

class RemoteFS(FS):
    
    _meta = { 'thead_safe' : True,
              'network' : True,
              'virtual' : False,
              'read_only' : False,
              'unicode_paths' : True,
              }
    
    def __init__(self, addr='', port=3000, username=None, password=None, resource=None, transport=None):
        self.addr = addr
        self.port = port
        self.username = None
        self.password = None
        self.resource = None
        self.transport = transport
        if self.transport is None:
            self.transport = self._open_connection()
        self.packet_handler = PacketHandler(self.transport)  
        self.packet_handler.start()  
        
        self._remote_call('auth',
                          username=username,
                          password=password,
                          resource=resource)
        
    def _open_connection(self):
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.connect((self.addr, self.port))
        socket_file = _SocketFile(sock)
        socket_file.write(b('pyfs/0.1\n'))
        return socket_file
    
    def _make_call(self, method_name, *args, **kwargs):
        call = dict(type='rpc',
                    method=method_name,
                    args=args,
                    kwargs=kwargs)
        return call        
        
    def _remote_call(self, method_name, *args, **kwargs):
        call = self._make_call(method_name, *args, **kwargs)
        call_id = self.packet_handler.send_packet(call)
        header, payload = self.packet_handler.get_packet(call_id)
        return header, payload
    
    def ping(self, msg):
        call_id = self.packet_handler.send_packet({'type':'rpc', 'method':'ping'}, msg)
        header, payload = self.packet_handler.get_packet(call_id)
        print("PING")
        print(header)
        print(payload)
    
    def close(self):
        self.transport.close()
        self.packet_handler.join()
    
    def open(self, path, mode="r", **kwargs):
        pass
    
    def exists(self, path):
        remote = self._remote_call('exists', path)
        return remote.get('response')
        
        

if __name__ == "__main__":
    
    rfs = RemoteFS()    
    rfs.close()
    
