; ***************************************************************************
; copyright   : (C) 2026 by Martin Preuss
; email       : martin@libchipcard.de
;
; ***************************************************************************
; * This file is part of the project "AqHome".                              *
; * Please see toplevel file COPYING of that project for license details.   *
; ***************************************************************************

#ifndef AQH_AVR_GUI2_GUIAPP_ASM
#define AQH_AVR_GUI2_GUIAPP_ASM


; ***************************************************************************
; defines

; GuiApp in flash
.equ GUIAPP_OFFS_GUIAPP            =  OBJECT_SIZE
.equ GUIAPP_OFFS_SDRAM_LO          =  GUIAPP_OFFS_GUIAPP+0
.equ GUIAPP_OFFS_SDRAM_HI          =  GUIAPP_OFFS_GUIAPP+1
.equ GUIAPP_OFFS_ROOTWIDGET_LO     =  GUIAPP_OFFS_GUIAPP+2
.equ GUIAPP_OFFS_ROOTWIDGET_HI     =  GUIAPP_OFFS_GUIAPP+3
.equ GUIAPP_SIZE                   =  GUIAPP_OFFS_GUIAPP+4

; signals
.equ GUIAPP_SIGNAL_RAISE        =  OBJECT_SIGNAL_NEXTFREE+0 ; app->cntrl: X=WIDGET to raise
.equ GUIAPP_SIGNAL_SHOW         =  OBJECT_SIGNAL_NEXTFREE+1 ; cntrl->app
.equ GUIAPP_SIGNAL_HIDE         =  OBJECT_SIGNAL_NEXTFREE+2 ; cntrl->app
.equ GUIAPP_SIGNAL_DEBUG        =  OBJECT_SIGNAL_NEXTFREE+3
.equ GUIAPP_SIGNAL_NEXTFREE     =  OBJECT_SIGNAL_NEXTFREE+4

; SDRAM data for guiapp
.equ GUIAPP_SD_OFFS_FLAGS            = 0
.equ GUIAPP_SD_OFFS_DRAWTIMER        = 1
.equ GUIAPP_SD_OFFS_CURRENTWIDGET_LO = 2
.equ GUIAPP_SD_OFFS_CURRENTWIDGET_HI = 3
.equ GUIAPP_SD_OFFS_TOUCHWIDGET_LO   = 4
.equ GUIAPP_SD_OFFS_TOUCHWIDGET_HI   = 5
.equ GUIAPP_SD_SIZE                  = 6


.equ GUIAPP_DRAWTIMER = 3



; ***************************************************************************
; data

.dseg

guiapp_touch_event:
  .byte WIDGET_DATA_TOUCH_SIZE



; ***************************************************************************
; code

.cseg



; ---------------------------------------------------------------------------
; @routine GuiApp_Init
;
; @param Z byte address of guiapp object (for LPM!)

GuiApp_Init:
  ldi r16, OBJECT_SIGNAL_CREATE
  clr r17
  bigcall OBJ_HandleSignal
  rcall GuiApp_ShowFirstView
  ret
; @end



; ---------------------------------------------------------------------------
; @routine GuiApp_Every100ms @global
;
; @param Z byte address of guiapp object (for LPM!)

GuiApp_Every100ms:
  ldi r16, OBJECT_SIGNAL_TIMER
  clr r17
  bigcall OBJ_HandleSignal
  ret
; @end



; ---------------------------------------------------------------------------
; @routine GuiApp_MsgReceived @global
;
; @param Z byte address of guiapp object (for LPM!)

GuiApp_MsgReceived:
  ldi r16, OBJECT_SIGNAL_RECVMSG
  clr r17
  bigcall OBJ_HandleSignal
  ret
; @end



; ---------------------------------------------------------------------------
; @routine GuiApp_OnTimer
;
; @param Z byte address of guiapp object (for LPM!)

GuiApp_OnTimer:
  ; check for touch event
  rcall guiAppCheckTouch

  ; send timer signal to all accepting widgets
  push zl
    push zh
      adiw zh:zl, GUIAPP_OFFS_ROOTWIDGET_LO
      lpm r16, Z+
      lpm zh, Z
      mov zl, r16
      or r17, zh
      breq GuiApp_OnTimer_l1
      bigcall Widget_Tree_SendTimerSignal
GuiApp_OnTimer_l1:
    pop zh
  pop zl

  ; possibly draw widgets
  rcall GuiApp_GetSdramPtr
  ldd r16, Y+GUIAPP_SD_OFFS_DRAWTIMER
  tst r16
  breq GuiApp_OnTimer_ret
  dec r16
  brne GuiApp_OnTimer_writeDrawTimer
  push yl
    push yh
      rcall guiAppDrawWidgets
    pop yh
  pop yl
  ldi r16, GUIAPP_DRAWTIMER
GuiApp_OnTimer_writeDrawTimer:
  std Y+GUIAPP_SD_OFFS_DRAWTIMER, r16
GuiApp_OnTimer_ret:
  sec
  ret
; @end



; ---------------------------------------------------------------------------
; @routine GuiApp_OnTouch
;
; @param Z byte address of guiapp object (for LPM!)
; @param X pointer to touch event in SDRAM

GuiApp_OnTouch:
  push zl
    push zh
      rcall GuiApp_GetSdramPtr
      ldd r18, Y+GUIAPP_SD_OFFS_TOUCHWIDGET_LO
      ldd r19, Y+GUIAPP_SD_OFFS_TOUCHWIDGET_HI
      mov r16, r18
      or r16, r19
      breq GuiApp_OnTouch_sendToAll
      mov zl, r18
      mov zh, r19
      ldi r16, WIDGET_SIGNAL_TOUCH
      clr r17
      bigcall OBJ_HandleSignal
      rjmp GuiApp_OnTouch_done
GuiApp_OnTouch_sendToAll:
      rcall GuiApp_GetRootWidgetToZ
      brcc GuiApp_OnTouch_done
      ; send touch signal (TODO: check for grabbed touch)
      bigcall Widget_Tree_SendTouchSignal
GuiApp_OnTouch_done:
    pop zh
  pop zl
  sec
  ret
; @end



; ---------------------------------------------------------------------------
; @routine GuiApp_OnMsgReceived @global
;
; @param Z byte address of guiapp object (for LPM!)
; @param X pointer to message received in SDRAM

GuiApp_OnMsgReceived:
  push zl
    push zh
      rcall GuiApp_GetRootWidgetToZ
      brcc GuiApp_OnMsgReceived_done
      bigcall Widget_Tree_SendMsgRecvSignal
GuiApp_OnMsgReceived_done:
    pop zh
  pop zl
  sec
  ret
; @end



; ---------------------------------------------------------------------------
; @routine GuiApp_OnCreate @global
;
; @param Z byte address of object (for LPM!)
; @return CFLAG set if signal handled
; @clobbers any, !Z

GuiApp_OnCreate:
  ; init sdram data
  rcall GuiApp_GetSdramPtr
  ldi r17, GUIAPP_SD_SIZE
  clr r16
  push yl
    push yh
GuiApp_OnCreate_loop:
        st Y+, r16
        dec r17
      brne GuiApp_OnCreate_loop
    pop yh
  pop yl
  clr r16
  std Y+GUIAPP_SD_OFFS_FLAGS, r16
  ldi r16, GUIAPP_DRAWTIMER
  std Y+GUIAPP_SD_OFFS_DRAWTIMER, r16

  rcall guiAppInitWidgets

  sec
  ret
; @end



; ---------------------------------------------------------------------------
; @routine GuiApp_GetSdramPtr @global
;
; @param  Z  byte address of object (for LPM!)
; @return Y  pointer to SDRAM data for widget
; @clobbers none

GuiApp_GetSdramPtr:
  adiw zh:zl, GUIAPP_OFFS_SDRAM_LO
  lpm yl, Z+
  lpm yh, Z
  sbiw zh:zl, GUIAPP_OFFS_SDRAM_LO+1
  ret
; @end



; ---------------------------------------------------------------------------
; @routine GuiApp_GetRootWidgetToZ @global
;
; @param  Z  byte address of object (for LPM!)
; @return CFLAG set if there is a root widget, cleared otherwise
; @return Z  byte address pointer to root widget (for LPM!), if CFLAG set, garbage otherwise
; @clobbers none

GuiApp_GetRootWidgetToZ:
  rcall OBJ_IsObject
  brcc GuiApp_GetRootWidgetToZ_ret
  adiw zh:zl, GUIAPP_OFFS_ROOTWIDGET_LO
  lpm r16, Z+
  lpm zh, Z
  mov zl, r16
  rcall OBJ_IsObject
GuiApp_GetRootWidgetToZ_ret:
  ret
; @end



; ---------------------------------------------------------------------------
; @routine GuiApp_ShowFirstView @global
;
; @param Z byte address of guiapp object (for LPM!)

GuiApp_ShowFirstView:
  push zl
    push zh
      rcall GuiApp_GetRootWidgetToZ
      brcc GuiApp_ShowFirstView_error

      bigcall OBJ_GetFirstChild
      brcc GuiApp_ShowFirstView_error
      mov xl, r18
      mov xh, r19
    pop zh
  pop zl
  rcall GuiApp_ShowView
  rjmp GuiApp_ShowFirstView_ret
GuiApp_ShowFirstView_error:
    pop zh
  pop zl
GuiApp_ShowFirstView_ret:
  ret
; @end



; ---------------------------------------------------------------------------
; @routine GuiApp_ShowView @global
;
; @param Z byte address of object (for LPM!)
; @param X byte address of object to show (for LPM!)
; @return CFLAG set if signal handled
; @clobbers any, !Z

GuiApp_ShowView:
  rcall GuiApp_GetSdramPtr                    ; Y=SDRAM
  ldd r18, Y+GUIAPP_SD_OFFS_CURRENTWIDGET_LO
  ldd r19, Y+GUIAPP_SD_OFFS_CURRENTWIDGET_HI
  eor r18, xl                                 ; 0 if same
  eor r19, xh                                 ; 0 if same
  or r18, r19                                 ; 0 if r19:r18==X
  breq GuiApp_ShowView_ret                    ; same, no change

  rcall guiAppHideCurrent

  ; store new view
  std Y+GUIAPP_SD_OFFS_CURRENTWIDGET_LO, xl
  std Y+GUIAPP_SD_OFFS_CURRENTWIDGET_HI, xh

  ; show new (if any)
  rcall guiAppShowCurrent

GuiApp_ShowView_ret:
  ret
; @end




; ---------------------------------------------------------------------------
; @routine GuiApp_GrabTouchEvents
;
; @param Z byte address of gui app object (for LPM!)
; @param X byte address of widget grabbing touch events
; @return CFLAG set if grabbed, cleared otherwise
; @clobbers R16, R17, Y

GuiApp_GrabTouchEvents:
  rcall OBJ_IsObject                      ; (none)
  brcc GuiApp_GrabTouchEvents_ret
  rcall GuiApp_GetSdramPtr                ; (none)
  ldd r16, Y+GUIAPP_SD_OFFS_TOUCHWIDGET_LO
  ldd r17, Y+GUIAPP_SD_OFFS_TOUCHWIDGET_HI
  or r16, r17
  clc
  brne GuiApp_GrabTouchEvents_ret         ; only grab if not already grabbed!
  std Y+GUIAPP_SD_OFFS_TOUCHWIDGET_LO, xl
  std Y+GUIAPP_SD_OFFS_TOUCHWIDGET_HI, xh
  sec
GuiApp_GrabTouchEvents_ret:
  ret
; @end



; ---------------------------------------------------------------------------
; @routine GuiApp_UngrabTouchEvents
;
; @param Z byte address of gui app object (for LPM!)
; @param X byte address of widget ungrabbing touch events
; @return CFLAG set if ungrabbed, cleared otherwise
; @clobbers R16, R17

GuiApp_UngrabTouchEvents:
  rcall OBJ_IsObject                      ; (none)
  brcc GuiApp_UngrabTouchEvents_ret
  rcall GuiApp_GetSdramPtr                ; (none)
  ldd r16, Y+GUIAPP_SD_OFFS_TOUCHWIDGET_LO
  ldd r17, Y+GUIAPP_SD_OFFS_TOUCHWIDGET_HI
  eor r16, xl
  eor r17, xh
  or r16, r17
  clc
  brne GuiApp_UngrabTouchEvents_ret         ; only ungrab same widget!
  clr r16
  std Y+GUIAPP_SD_OFFS_TOUCHWIDGET_LO, r16
  std Y+GUIAPP_SD_OFFS_TOUCHWIDGET_HI, r16
  sec
GuiApp_UngrabTouchEvents_ret:
  ret
; @end



; ---------------------------------------------------------------------------
; @routine guiAppHideCurrent
;
; @param Z byte address of gui app object (for LPM!)
; @clobbers any, !X, !Y. !Z

guiAppHideCurrent:
  push zl
    push zh
      rcall GuiApp_GetSdramPtr                    ; Y=SDRAM
      ldd zl, Y+GUIAPP_SD_OFFS_CURRENTWIDGET_LO
      ldd zh, Y+GUIAPP_SD_OFFS_CURRENTWIDGET_HI
      mov r16, zl
      or r16, zh
      breq guiAppHideCurrent_done
      ldi r16, WIDGET_SIGNAL_HIDE
      clr r17
      bigcall OBJ_HandleSignal                    ; (any, !X, !Y. !Z)
      ldi r16, (1<<WIDGET_FLAGS_ACTIVE_BIT)
      bigcall Widget_SubFlagsDown                 ; (R17, R18, R19, R22, R23)
guiAppHideCurrent_done:
    pop zh
  pop zl
  ret
; @end



; ---------------------------------------------------------------------------
; @routine guiAppShowCurrent
;
; @param Z byte address of gui app object (for LPM!)
; @clobbers any, !X, !Y. !Z

guiAppShowCurrent:
  push zl
    push zh
      rcall GuiApp_GetSdramPtr                    ; Y=SDRAM
      ldd zl, Y+GUIAPP_SD_OFFS_CURRENTWIDGET_LO
      ldd zh, Y+GUIAPP_SD_OFFS_CURRENTWIDGET_HI
      mov r16, zl
      or r16, zh
      breq guiAppShowCurrent_done
      ldi r16, WIDGET_SIGNAL_SHOW
      clr r17
      bigcall OBJ_HandleSignal
      ldi r16, (1<<WIDGET_FLAGS_ACTIVE_BIT) | (1<<WIDGET_FLAGS_DIRTY_BIT)
      bigcall Widget_AddFlagsDown
guiAppShowCurrent_done:
    pop zh
  pop zl
  ret
; @end



; ---------------------------------------------------------------------------
; @routine guiAppInitWidgets
;
; @param Z byte address of guiapp object (for LPM!)

guiAppInitWidgets:
  ; init widgets
  push zl
    push zh
      rcall GuiApp_GetRootWidgetToZ
      brcc guiAppInitWidgets_done
      ldi r16, OBJECT_SIGNAL_CREATE
      clr r17
      bigcall OBJ_TreeAllHandleSignal
      ldi r16, (1<<WIDGET_FLAGS_ACTIVE_BIT) | (1<<WIDGET_FLAGS_DIRTY_BIT)  ; root window is always active
      rcall Widget_AddFlags
guiAppInitWidgets_done:
    pop zh
  pop zl
  ret
; @end



; ---------------------------------------------------------------------------
; @routine guiAppDrawWidgets
;
; @param Z byte address of guiapp object (for LPM!)

guiAppDrawWidgets:
  push zl
    push zh
      rcall GuiApp_GetRootWidgetToZ
      brcc guiAppDrawWidgets_done
      bigcall Widget_Tree_SendDrawSignal
guiAppDrawWidgets_done:
    pop zh
  pop zl
guiAppDrawWidgets_ret:
  ret
; @end



; ---------------------------------------------------------------------------
; @routine guiAppCheckTouch
;
; @param Z byte address of guiapp object (for LPM!)

guiAppCheckTouch:
  bigcall Display_InputGetState    ; r16=flags, r5:r4=x, r7:r6=y
  mov r17, r16
  andi r17, (1<<DISPLAY_IFLAGS_CHGCOORD_BIT) | (1<<DISPLAY_IFLAGS_CHGPRESS_BIT)
  breq guiAppCheckTouch_ret
  ldi xl, LOW(guiapp_touch_event)
  ldi xh, HIGH(guiapp_touch_event)
  st X+, r4  ; X
  st X+, r5
  st X+, r6  ; Y
  st X+, r7
  st X, r16  ; flags
  sbiw xh:xl, 4 ; (4 times incremented)
  ; send touch signal
  ldi r16, WIDGET_SIGNAL_TOUCH
  clr r17
  bigcall OBJ_HandleSignal
guiAppCheckTouch_ret:
  ret
; @end





guiAbortIfNotObject:
  bigcall OBJ_IsObject
  brcs guiAbortIfNotObject_ret
rcall DEBUG2
guiAbortIfNotObject_ret:
  ret
; @end



#endif
