##########ci-dessous: tache 3#####################
def nextState(input_data):
    n = len(input_data)


    """initialization de prochain grille output_data, avec chaque valeur de output_data[i][j] = False."""
    output_data = []
    for i in range(0, n):
        output_data.append( [False]*n )


    #ECRIVEZ VOTRE CODE ICI


    return output_data
##########ci-dessus: tache 3#####################
















########Il n'est pas necessaire de lire ou de modifier le code sous cette ligne.##################
########Il n'est pas necessaire de lire ou de modifier le code sous cette ligne.##################
########Il n'est pas necessaire de lire ou de modifier le code sous cette ligne.##################
########Il n'est pas necessaire de lire ou de modifier le code sous cette ligne.##################
########Il n'est pas necessaire de lire ou de modifier le code sous cette ligne.##################
########Il n'est pas necessaire de lire ou de modifier le code sous cette ligne.##################
########Il n'est pas necessaire de lire ou de modifier le code sous cette ligne.##################
########Il n'est pas necessaire de lire ou de modifier le code sous cette ligne.##################
########Il n'est pas necessaire de lire ou de modifier le code sous cette ligne.##################
########Il n'est pas necessaire de lire ou de modifier le code sous cette ligne.##################
import sys
import math
import random
import OpenGL.GL as gl
from PyQt5 import (QtGui, QtCore, QtWidgets, QtTest)

class MainWindow(QtWidgets.QMainWindow):

    def __init__(self):
        super().__init__()

        self.glWidget = GLWidget()
        self.glWidget.setMinimumSize(100, 100)
        self.glWidget.setMouseTracking(True)
        self.glWidget.setFocusPolicy(QtCore.Qt.StrongFocus)

        inputbox_sizeGoL = QtWidgets.QLineEdit()
        inputbox_sizeGoL.setPlaceholderText("Change grid size and press Enter")
        self.glWidget.inputbox_sizeGoL = inputbox_sizeGoL
        inputbox_sizeGoL.returnPressed.connect(self.glWidget.setgridsize)

        button_stop = QtWidgets.QPushButton('Stop', clicked=self.glWidget.stopAnim)
        button_start = QtWidgets.QPushButton('Start', clicked=self.glWidget.startAnim)
        button_onestep = QtWidgets.QPushButton('Go one step', clicked=self.glWidget.oneStep)
        button_genrandom = QtWidgets.QPushButton('Random', clicked=self.glWidget.setRandom)
        button_clear = QtWidgets.QPushButton('Clear', clicked=self.glWidget.clear)
        button_print = QtWidgets.QPushButton('Print', clicked=self.glWidget.print)

        slider_animspeed_label = QtWidgets.QLabel('Speed:')
        slider_animspeed = QtWidgets.QSpinBox()
        slider_animspeed.setRange(1, 20)
        slider_animspeed.setValue(8)
        slider_animspeed.valueChanged.connect(self.glWidget.setAnimSpeed)

        self.resize(800, 800)

        main = QtWidgets.QWidget()
        self.setCentralWidget(main)

        main.setLayout(QtWidgets.QVBoxLayout())
        main.layout().addWidget(self.glWidget)

        controls_panel = QtWidgets.QHBoxLayout()
        main.layout().addLayout(controls_panel)

        controls_panel.addWidget(inputbox_sizeGoL)
        controls_panel.addWidget(button_clear)
        controls_panel.addWidget(button_print)
        controls_panel.addWidget(button_genrandom)
        controls_panel.addWidget(button_onestep)
        controls_panel.addWidget(button_start)
        controls_panel.addWidget(button_stop)
        controls_panel.addWidget(slider_animspeed_label)
        controls_panel.addWidget(slider_animspeed)

        self.show()


    def keyPressEvent(self, event):
        if event.key() == QtCore.Qt.Key_Escape or event.key() == QtCore.Qt.Key_Q :
            quit()
        self.update()
        super().keyPressEvent(event)


class GLWidget(QtWidgets.QOpenGLWidget):
    def __init__(self, parent=None):
        super(GLWidget, self).__init__(parent)

        self.GoL = myGoL()
        self.runAnim = False
        self.speedAnim = 1


    def initializeGL(self):
        print(self.getOpenglInfo())

        gl.glClearColor(0.2, 0.2, 0.2, 1)

        self.vertShaderSrc = """
            #version 130
            attribute vec3 aPosition;
            uniform mat4 projection_matrix;
            void main()
            {
                gl_Position = projection_matrix * vec4(aPosition, 1.0); 
            }
        """
        self.fragShaderSrc = """
            #version 130
            uniform vec4 mycolor;
            out vec4 finalcolor;
            void main()
            {
                finalcolor = mycolor;
            }
        """

        self.program = QtGui.QOpenGLShaderProgram()
        self.program.addShaderFromSourceCode(QtGui.QOpenGLShader.Vertex, self.vertShaderSrc)
        self.program.addShaderFromSourceCode(QtGui.QOpenGLShader.Fragment, self.fragShaderSrc)
        self.program.link()

        gl.glShadeModel(gl.GL_FLAT)
        gl.glEnable(gl.GL_DEPTH_TEST)
        gl.glDisable(gl.GL_CULL_FACE)

        self.program.bind() 

    def paintGL(self):
        gl.glViewport(0,0, self.width(), self.height())
        gl.glClear(gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT)

        projectionmatrix = QtGui.QMatrix4x4()
        projectionmatrix.ortho(0, 1, 0, 1, 0, 100)
        self.program.setUniformValue(self.program.uniformLocation('projection_matrix'), projectionmatrix)

        self.drawGoL()


    def drawGoL(self):
        interval = 1.0 / self.GoL.n

        lines = []
        for i in range(self.GoL.n):
            for j in range(self.GoL.n):
                lines.append( QtGui.QVector3D( i / self.GoL.n, j / self.GoL.n, 0.0 ) )
                lines.append( QtGui.QVector3D( (i+1) / self.GoL.n, j / self.GoL.n, 0.0 ) )

                lines.append( QtGui.QVector3D( (i+1) / self.GoL.n, j / self.GoL.n, 0.0 ) )
                lines.append( QtGui.QVector3D( (i+1) / self.GoL.n, (j+1) / self.GoL.n, 0.0 ) )

                lines.append( QtGui.QVector3D( (i+1) / self.GoL.n, (j+1) / self.GoL.n, 0.0 ) )
                lines.append( QtGui.QVector3D( i / self.GoL.n, (j+1) / self.GoL.n, 0.0 ) )

                lines.append( QtGui.QVector3D( i / self.GoL.n, (j+1) / self.GoL.n, 0.0 ) )
                lines.append( QtGui.QVector3D( i / self.GoL.n, j / self.GoL.n, 0.0 ) )

        self.program.setUniformValue(self.program.uniformLocation('mycolor'), QtGui.QVector4D(0, 0, 1, 1))
        gl.glLineWidth(40.0 / self.GoL.n )
        vertex_location = self.program.attributeLocation('aPosition')
        self.program.enableAttributeArray(vertex_location)
        self.program.setAttributeArray(vertex_location, lines)
        gl.glDrawArrays(gl.GL_LINES, 0, len(lines))

        quads = []
        for i in range(self.GoL.n):
            for j in range(self.GoL.n):
                if self.GoL.data[i][j] == True: 
                    quads.append( QtGui.QVector3D( i / self.GoL.n, j / self.GoL.n, 0.0 ) )
                    quads.append( QtGui.QVector3D( (i+1) / self.GoL.n, j / self.GoL.n, 0.0 ) )
                    quads.append( QtGui.QVector3D( (i+1) / self.GoL.n, (j+1) / self.GoL.n, 0.0 ) )
                    quads.append( QtGui.QVector3D( i / self.GoL.n, (j+1) / self.GoL.n, 0.0 ) )

        if len(quads) > 0:
            self.program.setUniformValue(self.program.uniformLocation('mycolor'), QtGui.QVector4D(0, 1, 0, 1))
            vertex_location = self.program.attributeLocation('aPosition')
            self.program.enableAttributeArray(vertex_location)
            self.program.setAttributeArray(vertex_location, quads)
            gl.glDrawArrays(gl.GL_QUADS, 0, len(quads))
        
        
    def resizeGL(self, w, h):
        if min(w,h) < 0:
            return
        self.update()


    def mousePressEvent(self, event):
        if event.buttons() & QtCore.Qt.LeftButton: 
            self.GoL.invert( int(self.GoL.n * event.x() / self.width()),  int( self.GoL.n * (1 - event.y() / self.height() ) ) )
        self.runAnim = False
        self.update()


    def keyPressEvent(self, event):
        #if event.key() == QtCore.Qt.Key_Right:
        #    camera_eye += QtGui.QVector3D(0.01, 0.01, 0.01)
        #elif event.key() == QtCore.Qt.Key_Left:
        #    camera_eye -= QtGui.QVector3D(0.01, 0.01, 0.01)
        self.update()
        super().keyPressEvent(event)


    def getOpenglInfo(self):
        info = """
            Vendor: {0}
            Renderer: {1}
            OpenGL Version: {2}
            Shader Version: {3}
        """.format(
            gl.glGetString(gl.GL_VENDOR),
            gl.glGetString(gl.GL_RENDERER),
            gl.glGetString(gl.GL_VERSION),
            gl.glGetString(gl.GL_SHADING_LANGUAGE_VERSION)
        )

        return info


    def setAnimSpeed(self, newval):
        self.speedAnim = int(newval)
        self.update()


    def startAnim(self):
        self.runAnim = True
        i = 0
        while self.runAnim == True:
           self.GoL.oneStep()
           self.update()
           app.processEvents()
           if self.GoL.n < 100:
               QtTest.QTest.qWait( int( 20000 / (10 * self.speedAnim)) )


    def stopAnim(self):
        self.runAnim = False
        self.update()


    def setgridsize(self):
        self.GoL.setSize( int(self.inputbox_sizeGoL.displayText()) )
        self.stopAnim()


    def oneStep(self):
        self.GoL.oneStep()
        self.stopAnim()


    def setRandom(self):
        self.GoL.setRandom()
        self.stopAnim()


    def clear(self):
        self.GoL.clear()
        self.stopAnim()

    def print(self):
        print("Current board:", self.GoL.data)
        for i in range(len(self.GoL.data)):
            print(f"\t{i}-th colonne, de bas en haut: ", self.GoL.data[i])
        
        self.stopAnim()


class myGoL:

    def __init__(self):
        self.n = 10
        self.data = [ [0]*self.n for i in range(self.n) ]
        for i in range(0, self.n):
            for j in range(0, self.n):
                self.data[i][j] = False


    def setSize(self, newval):
        self.n = newval
        self.data = [ [0]*self.n for i in range(self.n) ]
        for i in range(0, self.n):
            for j in range(0, self.n):
                self.data[i][j] = False


    def clear(self):
        for i in range(0, self.n):
            for j in range(0, self.n):
                self.data[i][j] = False
    

    def oneStep(self):
        self.data = nextState(self.data)


    def setRandom(self):
        for i in range(0, self.n):
            for j in range(0, self.n):
                self.data[i][j] = bool(random.getrandbits(1))


    def invert(self, i, j):
        if i >= 0 and i < self.n and j >= 0 and j < self.n:
            self.data[i][j] = not self.data[i][j]

    
if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    window = MainWindow()
    window.glWidget.qApp = app
    window.show()
    sys.exit(app.exec_())
