#!/usr/bin/env python
"""Usage: zserver <address> <ezfio_folder> [--file=<file>...]  [--dir=<dir>...]

-h --help    show this
-s --sorted  sorted output
-o FILE      specify output file [default: ./test.txt]
--quiet      print less text
--verbose    print more text

"""

import zmq
import logging
from docopt import docopt
from zezfio.generator import Generator
#Return a list of tuple (name_interface, the address)
#for all the socket avalable
def get_ip_address_tuples():
    import socket
    import fcntl
    import struct
    import os

    #For on iterface retourn the ip
    #Dark magic of stackoverflow (http://stackoverflow.com/a/9267833)
    def get_ip_address(ifname):
        s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        return socket.inet_ntoa(fcntl.ioctl(s.fileno(),0x8915, struct.pack('256s', ifname[:15]))[20:24])

    return [ (i, get_ip_address(i)) for i in os.listdir('/sys/class/net/') if i != 'lo' ]


if __name__ == '__main__':
    #                         
    # |  _   _   _  o ._   _  
    # | (_) (_| (_| | | | (_| 
    #        _|  _|        _| 
    #        
    logging.basicConfig(filename='zezfio.log', level=logging.ERROR)

    #  _             
    # |_) _. _|_ |_  
    # |  (_|  |_ | | 
    #
    arguments = docopt(__doc__)

    g = Generator(arguments)

    #                                     _       
    # |_|  _. ._   _| |  _     _  _  ._ _|_ o  _  
    # | | (_| | | (_| | (/_   (_ (_) | | |  | (_| 
    #                                          _|
    d_instance = g.d_instance

    #  __                 
    # (_   _  ._    _  ._ 
    # __) (/_ | \/ (/_ |  
    #
    context = zmq.Context(io_threads=4)
    sock = context.socket(zmq.REP)

    #IPC Adress
    sock.bind(arguments["<address>"])

    #Random tcp
    p = sock.bind_to_random_port('tcp://*', min_port=6001, max_port=16000, max_tries=100)
    string_interface_info = ";".join("%s,tcp://%s:%d"%(i,a,p) for i,a in get_ip_address_tuples())

    # ~#~#~#~#
    # Little_optimisation
    # ~#~#~#~#
    send = sock.send
    recv = sock.recv

    # ~#~#~#~#
    # Error_code
    # ~#~#~#~#    
    from ctypes import c_int
    errno_fail = c_int(-1)
    errno_success = c_int(0)

    while True:
            #For the sake of performance, inline the main loop
            #Get the info
            try:
                m = recv()
            except zmq.error.ZMQError:
                logging.exception("Error when asking for a message")
                continue

            if m == "get_tcp":
                send(string_interface_info)
                continue

            try:
                action, str_instance, name = m.split(".",3)
            except:
                logging.exception("Error when splitting the message %s"%m)
                continue

            try:
                instance = d_instance[str_instance]
            except Exception as e:
                logging.exception("Cannot get the %s category" % str_instance)
                send(errno_fail)
                continue

            #Get the instance
            if action == "has":

                try:
                    getattr(instance,"%s_interface" % name)
                except Exception as e:
                    logging.exception(e)
                    send(errno_fail)
                    continue
                else:
                    send(errno_success)
                    logging.debug("Is Present")

            elif action == "size":

                try:
                    size = getattr(instance, "%s_len_interface" % name)
                except Exception as e:
                    logging.error(e)
                    send(errno_fail)
                    continue
                else:
                    send(errno_success,  zmq.SNDMORE)
                    send(size)
                    logging.debug("Size send")

            elif action == "get":
                try:
                    size = getattr(instance, "%s_len_interface" % name)
                    array = getattr(instance, "%s_interface" % name)
                except Exception as e:
                    logging.exception(e)
                    send(errno_fail)
                    continue
                else:
                    send(errno_success,  zmq.SNDMORE)
                    send(size, zmq.SNDMORE)               
                    send(array)

            elif action == "set":
                try:
                    bytes = recv()
                except zmq.error.ZMQError:
                    logging.exception("Error when reading the number of bytes in set")
                    continue

                try:
                    getattr(instance, "set_%s" % name)(bytes)
                except Exception as e:
                    logging.exception(e)
                    send(errno_fail)
                    continue
                else:
                    errno_success = d_instance["zezfio_id"]
                    send(errno_success)
                    logging.debug("Set Done. Errno is %s" % errno_success.value)

            elif action == "get_ascii":
                try:
                    array = getattr(instance, "%s_ascii" % name)
                except Exception as e:
                    logging.exception(e)
                    send(str(errno_fail.value))
                    continue
                else:
                    send(str(errno_success.value),  zmq.SNDMORE)            
                    send(array)

            elif action == "set_ascii":
                try:
                    string = recv()
                except zmq.error.ZMQError:
                    logging.exception("Error when reading the number of string in set")
                    continue

                try:
                    getattr(instance, "set_ascii_%s" % name)(string)
                except Exception as e:
                    logging.exception(e)
                    send(str(errno_fail.value))
                    continue
                else:
                    errno_success = d_instance["zezfio_id"]
                    send(str(errno_success.value))
                    logging.debug("Set Done. Errno is %s" % errno_success.value)

            else:
                logging.error("Cannot understand the action to do %s"%action)
                continue
