;    Copyright (C) 2014 Anton_K
;
;    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 2 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/>.

format binary as 'kex'

__DEBUG__       = 1             ; 0 - disable debug output / 1 - enable debug output
__DEBUG_LEVEL__ = DEBUG_FINE    ; DEBUG_FINE - all debug messages / DEBUG_INFO - info and errors / DEBUG_ERR - only errors

DEBUG_FINE      = 0
DEBUG_INFO      = 1
DEBUG_ERR       = 2

include '../../macros.inc'
purge   mov, add, sub

; ================================= Header =================================== ;
MEOS_APP_START
store dword StartupPath at $ - 4

; ================================ Includes ================================== ;
include '../../debug-fdo.inc'
include '../../proc32.inc'
include '../../dll.inc'

include 'AKODE/AKODE.inc'
include 'datadef.inc'

; =============================== Entry point ================================ ;
CODE
        DEBUGF  DEBUG_INFO, 'Started\n'

        mcall   68, 11                          ; initialize heap
        test    eax, eax
        jz      .exit_fail_heap_init

        stdcall dll.Load, @IMPORT               ; load libraries
        test    eax, eax
        jnz     .exit_fail_load_libs

        mcall   40, MAIN_EVENT_MASK             ; used events
        mcall   66, 1, 1                        ; use scancodes

        stdcall draw_window

        mcall   9, ThreadInfoBuffer, -1         ; get real window size
        mov     eax, [ebx + process_information.client_box.width]
        inc     eax
        mov     [MainWindowWidth], eax
        mov     edx, [ebx + process_information.client_box.height]
        inc     edx
        mov     [MainWindowHeight], edx
        DEBUGF  DEBUG_FINE, 'Window width: %u\nWindow height: %u\n', eax, edx

        mov     ecx, eax
        shl     ecx, 2
        imul    ecx, edx                        ; ecx = width * 4 * height

        sub     edx, HUD_PANEL_HEIGHT
        mov     [WorldViewHeight], edx

if FSAA
        shl     eax, FSAA
        shl     edx, FSAA
end if

        stdcall akode.init, eax, edx, FIELD_OF_VIEW, BLOCK_BASE_SIZE, BLOCK_HEIGHT
        test    eax, eax
        jz      .exit_fail_akode_init

        mcall   68, 12                          ; alloc ecx bytes for image buffer
        test    eax, eax
        jz      .exit_fail_alloc

        mov     [ImageBufferPtr], eax
        push    eax

        stdcall set_startup_path

        stdcall load_hud_images
        test    eax, eax
        jnz     @f
        DEBUGF  DEBUG_ERR, 'Failed to load HUD images\n'
        jmp     .exit_fail_load_hud

@@:
        cld
        xor     eax, eax
        mov     edi, PressedKeys
        mov     ecx, 128 * 2
        rep     stosd

        mov     edi, Inventory
        mov     ecx, INVENTORY_SIZE * 2
        rep     stosd

        stdcall akode.set_callbacks, level_load_callback, action_callback

        stdcall akode.load_level, levels.level1

if FULLSCREEN
        stdcall hide_cursor
        push    eax
end if

        stdcall main_loop

if FULLSCREEN
        pop     ecx
        test    ecx, ecx
        jz      @f
        mcall   37, 6                           ; delete cursor
@@:
end if

.exit_fail_load_hud:
        stdcall free_hud_images

        pop     ecx
        mcall   68, 13                          ; free image buffer

        jmp     .exit

.exit_fail_heap_init:
        DEBUGF  DEBUG_ERR, 'Heap initialization failed\n'
        jmp     .exit

.exit_fail_load_libs:
        DEBUGF  DEBUG_ERR, 'Failed to load libraries\n'
        jmp     .exit

.exit_fail_akode_init:
        DEBUGF  DEBUG_ERR, 'AKODE initialization failed\n'
        jmp     .exit

.exit_fail_alloc:
        DEBUGF  DEBUG_ERR, 'Memory allocation for image buffer failed\n'
        ;jmp     .exit

.exit:
        stdcall akode.cleanup
        DEBUGF  DEBUG_INFO, 'Exiting\n'

        xor     eax, eax
        dec     eax
        mcall                                   ; kill this thread

; ============================================================================ ;
proc set_startup_path
        cld
        mov     esi, StartupPath
        mov     ecx, esi

@@:
        lodsb
        test    al, al
        jnz     @b

        sub     esi, 2
        std

@@:
        lodsb
        cmp     al, '/'
        jne     @b

        mov     [esi + 1], byte 0

        mcall   30, 1                           ; set current directory

        ret
endp
; ============================================================================ ;

; ============================================================================ ;
; < eax = 0 - fail                                                             ;
; ============================================================================ ;
proc load_hud_images
        mov     ebx, [MainWindowWidth]
        mov     ecx, [MainWindowHeight]
        xor     edx, edx

        stdcall akode.load_and_scale_image, LevelLoadingImageFile, ebx, ecx, edx
        test    eax, eax
        jz      .exit

        mov     [LevelLoadingImagePtr], eax

        stdcall akode.load_and_scale_image, DeathImageFile, ebx, ecx, edx
        test    eax, eax
        jz      .exit

        mov     [DeathImagePtr], eax

        stdcall akode.load_and_scale_image, EndImageFile, ebx, ecx, edx
        test    eax, eax
        jz      .exit

        mov     [EndImagePtr], eax

        stdcall akode.load_and_scale_image, HudPanelImageFile, ebx, HUD_PANEL_HEIGHT, edx
        test    eax, eax
        jz      .exit

        mov     [HudPanelImagePtr], eax

.exit:
        ret
endp
; ============================================================================ ;

; ============================================================================ ;
proc free_hud_images
        xor     edx, edx
        mov     ebx, 13

        mov     ecx, [LevelLoadingImagePtr]
        test    ecx, ecx
        jz      @f
        mcall   68                              ; free
        mov     [LevelLoadingImagePtr], edx
@@:
        mov     ecx, [HudPanelImagePtr]
        test    ecx, ecx
        jz      @f
        mcall   68
        mov     [HudPanelImagePtr], edx
@@:
        mov     ecx, [DeathImagePtr]
        test    ecx, ecx
        jz      @f
        mcall   68
        mov     [DeathImagePtr], edx
@@:
        mov     ecx, [EndImagePtr]
        test    ecx, ecx
        jz      @f
        mcall   68
        mov     [EndImagePtr], edx
@@:

        ret
endp
; ============================================================================ ;

; ============================================================================ ;
; < eax = cursor handle / 0 - fail                                             ;
; ============================================================================ ;
proc hide_cursor
        mcall   68, 12, 32 * 32 * 4
        test    eax, eax
        jz      .exit

        mov     edi, eax
        xor     ebx, ebx
        shr     ecx, 2
@@:     mov     [eax], ebx
        add     eax, 4
        loop    @b

        mcall   37, 4, edi, 2                   ; load cursor
        mov     ecx, eax
        inc     ebx
        mcall   37                              ; set cursor

        xchg    edi, ecx

        mcall   68, 13

        mov     eax, edi

.exit:
        ret
endp
; ============================================================================ ;

; ============================================================================ ;
proc draw_image uses eax ebx ecx edx esi edi, image_ptr, x, y, width, height
        mov     ebx, [image_ptr]
        mpack   ecx, [width], [height]
        mpack   edx, [x], [y]
        mov     esi, 32
        xor     edi, edi
        xchg    edi, ebp
        mcall   65
        mov     ebp, edi

        ret
endp
; ============================================================================ ;

; ============================================================================ ;
proc draw_image_to_buffer uses eax ebx ecx edx esi edi, image_ptr, x, y, width, height
        cld
        mov     esi, [image_ptr]
        mov     edi, [ImageBufferPtr]
        mov     ebx, [MainWindowWidth]
        mov     eax, [y]
        mul     ebx
        add     eax, [x]
        lea     edi, [edi + eax * 4]

        sub     ebx, [width]
        shl     ebx, 2
        mov     edx, [height]

@@:
        mov     ecx, [width]
        rep     movsd
        add     edi, ebx

        sub     edx, 1
        jnz     @b

        ret
endp
; ============================================================================ ;

; ============================================================================ ;
proc draw_image_with_transparency_to_buffer uses eax ebx ecx edx esi edi, image_ptr, x, y, width, height
        mov     esi, [image_ptr]
        mov     edi, [ImageBufferPtr]
        mov     ebx, [MainWindowWidth]
        mov     eax, [y]
        mul     ebx
        add     eax, [x]
        lea     edi, [edi + eax * 4]

        sub     ebx, [width]
        shl     ebx, 2
        mov     edx, [height]

.y_draw_loop:
        mov     ecx, [width]

.x_draw_loop:
        mov     eax, [esi]
        cmp     eax, 0FF00FFh
        je      @f
        mov     [edi], eax
@@:
        add     esi, 4
        add     edi, 4

        sub     ecx, 1
        jnz     .x_draw_loop

        add     edi, ebx

        sub     edx, 1
        jnz     .y_draw_loop

        ret
endp
; ============================================================================ ;

; ============================================================================ ;
proc draw_window
        mcall   12, 1                           ; start drawing

if FULLSCREEN
        mov     ebx, 0FFFFh
        mov     ecx, 0FFFFh
else
        mcall   48, 4                           ; eax - skin height

        mpack   ebx, MAIN_WINDOW_X, MAIN_WINDOW_WIDTH + 9
        mpack   ecx, MAIN_WINDOW_Y, MAIN_WINDOW_HEIGHT + 4
        add     ecx, eax
end if
        mov     edx, MAIN_WINDOW_STYLE
        mov     esi, MAIN_WINDOW_STYLE2
        mov     edi, MAIN_WINDOW_TITLE
        xor     eax, eax
        mcall                                   ; draw window

        mcall   12, 2                           ; end drawing

        ret
endp
; ============================================================================ ;

; ============================================================================ ;
proc draw_world
        mov     ebx, [ImageBufferPtr]
        ;test    ebx, ebx
        ;jz      @f

        stdcall akode.render
        stdcall akode.get_image, ebx, FSAA

        mpack   ecx, [MainWindowWidth], [WorldViewHeight]
        xor     edx, edx
        mov     esi, 32
        xor     edi, edi
        xchg    edi, ebp
        mcall   65
        mov     ebp, edi

;@@:
        ret
endp
; ============================================================================ ;

; ============================================================================ ;
proc draw_hud
        mov     eax, [HudPanelNeedsRedraw]
        test    eax, eax
        jz      .exit

        xor     eax, eax
        mov     [HudPanelNeedsRedraw], eax

        stdcall draw_image_to_buffer, [HudPanelImagePtr], eax, [WorldViewHeight], [MainWindowWidth], HUD_PANEL_HEIGHT

        mov     esi, Inventory + 4
        mov     ebx, INVENTORY_Y
        add     ebx, [WorldViewHeight]
        mov     edx, 2

.y_inventory_loop:
        mov     eax, INVENTORY_X
        mov     ecx, INVENTORY_SIZE / 2

.x_inventory_loop:
        mov     edi, [esi]
        add     esi, 8
        test    edi, edi
        jz      @f
        stdcall draw_image_with_transparency_to_buffer, edi, eax, ebx, OBJECT_IMAGE_WIDTH, OBJECT_IMAGE_HEIGHT

@@:
        add     eax, OBJECT_IMAGE_WIDTH + INVENTORY_PADDING_X

        sub     ecx, 1
        jnz     .x_inventory_loop

        add     ebx, OBJECT_IMAGE_HEIGHT + INVENTORY_PADDING_Y

        sub     edx, 1
        jnz     .y_inventory_loop

        mpack   ecx, [MainWindowWidth], HUD_PANEL_HEIGHT
        mov     edx, [WorldViewHeight]
        mov     ebx, [ImageBufferPtr]
        mov     eax, [MainWindowWidth]
        imul    eax, edx
        lea     ebx, [ebx + eax * 4]
        mov     esi, 32
        xor     edi, edi
        xchg    edi, ebp
        mcall   65
        mov     ebp, edi

        jmp     draw_game_message

.exit:
        ret
endp
; ============================================================================ ;

; ============================================================================ ;
proc draw_game_message
        mov     esi, [GameMessage]
        test    esi, esi
        jz      .exit

        mpack   ebx, GAME_MESSAGE_X, GAME_MESSAGE_Y
        add     ebx, [WorldViewHeight]
        mov     ecx, GAME_MESSAGE_COLOR or (80h shl 24)

.draw_strings_loop:
        mov     edx, esi

@@:
        mov     al, [esi]
        add     esi, 1

        test    al, al
        jz      .draw_last_string
        cmp     al, 0Ah
        jne     @b

        mov     [esi - 1], byte 0

        mcall   4

        mov     [esi - 1], byte 0Ah
        add     ebx, 10
        jmp     .draw_strings_loop

.draw_last_string:
        mcall   4

.exit:
        ret
endp
; ============================================================================ ;

; ============================================================================ ;
proc main_loop
        locals
                frame_count     dd 0
                last_timestamp  dd 0
        endl

.main_loop:
        mcall   26, 9                           ; get timestamp
        mov     ebx, eax
        sub     ebx, [last_timestamp]
        cmp     ebx, 100
        jb      @f

        mov     [last_timestamp], eax
        imul    eax, [frame_count], 100
        xor     edx, edx
        mov     [frame_count], edx
        div     ebx                             ; eax - fps

        DEBUGF  DEBUG_FINE, 'FPS: %u\n', eax

@@:
        mcall   11                              ; check events

        test    eax, eax
        jz      .idle

        dec     eax
        jz      .redraw

        dec     eax
        jz      .key

        dec     eax
        jz      .button

        sub     eax, 3
        jz      .mouse

        jmp     .idle

.redraw:
        stdcall draw_window
        mov     [HudPanelNeedsRedraw], 1

.idle:
        mov     eax, [GameStatus]
        test    eax, eax
        jnz     @f

        stdcall akode.process

        mov     eax, [GameStatus]
        test    eax, eax
        jnz     @f

        stdcall draw_hud
        stdcall draw_world

        add     [frame_count], 1
        jmp     .main_loop

@@:     cmp     eax, GAME_STATUS.LEVEL_LOAD_FAILED
        jne     @f
        jmp     .exit

@@:     cmp     eax, GAME_STATUS.DEAD
        jne     @f
        stdcall draw_image, [DeathImagePtr], 0, 0, [MainWindowWidth], [MainWindowHeight]
        jmp     .main_loop

@@:     cmp     eax, GAME_STATUS.END
        jne     @f
        stdcall draw_image, [EndImagePtr], 0, 0, [MainWindowWidth], [MainWindowHeight]
@@:
        jmp     .main_loop

.key:
        stdcall get_key
        test    eax, eax
        jz      .idle

        cmp     eax, 001h                       ; Esc
        je      .exit

        cmp     eax, 039h                       ; Space
        je      .space_pressed
        cmp     eax, 002h                       ; 1
        jb      @f
        cmp     eax, 00Bh                       ; 0
        ja      @f

        ; 0..9 pressed
        sub     eax, 002h
        mov     eax, dword [Inventory + eax * 8]
        test    eax, eax
        jz      @f
        shl     eax, 16
        push    eax

        stdcall check_key, 02Ah                 ; left Shift
        test    eax, eax
        jnz     .shift_pressed

        stdcall check_key, 036h                 ; right Shift
        test    eax, eax
        jnz     .shift_pressed

        pop     eax
        or      eax, ACTION.USE_OBJECT
        stdcall akode.action, eax
        jmp     @f

.shift_pressed:
        pop     eax
        or      eax, ACTION.LOOK_AT_OBJECT
        stdcall akode.action, eax
        jmp     @f

.space_pressed:
        stdcall check_key, 02Ah                 ; left Shift
        test    eax, eax
        jnz     .shift_pressed2

        stdcall check_key, 036h                 ; right Shift
        test    eax, eax
        jnz     .shift_pressed2

        stdcall akode.action, ACTION.DO_SOMETHING
        jmp     @f

.shift_pressed2:
        stdcall akode.action, ACTION.LOOK_AROUND

@@:
        xor     esi, esi
        dec     esi

        stdcall check_key, 0E048h               ; ^
        test    eax, eax
        jz      @f
        mov     esi, AKODE_DIRECTION.NORTH
        jmp     .set_moving_direction

@@:     stdcall check_key, 0E050h               ; v
        test    eax, eax
        jz      @f
        mov     esi, AKODE_DIRECTION.SOUTH
        jmp     .set_moving_direction

@@:     stdcall check_key, 011h                 ; W
        test    eax, eax
        jz      @f
        mov     esi, AKODE_DIRECTION.NORTH
        jmp     .set_moving_direction

@@:     stdcall check_key, 01Fh                 ; S
        test    eax, eax
        jz      @f
        mov     esi, AKODE_DIRECTION.SOUTH
        ;jmp     .set_moving_direction

@@:

.set_moving_direction:
        test    esi, esi
        js      @f
        stdcall akode.start_moving, esi
        jmp     .turn

@@:
        stdcall akode.stop_moving

.turn:
        xor     esi, esi
        dec     esi

        stdcall check_key, 0E04Bh               ; <-
        test    eax, eax
        jz      @f
        mov     esi, AKODE_DIRECTION.WEST
        jmp     .set_turning_direction

@@:     stdcall check_key, 0E04Dh               ; ->
        test    eax, eax
        jz      @f
        mov     esi, AKODE_DIRECTION.EAST
        jmp     .set_turning_direction

@@:     stdcall check_key, 01Eh                 ; A
        test    eax, eax
        jz      @f
        mov     esi, AKODE_DIRECTION.WEST
        jmp     .set_turning_direction

@@:     stdcall check_key, 020h                 ; D
        test    eax, eax
        jz      @f
        mov     esi, AKODE_DIRECTION.EAST
        ;jmp     .set_turning_direction

@@:

.set_turning_direction:
        test    esi, esi
        js      @f
        stdcall akode.start_turning, esi
        jmp     .key

@@:
        stdcall akode.stop_turning
        jmp     .key

.mouse:

        jmp     .idle

.button:
.exit:
        ret
endp
; ============================================================================ ;

; ============================================================================ ;
; < eax = key scancode / 0 - no keys                                           ;
; ============================================================================ ;
proc get_key
        mcall   2                               ; get key scancode

        test    al, al
        jz      @f
        xor     eax, eax
        jmp     .exit

@@:
        shr     eax, 8

        cmp     eax, 0E1h
        jne     @f
        mcall   2
        mcall   2
        xor     eax, eax
        jmp     .exit

@@:
        xor     ebx, ebx
        mov     ecx, eax

        cmp     eax, 0E0h
        jne     @f
        mcall   2
        shr     eax, 8
        mov     ecx, eax
        or      eax, 0E000h
        mov     ebx, 128

@@:
        test    ecx, 80h
        jnz     .key_up

        ; key down
        add     ebx, ecx
        lea     ebx, [PressedKeys + ebx * 4]

        mov     edx, [ebx]
        test    edx, edx
        jz      @f

        xor     eax, eax
        jmp     .exit

@@:
        inc     edx
        mov     [ebx], edx
        jmp     .exit

.key_up:
        and     ecx, 7Fh
        add     ebx, ecx
        xor     edx, edx
        mov     [PressedKeys + ebx * 4], edx

.exit:
        ret
endp
; ============================================================================ ;

; ============================================================================ ;
; < eax = 1 - key pressed / 0 - not pressed                                    ;
; ============================================================================ ;
proc check_key scancode
        mov     eax, [scancode]
        mov     ecx, eax
        shr     eax, 8
        and     ecx, 7Fh
        xor     ebx, ebx

        cmp     eax, 0E0h
        jne     @f
        mov     ebx, 128

@@:
        add     ebx, ecx
        mov     eax, [PressedKeys + ebx * 4]

        ret
endp
; ============================================================================ ;

; ============================================================================ ;
proc level_load_callback uses eax ebx ecx, load_action, action_result
        mov     eax, [load_action]
        mov     ebx, [action_result]
        xor     ecx, ecx

        cmp     eax, AKODE_LEVEL_LOAD.START
        jne     @f
        stdcall draw_image, [LevelLoadingImagePtr], ecx, ecx, [MainWindowWidth], [MainWindowHeight]

        cmp     ebx, -1
        je      .level_load_failed

        mov     [GameMessage], ebx
        inc     ecx
        mov     [HudPanelNeedsRedraw], ecx
        jmp     .exit

@@:     cmp     eax, AKODE_LEVEL_LOAD.END
        jne     @f
        DEBUGF  DEBUG_INFO, 'Level load result: %u\n', ebx

        test    ebx, ebx
        jnz     .exit

.level_load_failed:
        DEBUGF  DEBUG_ERR, 'Failed to load level\n'
        mov     [GameStatus], GAME_STATUS.LEVEL_LOAD_FAILED
        ;jmp     .exit

@@:

.exit:
        ret
endp
; ============================================================================ ;

; ============================================================================ ;
proc action_callback action, cell_x, cell_y, action_result
        m2m     [GameMessage], [action_result]
        mov     [HudPanelNeedsRedraw], 1
        ret
endp
; ============================================================================ ;

; ============================================================================ ;
proc add_object_to_inventory uses eax ecx edi, object_id, object_image_ptr
        mov     edi, Inventory
        mov     ecx, INVENTORY_SIZE

.inventory_loop:
        mov     eax, [edi]
        test    eax, eax
        jnz     @f

        mov     eax, [object_id]
        mov     [edi], eax
        mov     eax, [object_image_ptr]
        mov     [edi + 4], eax
        mov     [HudPanelNeedsRedraw], ecx
        jmp     .exit

@@:
        add     edi, 8

        sub     ecx, 1
        jnz     .inventory_loop

.exit:
        ret
endp
; ============================================================================ ;

; ============================================================================ ;
proc remove_object_from_inventory uses eax ecx esi, object_id
        mov     eax, [object_id]
        mov     esi, Inventory
        mov     ecx, INVENTORY_SIZE

.inventory_loop:
        cmp     [esi], eax
        jne     @f

        xor     eax, eax
        mov     [esi], eax
        mov     [esi + 4], eax
        mov     [HudPanelNeedsRedraw], ecx
        jmp     .exit

@@:
        add     esi, 8

        sub     ecx, 1
        jnz     .inventory_loop

.exit:
        ret
endp
; ============================================================================ ;

; ============================================================================ ;
; < eax = pointer to image data / 0 - fail                                     ;
; ============================================================================ ;
proc load_object_image image_path_ptr
        stdcall akode.load_and_scale_image, [image_path_ptr], OBJECT_IMAGE_WIDTH, OBJECT_IMAGE_HEIGHT, 0

        ret
endp
; ============================================================================ ;

; ============================================================================ ;
proc free_object_image uses eax ebx ecx, image_ptr
        mov     ecx, [image_ptr]
        test    ecx, ecx
        jz      .exit

        mcall   68, 13

.exit:
        ret
endp
; ============================================================================ ;

; ============================================================================ ;
proc player_death
        mov     [GameStatus], GAME_STATUS.DEAD
        ret
endp
; ============================================================================ ;

; ============================================================================ ;
proc game_over
        mov     [GameStatus], GAME_STATUS.END
        ret
endp
; ============================================================================ ;

; ============================ Initialized data ============================== ;
DATA
        include 'data.inc'

        ; for debug-fdo
        include_debug_strings

        align 4
        @IMPORT:
        include 'import.inc'

; =========================== Uninitialized data ============================= ;
UDATA
        include 'udata.inc'

; ================================= The End ================================== ;
MEOS_APP_END