#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
import _thread
import socket
import pygame
import math
import random
import numpy as np
from pygame.locals import *

import serial #pip3 install pyserial
import time

HEIGHT=24
WIDTH=160

class FlipdotSim():
    def __init__(self,
                 imageSize = (160,48), #80,16
                 pixelSize = 10,
                 udpPort = 2323,
                 ser=None):
        self.udpPort = udpPort
        self.flipdotMatrixSimulatorWidget=None #deactivate simulator
        #self.flipdotMatrixSimulatorWidget = FlipdotMatrixSimulatorWidget(imageSize, pixelSize) #comment out if no simulator needed
        self.udpHostSocket = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
        self.udpHostSocket.bind(("", self.udpPort))
        self.ser=ser

    def run(self):
        self.RunServer()

    def RunServer(self):
        try:
            while True:
                rawData = self.udpHostSocket.recv(4096)

                #print(rawData)
                imageArray = ImageArrayAdapter().convertPacketToImageArray(rawData)
                #print("Image array")
                #print(imageArray)
                if self.flipdotMatrixSimulatorWidget is not None:
                    self.flipdotMatrixSimulatorWidget.show(imageArray)

                self.sendToMatrix(imageArray[0: int(WIDTH*HEIGHT) ],0)
                self.sendToMatrix(imageArray[int(WIDTH*HEIGHT): ],1)


        finally:
            self.udpHostSocket.close()

    def sendToMatrix(self,imageArray,displayid=0):
        imageArray=[x for x in reversed(imageArray)] #Rotated Image

        data=np.zeros(int(WIDTH*HEIGHT/8),dtype=np.uint8)
        imageArray=np.hstack( (np.array(imageArray,dtype=np.uint8),np.zeros(len(data)*8-len(imageArray),dtype=np.uint8) ))
        print("imageArray size="+str(len(imageArray)))
        numberofbytes=int(len(imageArray)/8)
        print("numberofbytes="+str(numberofbytes))
        # 477 ist oben links
        # 474 ist rechts daneben
        for i in range(numberofbytes):
            # j=numberofbytes+1 - ( int(i/3)+(2-i%3)+3)
            # j=(numberofbytes-3) # Erste Stelle oben links
            j=(numberofbytes-3)-(i*3)+int(i/160)*480+1*(int(i/160))
            #j=(numberofbytes-3)-(i*3)+int(i/(numberofbytes/3))*numberofbytes+1*(int(i/(numberofbytes/3)))
            # 417 ist zweite Zeile, ganz links oben
            data[j] = int( ''.join(str(e)[::-1] for e in imageArray[i*8:(i+1)*8]) ,2)
        #data[59]=255
        matrixSetup(ser,displayid)
        ser.write(bytearray(fixMatrixBits(data)))
        matrixEnd(ser)

class ImageArrayAdapter():
    def __init__(self):
        self.arrayOfBinaryInts = []

    def convertPacketToImageArray(self, udpPacketStr):
        self.arrayOfBinaryInts = []
        byteArray = bytearray(udpPacketStr)

        #Fix for other format. Not Used
        #byteArray = udpPacketStr.translate(None, b'\r\n').decode().replace('[','').replace(']','').replace(' ','').split(',')
        #byteArray = [int(x) for x in byteArray]
        #print("rawtype="+str(type(byteArray)))

        #print(byteArray)
        for byte in byteArray:
            #print("byte:"+str(byte))
            self.__appendByteToArrayOfBinaryInts(byte)
        return self.arrayOfBinaryInts

    def __appendByteToArrayOfBinaryInts(self, byte):
        byteValue = int(byte)
        for i in range(8):
            if math.floor(byteValue/(2**(7-i))) > 0:
                self.arrayOfBinaryInts.append(1)
                #print("append 1")
            else:
                self.arrayOfBinaryInts.append(0)
                #print("append 0")
            byteValue = byteValue%(2**(7-i))


class FlipdotMatrixSimulatorWidget():
    BLACKCOLOR = 0
    WHITECOLOR = 1

    def __init__(self,
                 imageSize = (160,48),
                 pixelSize = 10):
        self.imageSize = imageSize
        self.pixelSize = pixelSize

        pygame.init()
        self.screen = pygame.display.set_mode((imageSize[0]*pixelSize, imageSize[1]*pixelSize))
        self.screen.fill((255,255,255))
        _thread.start_new_thread(self.watchCloseThread, ())

    def watchCloseThread(self):
        while True:
            for event in pygame.event.get():
                if event.type in (QUIT, QUIT):
                    import os
                    os.kill(os.getpid(), 9)
            pygame.time.delay(500)

    def show(self, imageArray):
        for yValue in range(self.imageSize[1]):
            for xValue in range(self.imageSize[0]):
                i = self.imageSize[0]*yValue + xValue
                color = imageArray[i]
                self.updatePixel(xValue, yValue, color)
        pygame.display.update()

    def clearPixels(self):
        for xValue in range(self.imageSize[0]):
            for yValue in range(self.imageSize[1]):
                self.updatePixel(xValue, yValue, self.BLACKCOLOR)

    def updatePixel(self, xValue, yValue, color):
        surface = pygame.Surface((self.pixelSize-1, self.pixelSize-1))
        if color == self.BLACKCOLOR:
                rectcolor = (0,0,0)
        else:
                rectcolor = (255,255,255)
        surface.fill(rectcolor)
        self.screen.blit(surface, (xValue*self.pixelSize, yValue*self.pixelSize))


def matrixSetup(ser,displayid=0):
    ser.write(chr(2).encode())
    ser.write(b'B') #command char
    if displayid==0:
        ser.write(b'1') #display id  0 or 1
    elif displayid==1:
        ser.write(b'0')
    #ser.write(b'00000000000')
    ser.write(b'00000000000') #alignment
    ser.write(chr(27).encode()) #oneB
    ser.write(b'1')

def matrixEnd(ser):
    #ser.write(chr(151).encode())
    ser.write(chr(3).encode()) #END cmd

def fixMatrixBits(data):
    print("data vor="+str(len(data)))
    #for bi,b in enumerate(data):
    bi=0
    while bi <len(data):
        b=data[bi]
        if b==2:
            data=np.hstack(  (np.hstack( (data[0:bi],[27,48,50]) ), data[bi+1:] ) )
            bi+=2
        elif b==3:
            data=np.hstack(  (np.hstack( (data[0:bi],[27,48,51]) ), data[bi+1:] ) )
            bi+=2
        elif b==27:
            data=np.hstack(  (np.hstack( (data[0:bi],[27,48,66]) ), data[bi+1:] ) )
            bi+=2
        bi+=1

    data=np.array(data,dtype=np.uint8)

    return data


if __name__ == '__main__':
    ser = None
    ser = serial.Serial('/dev/ttyUSB0',9600)  # open serial port
    print(ser.name)


    FlipdotSim(imageSize=(WIDTH,HEIGHT), pixelSize = 4, udpPort=2323,ser=ser).run()

    #ser.close()
