"""
psmain.py

Autor: Mahesh Venkitachalam
 
Opis: system cząsteczek
"""

import sys, os, math, numpy
import OpenGL
from OpenGL.GL import *
import numpy    
from ps import ParticleSystem, Camera
from box import Box
import glutils
import glfw

class PSMaker:
    """Klasa okna renderowania GLFW dla systemu cząsteczek"""
    def __init__(self):
        self.camera = Camera([15.0, 0.0, 2.5],
                             [0.0, 0.0, 2.5],
                             [0.0, 0.0, 1.0])
        self.aspect = 1.0
        self.numP = 300
        self.t = 0
        # flaga do obracania widoku kamery
        self.rotate = True

        # zapisanie bieżącego katalogu roboczego
        cwd = os.getcwd()

        # inicjowanie glfw - to zmienia cwd
        glfw.glfwInit()
        
        # przywrócenie cwd
        os.chdir(cwd)

        # wskazówki wersji
        glfw.glfwWindowHint(glfw.GLFW_CONTEXT_VERSION_MAJOR, 3)
        glfw.glfwWindowHint(glfw.GLFW_CONTEXT_VERSION_MINOR, 3)
        glfw.glfwWindowHint(glfw.GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE)
        glfw.glfwWindowHint(glfw.GLFW_OPENGL_PROFILE, 
                            glfw.GLFW_OPENGL_CORE_PROFILE)

        # utworzenie okna
        self.width, self.height = 640, 480
        self.aspect = self.width/float(self.height)
        self.win = glfw.glfwCreateWindow(self.width, self.height, 
                                     b"Particle System")
        # ustawienie kontekstu jako kontekstu bieżącego
        glfw.glfwMakeContextCurrent(self.win)
        
        # inicjowanie GL
        glViewport(0, 0, self.width, self.height)
        glEnable(GL_DEPTH_TEST)
        glClearColor(0.2, 0.2, 0.2,1.0)

        # ustawienie wywołań zwrotnych okna
        glfw.glfwSetMouseButtonCallback(self.win, self.onMouseButton)
        glfw.glfwSetKeyCallback(self.win, self.onKeyboard)
        glfw.glfwSetWindowSizeCallback(self.win, self.onSize)        

        # tworzenie 3D
        self.psys = ParticleSystem(self.numP)
        self.box = Box(1.0)

        # flaga wyjścia
        self.exitNow = False

        
    def onMouseButton(self, win, button, action, mods):
        #print 'przycisk myszy: ', win, button, action, mods
        pass

    def onKeyboard(self, win, key, scancode, action, mods):
        #print 'klawiatura: ', win, key, scancode, action, mods
        if action == glfw.GLFW_PRESS:
            # ESC, aby zakończyć
            if key == glfw.GLFW_KEY_ESCAPE: 
                self.exitNow = True
            elif key == glfw.GLFW_KEY_R:
                self.rotate = not self.rotate
            elif key == glfw.GLFW_KEY_B:
                # przełączanie billboardingu
                self.psys.enableBillboard = not self.psys.enableBillboard
            elif key == glfw.GLFW_KEY_D:
                # przełączanie maski głębi
                self.psys.disableDepthMask = not self.psys.disableDepthMask
            elif key == glfw.GLFW_KEY_T:
                # przełączanie przezroczystości
                self.psys.enableBlend = not self.psys.enableBlend
        
    def onSize(self, win, width, height):
        #print 'zmiana rozmiaru okna: ', win, width, height
        self.width = width
        self.height = height
        self.aspect = width/float(height)
        glViewport(0, 0, self.width, self.height)

    def step(self):
        # inkrementowanie czasu
        self.t += 10
        self.psys.step()
        # rotacja oka
        if self.rotate:
            self.camera.rotate()
        # restart co 5 sekund 
        if not int(self.t) % 5000:
            self.psys.restart(self.numP)

    def run(self):
        # timer inicjatora
        glfw.glfwSetTime(0)
        t = 0.0
        while not glfw.glfwWindowShouldClose(self.win) and not self.exitNow:
            # aktualizacja co x sekund
            currT = glfw.glfwGetTime()
            if currT - t > 0.01:
                # aktualizacja czasu
                t = currT

                # czyszczenie
                glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)

                # renderowanie
                pMatrix = glutils.perspective(100.0, self.aspect, 0.1, 100.0)
                # macierz widoku modelu
                mvMatrix = glutils.lookAt(self.camera.eye, self.camera.center, 
                                          self.camera.up)
                
                # rysowanie najpierw obiektów nieprzezroczystych
                self.box.render(pMatrix, mvMatrix)

                # renderowanie
                self.psys.render(pMatrix, mvMatrix, self.camera)

                # krok 
                self.step()

                glfw.glfwSwapBuffers(self.win)
                # sprawdzanie i przetwarzanie zdarzeń
                glfw.glfwPollEvents()
        # koniec
        glfw.glfwTerminate()

# funkcja main()
def main():
  # w razie potrzeby użycie sys.argv
  print('uruchamianie systemu cząsteczek...')
  prog = PSMaker()
  prog.run()

# wywołanie main
if __name__ == '__main__':
  main()

