#-*- coding:utf-8 -*-
# cython: profile=False

#  Pybik -- A 3 dimensional magic cube game.
#  Copyright © 2009-2013  B. Clausius <barcc@gmx.de>
#
#  This program is free software: you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation, either version 3 of the License, or
#  (at your option) any later version.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with this program.  If not, see <http://www.gnu.org/licenses/>.

# Ported from GNUbik
# Original filename: cube.c, drwBlock.c
# Original copyright and license:  1998, 2003, 2004  Dale Mellor, John Darrington,  GPL3+

# although this file is compiled with Python3 syntax, Cython needs at least division from __future__
from __future__ import print_function, division

# This line makes cython happy
global __name__, __package__    # pylint: disable=W0604
#px/__compiled = True
__compiled = False

__all__ = ['VERTEX_ATTRIB_LOCATION', 'NORMAL_ATTRIB_LOCATION', 'COLOR_ATTRIB_LOCATION', 'TEXCOORD_ATTRIB_LOCATION',
           'gl_draw_cube', 'gl_pick_cube', 'gl_init_buffers', 'gl_delete_buffers',
           'gl_draw_cube_debug', 'gl_init_object_location']

from .debug import debug  # pylint: disable=W0614,W0401

#pxd/from gl cimport *
from OpenGL.GL import *     # pylint: disable=W0614,W0401
#px/from glm cimport *
from .glm import *


debug('Importing module:', __name__)
debug('  from package:', __package__)
debug('  compiled:', __compiled)


#pxd/cdef enum: #
if True:
    MAX_BLOCKS = 1000
    MAX_FACES = 6
    MAX_FACES_PER_BLOCK = 6
    #pxd=4
    VERTEX_ATTRIB_LOCATION = 0
    NORMAL_ATTRIB_LOCATION = 1
    COLOR_ATTRIB_LOCATION = 2
    TEXCOORD_ATTRIB_LOCATION = 3

#px/cdef struct Block:
class Block:     # pylint: disable=R0903
#px-
    def __init__(self):
        # The position from the centre of the cube,  and the rotation from the
        # 'natural' position.
        #px/mat4 transformation
        self.transformation = mat4()
        
        #px/bint in_motion
        self.in_motion = None
        
        #px/int cnt_faces
        self.cnt_faces = None
        #px/int idx_label[MAX_FACES_PER_BLOCK]
        self.idx_label = [None]*MAX_FACES_PER_BLOCK
        #px/int cnt_label[MAX_FACES_PER_BLOCK]
        self.cnt_label = [None]*MAX_FACES_PER_BLOCK
        #px/int texname[MAX_FACES_PER_BLOCK]
        self.texname = [None]*MAX_FACES_PER_BLOCK
        
        #px/int idx_triangles
        self.idx_triangles = None
        #px/int cnt_triangles
        self.cnt_triangles = None
        
#px/cdef struct Cube:
class cube:     # pylint: disable=W0232, R0903
    #px+unsigned int number_blocks
    #px/Block blocks[MAX_BLOCKS]
    blocks = [Block() for __block in range(MAX_BLOCKS)]
    
    #px+int idx_pick
    #px+int cnt_pick
    #px+int idx_debug
    
    #px+GLuint object_location
    #px+GLuint glbuffer
    
#px+cdef Cube cube

#px/cdef struct Animation_Struct:
class animation:    # pylint: disable=W0232, R0903
    #px+float angle, angle_max
    #px/vec3 rotation
    rotation = [None] * 3
    #px+mat4 rotation_matrix
#px+cdef Animation_Struct animation


def init_module():
    #px+cdef int i
    
    cube.number_blocks = 0
    #cube.blocks
    
    cube.idx_pick = 0
    cube.cnt_pick = 0
    cube.idx_debug = 0
    
    animation.angle = animation.angle_max = 0

def set_transformations(blocks):
    #px+cdef unsigned int b,i,j
    for b in range(cube.number_blocks):
        for i in range(4):
            for j in range(4):
                cube.blocks[b].transformation[i][j] = float(blocks[b][i][j])
        cube.blocks[b].in_motion = False

#px/cpdef set_animation_start(blocks, float angle_max, float axisx, float axisy, float axisz):
def set_animation_start(blocks, angle_max, axisx, axisy, axisz):
    animation.angle = 0.0
    animation.angle_max = angle_max
    animation.rotation = vec3(axisx, axisy, axisz)
    animation.rotation_matrix = mat4(1.)
    #px+cdef int block_id
    #px+cdef bint selected
    for block_id, selected in blocks:
        cube.blocks[block_id].in_motion = selected
    
#px/cpdef set_animation_next(float increment):
def set_animation_next(increment):
    animation.angle -= increment
    animation.rotation_matrix = rotate(animation.angle, animation.rotation)
    return abs(animation.angle) < animation.angle_max

#pxd/cdef void gl_draw_cube():
def gl_draw_cube():
    glEnableVertexAttribArray(VERTEX_ATTRIB_LOCATION)
    glEnableVertexAttribArray(NORMAL_ATTRIB_LOCATION)
    glEnableVertexAttribArray(COLOR_ATTRIB_LOCATION)
    glEnableVertexAttribArray(TEXCOORD_ATTRIB_LOCATION)
    #px+cdef unsigned int i
    #px+cdef mat4 object_matrix
    for i in range(cube.number_blocks):
        if cube.blocks[i].in_motion:
            object_matrix = animation.rotation_matrix * cube.blocks[i].transformation
            #px/glUniformMatrix4fv(cube.object_location, 1, GL_FALSE, &object_matrix[0][0])
            glUniformMatrix4fv(cube.object_location, 1, GL_FALSE, object_matrix)
        else:
            #px/glUniformMatrix4fv(cube.object_location, 1, GL_FALSE, &cube.blocks[i].transformation[0][0])
            glUniformMatrix4fv(cube.object_location, 1, GL_FALSE, cube.blocks[i].transformation)
        _gl_draw_block(i)
    glDisableVertexAttribArray(VERTEX_ATTRIB_LOCATION)
    glDisableVertexAttribArray(NORMAL_ATTRIB_LOCATION)
    glDisableVertexAttribArray(COLOR_ATTRIB_LOCATION)
    glDisableVertexAttribArray(TEXCOORD_ATTRIB_LOCATION)
        
#px/cdef void _gl_draw_block(int block_id):
def _gl_draw_block(block_id):
    #px+cdef int i
    
    glBindTexture(GL_TEXTURE_2D, 0)
    glDrawArrays(GL_TRIANGLES, cube.blocks[block_id].idx_triangles, cube.blocks[block_id].cnt_triangles)
    
    for i in range(cube.blocks[block_id].cnt_faces):
        # render the colors (ie the little sticky labels)
        glBindTexture(GL_TEXTURE_2D, cube.blocks[block_id].texname[i])
        #TODO: Workaround for artifacts on the bottom and left sides of images
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT)
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT)
        glDrawArrays(GL_TRIANGLES, cube.blocks[block_id].idx_label[i], cube.blocks[block_id].cnt_label[i])
            
#pxd/cdef void gl_pick_cube():
def gl_pick_cube():
    glEnableVertexAttribArray(VERTEX_ATTRIB_LOCATION)
    glEnableVertexAttribArray(COLOR_ATTRIB_LOCATION)
    glDrawArrays(GL_TRIANGLES, cube.idx_pick, cube.cnt_pick)
    glDisableVertexAttribArray(VERTEX_ATTRIB_LOCATION)
    glDisableVertexAttribArray(COLOR_ATTRIB_LOCATION)
    
#pxd/cdef void gl_init_buffers():
def gl_init_buffers():
    #px/glGenBuffers(1, &cube.glbuffer)
    cube.glbuffer = glGenBuffers(1)
    
#pxd/cdef void gl_delete_buffers():
def gl_delete_buffers():
    glBindBuffer(GL_ARRAY_BUFFER, 0)
    #px/glDeleteBuffers(1, &cube.glbuffer)
    glDeleteBuffers(1, [cube.glbuffer])
    cube.glbuffer = 0
    
#px/cpdef gl_set_data(int nblocks, bytes vertexdata, vertexpointers, vertexinfo, texnames):
def gl_set_data(nblocks, vertexdata, vertexpointers, vertexinfo, texnames):
    cube.number_blocks = nblocks
    assert cube.number_blocks <= MAX_BLOCKS
    
    #### Create the raw GL data ####
    #px+cdef long normalpointer, colorpointer, texpospointer
    #px+cdef unsigned int idx_block, idx_pick, cnt_pick, idx_debug
    normalpointer, colorpointer, texpospointer = vertexpointers
    labelinfos, idx_block, cnts_block, idx_pick, cnt_pick, idx_debug = vertexinfo
    
    #px+cdef unsigned int block, i, idx_label, cnt, faceno
    idx_label = 0
    for block in range(cube.number_blocks):
        cube.blocks[block].cnt_faces = len(labelinfos[block])
        for i, (cnt, faceno) in enumerate(labelinfos[block]):
            cube.blocks[block].idx_label[i] = idx_label
            cube.blocks[block].cnt_label[i] = cnt
            cube.blocks[block].texname[i] = texnames[faceno]
            idx_label += cnt
        cube.blocks[block].idx_triangles = idx_block
        cube.blocks[block].cnt_triangles = cnts_block[block]
        idx_block += cnts_block[block]
        
    cube.idx_pick = idx_pick
    cube.cnt_pick = cnt_pick
    cube.idx_debug = idx_debug
    
    debug('GL data: %d bytes' % len(vertexdata))
    glBindBuffer(GL_ARRAY_BUFFER, cube.glbuffer)
    #px/glBufferData(GL_ARRAY_BUFFER, len(vertexdata), <char*>vertexdata, GL_STATIC_DRAW)
    glBufferData(GL_ARRAY_BUFFER, len(vertexdata), vertexdata, GL_STATIC_DRAW)
    
    #px/glVertexAttribPointer(VERTEX_ATTRIB_LOCATION, 3, GL_FLOAT, GL_FALSE, 0, <void*>0)
    glVertexAttribPointer(VERTEX_ATTRIB_LOCATION, 3, GL_FLOAT, GL_FALSE, 0, 0)
    #px/glVertexAttribPointer(NORMAL_ATTRIB_LOCATION, 3, GL_FLOAT, GL_FALSE, 0, <void*>normalpointer)
    glVertexAttribPointer(NORMAL_ATTRIB_LOCATION, 3, GL_FLOAT, GL_FALSE, 0, normalpointer)
    #px/glVertexAttribPointer(COLOR_ATTRIB_LOCATION, 3, GL_UNSIGNED_BYTE, GL_TRUE, 0, <void*>colorpointer)
    glVertexAttribPointer(COLOR_ATTRIB_LOCATION, 3, GL_UNSIGNED_BYTE, GL_TRUE, 0, colorpointer)
    #px/glVertexAttribPointer(TEXCOORD_ATTRIB_LOCATION, 2, GL_FLOAT, GL_FALSE, 0, <void*>texpospointer)
    glVertexAttribPointer(TEXCOORD_ATTRIB_LOCATION, 2, GL_FLOAT, GL_FALSE, 0, texpospointer)
    
#pxd/cdef void gl_draw_cube_debug():
def gl_draw_cube_debug():
    #px+cdef mat4 object_matrix
    object_matrix = mat4(1.)
    #px/glUniformMatrix4fv(cube.object_location, 1, GL_FALSE, &object_matrix[0][0])
    glUniformMatrix4fv(cube.object_location, 1, GL_FALSE, object_matrix)
    glEnableVertexAttribArray(VERTEX_ATTRIB_LOCATION)
    glEnableVertexAttribArray(COLOR_ATTRIB_LOCATION)
    glDrawArrays(GL_LINES, cube.idx_debug, 6)
    glDisableVertexAttribArray(VERTEX_ATTRIB_LOCATION)
    glDisableVertexAttribArray(COLOR_ATTRIB_LOCATION)
    
#pxd/cdef void gl_draw_select_debug(GLfloat *selectdata, GLsizeiptr size, GLuint prog_hud):
def gl_draw_select_debug(selectdata, size, prog_hud):
    #px+cdef int i, j
    #px+cdef GLintptr offset
    offset = (cube.idx_debug+6) * 3 * sizeof(GLfloat)
    #px/glBufferSubData(GL_ARRAY_BUFFER, offset, size, &selectdata[0])
    glBufferSubData(GL_ARRAY_BUFFER, offset, size, selectdata)
    
    glDisable(GL_DEPTH_TEST)
    glEnableVertexAttribArray(VERTEX_ATTRIB_LOCATION)
    glEnableVertexAttribArray(COLOR_ATTRIB_LOCATION)
    glDrawArrays(GL_LINES, cube.idx_debug+6, 2)
    glUseProgram(prog_hud)
    glDrawArrays(GL_POINTS, cube.idx_debug+6+2, 2)
    glDisableVertexAttribArray(VERTEX_ATTRIB_LOCATION)
    glDisableVertexAttribArray(COLOR_ATTRIB_LOCATION)
    glEnable(GL_DEPTH_TEST)
    
#pxd/cdef void gl_init_object_location(GLuint location):
def gl_init_object_location(location):
    cube.object_location = location
    

