from PIL import Image, ImageDraw, ImageFont
import socket
import binascii
import io
import time
import re
import numpy as np

import PIL.ImageOps


class FlipdotSender(object):
    '''
    classdocs
    '''

    C_BLACK = 0
    C_WHITE = 255

    global threadrunning
    threadrunning=False

    lastimgmap = []

    def __init__(self, udphost, udpport, img_size=(160,48), font_size=9, font_size_scroll=12,
                 font_offset1=(0,0), font_offset2=(0,8),
                 #font_family='/usr/share/fonts/gnu-free/FreeMono.ttf',
                 #font_family='/usr/share/fonts/truetype/dejavu/DejaVuSansMono.ttf',
                 font_family='freefont/FreeMono.ttf',

                 chars_per_line=11):
        '''
        Constructor
        '''

        self._udphost = udphost
        if not type(udpport) is int or udpport > 65536:
            raise TypeError('port has to be int and > 65536 !!')
        self._udpport = udpport
        self._img_size = img_size
        self._font_size = font_size
        self._font_size_scroll = font_size_scroll
        self._font_offset1 = font_offset1
        self._font_offset2 = font_offset2
        self._font_family = font_family
        self._chars_per_line = chars_per_line

        self._sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

    def stopAnimation(self):
        global threadrunning
        threadrunning=False #tried to stop a running animation


    def _list2byte(self, l):
        byte = 0
        i = 0
        for i in range(8):
            byte += 2**(7-i) if l[i] else 0
        return byte

    def _array2packet(self, a):
        return [self._list2byte(a[i*8:i*8+8]) for i in range(int(len(a)/8))]

    '''
    def _send(self, image):  #old function, backup
        imgmap = []
        for pixel in image.getdata():
            r, g, b, a = pixel
            if r == FlipdotSender.C_WHITE:
                imgmap.append(1)
            else:
                imgmap.append(0)

        lastimgmap=imgmap

        packet = self._array2packet(imgmap)

        self._sock.sendto(bytes(packet), (self._udphost, self._udpport))'''

    def _send(self, image,fadespeed=0,invert=False): #changes slowly 'fadespeed'-pixels at a time
        global threadrunning
        #if fadespeed=0 -> change instant.
        #time to change=  1280/25*0.2
        imgmap = []
        for pixel in image.getdata():
            r, g, b, a = pixel
            if r >= 127:
                if invert:
                    imgmap.append(0)
                else:
                    imgmap.append(1)
            else:
                if invert:
                    imgmap.append(1)
                else:
                    imgmap.append(0)

        imgmaptmp=FlipdotSender.lastimgmap


        if fadespeed>0:
            threadrunning=True
            #diff=np.sum(np.array(imgmaptmp) != np.array(imgmap)) #different pixels
            pixelchangeind=np.arange(self._img_size[0]*self._img_size[1])
            np.random.shuffle(pixelchangeind)
            for _i,ind in enumerate(pixelchangeind):
                if threadrunning==False:
                    break #stop this for

                if ind<len(imgmaptmp): #imgmaptmp is not empty (normally at first run)
                    imgmaptmp[ind]=imgmap[ind]
                    if _i%fadespeed==0:
                        packet = self._array2packet(imgmaptmp)
                        self._sock.sendto(bytes(packet), (self._udphost, self._udpport))
                        time.sleep(0.2)
            if threadrunning==True: #if animation hasnt been cancelled
                self.sendPacket(imgmap) #send packet and save last-imagemap
            threadrunning=False
        else:
            self.sendPacket(imgmap) #send packet and save last-imagemap


    def sendPacket(self, imgmap):
        packet = self._array2packet(imgmap)
        self._sock.sendto(bytes(packet), (self._udphost, self._udpport))

        FlipdotSender.lastimgmap=imgmap

    def send_bytes(self, img):
        imgmap = []
        for pixel in img:
            if pixel == "1":
                imgmap.append(1)
            else:
                imgmap.append(0)

        if len(img) < 1280:
            imgmap = np.hstack((imgmap, np.zeros(1280-len(img), dtype=int)))

        packet = self._array2packet(imgmap)

        self._sock.sendto(bytes(packet), (self._udphost, self._udpport))


    def send_text(self, text,fadespeed=0):
        image = Image.new("RGBA", self._img_size, FlipdotSender.C_BLACK)
        draw = ImageDraw.Draw(image)
        draw.fontmode = "1"         # No AA

        font = ImageFont.truetype(self._font_family, self._font_size)

        cut = self._chars_per_line

        splitted_text = text.split("|")

        draw.text(self._font_offset1, splitted_text[0], font=font, fill=FlipdotSender.C_WHITE)
        if len(splitted_text)>1:
                draw.text(self._font_offset2, splitted_text[1], font=font, fill=FlipdotSender.C_WHITE)

        self._send(image,fadespeed)

    def send_textFull(self, text,fadespeed=0):
        image = Image.new("RGBA", self._img_size, FlipdotSender.C_BLACK)
        draw = ImageDraw.Draw(image)
        draw.fontmode = "1"         # No AA

        l=1000 #init very high
        splitpoint=40 #very high
        currentfontsize=12+1 #init with max font size
        font = ImageFont.truetype(self._font_family, currentfontsize)

        while(l>80): #while text too long and font not too small
            if currentfontsize>8:
                currentfontsize=currentfontsize-1 #reduce size and try if fits
            else: #if fontsize too small, try cutting sentence (2 lines)
                splitpoint=splitpoint-1

            font = ImageFont.truetype(self._font_family, currentfontsize)
            l=draw.textsize(text[0:splitpoint], font=font)[0]
            print("Textlength="+str(l)+" split="+str(splitpoint))

        if splitpoint==40: #not splitted
            draw.text((0,int((16-currentfontsize)/2) ), text, font=font, fill=FlipdotSender.C_WHITE)
        else:
            draw.text((0,-1), text[0:splitpoint], font=font, fill=FlipdotSender.C_WHITE)
            draw.text((0,-1+currentfontsize), text[splitpoint:], font=font, fill=FlipdotSender.C_WHITE)

        self._send(image,fadespeed)


    def send_marquee(self, str, speed=3):
        global threadrunning
        threadrunning=True
        offset = self._img_size[0]
        font = ImageFont.truetype(self._font_family, self._font_size_scroll)

        while offset >= -font.getsize(str)[0]-speed and threadrunning==True:
            image = Image.new("RGBA", self._img_size, FlipdotSender.C_BLACK)
            draw = ImageDraw.Draw(image)
            draw.fontmode = "1"         # No AA

            draw.text((offset,0), str, font=font, fill=FlipdotSender.C_WHITE)
            self._send(image)
            offset -= speed
            time.sleep(0.15)
        threadrunning=False



    def send_img_from_filename(self, imgfilename,invert=False):
        background = Image.new("RGBA", self._img_size, FlipdotSender.C_BLACK)
        image = Image.open(imgfilename)
        image = image.convert(mode='RGBA')
        image = image.resize(self._img_size)
        image.save('/tmp/send2.png', 'PNG')
        background.paste(image, box=(0,0), mask=None)
        background.save('/tmp/send.png', 'PNG')

        self._send(background,0,invert)

    def send_img(self, img):
        background = Image.new("RGBA", self._img_size, FlipdotSender.C_BLACK)
        stream = io.BytesIO(img)
        image = Image.open(stream)
        image = image.convert(mode='RGBA')
        image = image.resize(self._img_size)
        image.save('/tmp/send2.jpeg', 'JPEG')
        background.paste(image, box=(0,0), mask=None)
        background.save('/tmp/send.jpeg', 'JPEG')

        self._send(background)
