.include "collision.h.s"
.include "man/entity_manager.h.s"
.include "cpctelera.h.s"
.include "cpctelera_functions.h.s"
.include "man/game_manager.h.s"
.include "render_system.h.s"


.globl _entity_array
.globl _player_lives
.globl _player_invuln_sec

;;================================================
;; Comprueba colisiones de todos con todas las entidades
;; Input:
;;      HL : inicio vector entidades
;; Destroy:
;;      HL,IX,IY,AF,DE,BC
;; Return:
;;      
;;================================================
sys_collision_update::
    ;1-2
    ;1-3 2-3
    ;1-4 2-4 3-4

    ; Decrementamos el contador de invuln del jugador si no tiene colisiones
    ld e,l
    ld d,h
    ld__ixl_e
    ld__ixh_d
    ld a, e_cmp(ix)
    and #e_cmp_colision
    call z, sys_collision_player_vuln

    _loop_collision_update:
        ; Cogemos el primer elemento en de
        ld e, l
        ld d, h

        ; If (null) -> Exit
        ld a, (hl)
        inc hl
        or (hl)
        ret z
        
        ; hl += size_e-1
        ld bc, #sizeof_e
        dec bc
        add hl,bc

        ; Guardamos hl
        push hl

        ; Cargar en ix (ix=de)
        ld__ixl_e
        ld__ixh_d

        ; Si no está viva o no colision siguiente
        ld a, e_cmp(ix)
        cp #e_cmp_invalid
        jr z, _nextRow_update
        and #e_cmp_colision
        jr z, _nextRow_update

    _nextColumn_update:
        ; Cogemos el segundo elemento (de=hl)
        ld e, l
        ld d, h
        
        ; Si es null, pasar a la siguiente fila de combinaciones
        ld a, (hl)
        inc hl
        or (hl)
        jr z, _nextRow_update

        ; hl += size_e-1
        ld bc, #sizeof_e
        dec bc
        add hl,bc

        ; Cargar en iy (iy=de)
        ld__iyl_e
        ld__iyh_d

        ; Si no está viva o no colision siguiente
        ld a, e_cmp(iy)
        cp #e_cmp_invalid
        jr z, _nextColumn_update
        and #e_cmp_colision
        jr z, _nextColumn_update

        ; Comprobamos si colisionan
        call sys_collision_check

        ; If (colisionan) -> _collisionBehavior TODO
        cp #0x01 
        call z, sys_collision_behavior
        
        ; Siguiente elemento en la columna
        jr _nextColumn_update

    _nextRow_update:
        pop hl
        jr _loop_collision_update
    
ret



;;================================================
;; Comprueba si hay colisiones entre 2 entidades
;; Input:
;;      IX : entidad actual que se ha movido
;;      IY : otra entidad con la q comprueba
;; Destroy:
;;      AF
;; Return:
;;      A : bool colision
;;================================================
sys_collision_check::

    ; (1) Colision eje x 
    ; If ( e_x(ix) + e_w(ix) < e_x(iy) ) no_collision
    ; If ( e_x(ix) + e_w(ix) - e_x(iy) < 0) no_collision
    ld a, e_x(ix)
    add e_w(ix)
    sub e_x(iy)
    jr c, no_collision

    ; (2) Colision eje x
    ; If ( e_x(iy) + e_w(iy) < e_x(ix) ) no_collision
    ; If ( e_x(iy) + e_w(iy) - e_x(ix) < 0 ) no_collision
    ld a, e_x(iy)
    add e_w(iy)
    sub e_x(ix)
    jr c, no_collision

    ; (3) Colision eje y
    ; If ( e_y(ix) + e_h(ix) < e_y(iy) ) no_collision
    ; If ( e_y(ix) + e_h(ix) - e_y(iy) < 0 ) no_collision
    ld a, e_y(ix)
    add e_h(ix)
    sub e_y(iy)
    jr c, no_collision

    ; (4) Colision eje y
    ; If ( e_y(iy) + e_h(iy) < e_y(ix) ) no_collision
    ; If ( e_y(iy) + e_h(iy) - e_y(ix) < 0 ) no_collision
    ld a, e_y(iy)
    add e_h(iy)
    sub e_y(ix)
    jr c, no_collision

    ; Colision
    ld a, #1
    ret

    no_collision:
    ld a, #0
ret


;;================================================
;; Realiza las acciones cuando se produce una colisión
;; Input:
;;      IX : objeto 1 que colisiona
;;      IY : objeto 2
;; Destroy:
;;      DE, AF
;; Return:
;;      
;;================================================
sys_collision_behavior::
    ; Comprobar qué objetos son

    ; If (obj1 = player & obj2 = asteroide) -> EndGame o quitar una vida
    ld de, #_entity_array
    ld a,e
    cp__ixl
    jr nz, _elseBehavior ; else
    ld a, d
    cp__ixh
    ;; Quitar vida
    jp z, sys_collision_decrease_lives

    ; Else If (obj1 = obj2 = asteroide) -> Cambiar trayectoria asteroides (simular choque elástico)
    _elseBehavior:
        ; Cambiar dirección del asteroide1
        ;;ld a, e_vx(ix)
        ;;neg
        ;;ld e_vy(ix),a
        ld a, e_vy(ix)
        neg
        ld e_vy(ix),a

        ; Cambiar dirección del asteroide2
        ;;ld a, e_vx(iy)
        ;;neg
        ;;ld e_vy(iy),a
        ld a, e_vx(iy)
        neg
        ld e_vx(iy),a

ret


;;================================================
;; Decrementa el nº de vidas del jugador
;; Input:
;;      IX : puntero al jugador
;; Destroy:
;;      AF,DE
;; Return:
;;      
;;================================================
sys_collision_decrease_lives::
    ld  a, (_player_lives)
    dec a
    jp z,game_manager_endGame ; killPlayer

_player_not_dead:
    ld  (_player_lives), a

    ; Quitar colisiones del player
    ld a, e_cmp(ix)
    xor #e_cmp_colision
    ld e_cmp(ix), a

    ; Resetear contador invulnerabilidad
    ld a,#10000
    ld (_player_invuln_sec),a

    ; Pintar numero de vidas
    jp render_system_numlives


;;================================================
;; Si se acaba el contador de invulnerabilidad
;; volverle a poner componente de colisiones
;; Input:
;;      IX : puntero al jugador
;; Destroy:
;;      AF
;; Return:
;;      
;;================================================
sys_collision_player_vuln::
    ; Contador--
    ld a,(_player_invuln_sec)
    dec a
    jr nz, _colisionvuln_end
    ; Poner colisiones
    ld a,e_cmp(ix)
    xor #e_cmp_colision
    ld e_cmp(ix),a
    _colisionvuln_end:
    ld (_player_invuln_sec),a
    ret