# -*- coding: utf-8 -*-

from __future__ import print_function, absolute_import, division
import socket

from struct import pack, unpack
from functools import wraps
from collections import namedtuple

DECODER = {}
ENCODER = {}
def decode(data):
    format, make = DECODER[ord(data[0])]
    args = tuple([e if not isinstance(e, str) else e.decode('utf-8').rstrip('\0') for e in unpack(format, data[1:])])
    return make(args)


class Sender(object):
    def __init__(self, host, port):
        self.host = host
        self.port = port
        self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

        def send(encoder):
            def _send(*args, **kwargs):
                data = encoder(*args, **kwargs)
                print('sending %s bytes' % len(data))
                self.sock.sendto(data, (self.host, self.port))
            return _send
        for name, f in ENCODER.iteritems():
            setattr(self, name, send(f))

def matrix_api(api_index, format, names):
    assert api_index < 256
    def wrap(func):
        DECODER[api_index] = (format, namedtuple(func.__name__, names)._make)
        @wraps(func)
        def w(*args, **kwargs):
            pack_args = func(*args, **kwargs)
            if pack_args is None:
                assert not format and not names
                return chr(api_index)
            else:
                if isinstance(pack_args, (basestring, int, float)):
                    pack_args = (pack_args, )
                #~ print(format, pack_args, pack(format, *pack_args))
                return chr(api_index) + pack(format, *pack_args)
        ENCODER[func.__name__] = w
        return w
    return wrap

## API Decls

@matrix_api(1, '500s', 'text')
def print_text(text):
    '''The maximum safe UDP payload is 508 bytes'''
    return text.encode('utf-8')

@matrix_api(2, '500sh', 'text loop')
def scroll_text(text, loop=1):
    return text.encode('utf-8'), loop

@matrix_api(3, '500shh', 'text width fade')
def fit_text(text, width=0, fade=0):
    return text.encode('utf-8'), width, fade

@matrix_api(10, 'hh', 'step total')
def progress(text, step=0, total=0):
    return step, total

@matrix_api(255, '', '')
def destroy():
    return None

if __name__ == '__main__':
    #~ print(repr(print_text(u'Christine ♥')))
    print(decode(print_text(u'Christine ♥')).text.encode('utf-8'))
    #~ print(decode(print_text('Christine ♥'))._asdict())
    #~ print(type(decode(print_text('Christine ♥'))).__name__)


    sender = Sender('blaubeere.fritz.box', 10000)
    sender.print_text(u'Christine ♥')
