; ***************************************************************************
; 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_BUTTON_ASM
#define AQH_AVR_GUI2_BUTTON_ASM


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

.equ BUTTON_OFFS_BEGIN       =  WIDGET_SIZE
.equ BUTTON_OFFS_MODE        =  BUTTON_OFFS_BEGIN+0
.equ BUTTON_OFFS_STATE       =  BUTTON_OFFS_BEGIN+1
.equ BUTTON_OFFS_TIMER       =  BUTTON_OFFS_BEGIN+2
.equ BUTTON_SIZE             =  BUTTON_OFFS_BEGIN+3


.equ BUTTON_REPEAT_TIMER_WAITREPEAT = 10
.equ BUTTON_REPEAT_TIMER_REPEAT     = 3

; button modes
.equ BUTTON_MODE_NORMAL   = 0
.equ BUTTON_MODE_REPEATED = 1
.equ BUTTON_MODE_LONG     = 2

; button states
.equ BUTTON_STATE_INACTIVE   = 0
.equ BUTTON_STATE_ACTIVE     = 1
.equ BUTTON_STATE_WAITREPEAT = 2
.equ BUTTON_STATE_REPEATING  = 3


.equ BUTTON_LONGPRESS_TIME100MS = 50


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

.cseg



; ---------------------------------------------------------------------------
; @routine Button_new @global
;
; @return CFLAG set of okay, cleared otherwise
; @return Y address of newly created object
; @param X parent widget
; @param r16 value for OBJECT_OFFS_OPTS
; @param r17 value for WIDGET_OFFS_PACK
; @param r20 mode
; @clobbers any

Button_new:
  push r20
    ldi r24, LOW(BUTTON_SIZE)
    ldi r25, HIGH(BUTTON_SIZE)
    bigcall Object_Alloc           ; (!r16, !r17, !X)
  pop r20
  brcc Button_new_ret
  rcall Button_Init               ; (r16, r17, X)
  sec
Button_new_ret:
  ret
; @end



; ---------------------------------------------------------------------------
; @routine Button_Init @global
;
; @param Y address of widget
; @param X parent widget (if any)
; @param r16 value for OBJECT_OFFS_OPTS
; @param r17 value for WIDGET_OFFS_PACK
; @param r20 mode
; @clobbers r16, r17, X

Button_Init:
  push r20
    ; call base class
    bigcall Widget_Init                       ; (r16, r17, X)
  pop r20
  
  ; setup button data
  std Y+BUTTON_OFFS_MODE, r20

  ; set input and timer opts
  ldd r16, Y+OBJECT_OFFS_OPTS
  sbr r16, (1<<WIDGET_OPTS_INPUT_BIT) | (1<<OBJECT_OPTS_TIMER_BIT)
  std Y+OBJECT_OFFS_OPTS, r16
  
  ; set default signal map
  ldi r16, LOW(Button_DefaultSignalmap*2)
  std Y+OBJECT_OFFS_SIGNALMAP_LO, r16
  ldi r16, HIGH(Button_DefaultSignalmap*2)
  std Y+OBJECT_OFFS_SIGNALMAP_HI, r16

  ret
; @end



; ---------------------------------------------------------------------------
; @routine Button_OnSetValueStyle @global
;
; @param Y address of widget
; @param X=new style

Button_OnSetValueStyle:
  std Y+WIDGET_OFFS_STYLE_LO, xl
  std Y+WIDGET_OFFS_STYLE_HI, xh
  bigcall OBJ_GetFirstChild
  brcc Button_OnSetValueStyle_ret
  push yl
    push yh
      mov yl, r18
      mov yh, r19
      bigcall Widget_SetStyle        ; set style in child
    pop yh
  pop yl
Button_OnSetValueStyle_ret:
  sec
  ret
; @end



; ---------------------------------------------------------------------------
; @routine Button_OnTouchBegin @global
;
; @param Y address of widget
; @param xl param1
; @param xh param2
; @return CFLAG set if signal handled
; @clobbers any, !Y

Button_OnTouchBegin:
  ldi r16, (1<<WIDGET_FLAGS_ACTIVATED_BIT) | (1<<WIDGET_FLAGS_DIRTY_BIT)
  bigcall OBJ_AddFlagsDown    ; (r17, r18, r19)

  ldd r16, Y+BUTTON_OFFS_MODE
  cpi r16, BUTTON_MODE_REPEATED
  breq Button_OnTouchBegin_repeated
  cpi r16, BUTTON_MODE_LONG
  breq Button_OnTouchBegin_setActiveResetTimer
  cpi r16, BUTTON_MODE_NORMAL
  brne Button_OnTouchBegin_done
Button_OnTouchBegin_setActiveResetTimer:
  ldi r16, BUTTON_STATE_ACTIVE
  std Y+BUTTON_OFFS_STATE, r16
  clr r16
  std Y+BUTTON_OFFS_TIMER, r16      ; reset timer
  rjmp Button_OnTouchBegin_done
Button_OnTouchBegin_repeated:
  ldi r16, BUTTON_STATE_WAITREPEAT
  std Y+BUTTON_OFFS_STATE, r16
  ldi r16, BUTTON_REPEAT_TIMER_WAITREPEAT
  std Y+BUTTON_OFFS_TIMER, r16
Button_OnTouchBegin_done:
  sec
  ret
; @end



; ---------------------------------------------------------------------------
; @routine Button_OnTouchEnd @global
;
; @param Y address of widget
; @param xl param1
; @param xh param2
; @return CFLAG set if signal handled
; @clobbers any, !Y

Button_OnTouchEnd:
  ldi r16, (1<<WIDGET_FLAGS_ACTIVATED_BIT)
  bigcall OBJ_SubFlagsDown    ; (r17, r18, r19)
  ldi r16, (1<<WIDGET_FLAGS_DIRTY_BIT)
  bigcall OBJ_AddFlagsDown    ; (r17, r18, r19)

  ; check mode
  ldd r17, Y+BUTTON_OFFS_MODE
  cpi r17, BUTTON_MODE_REPEATED
  breq Button_OnTouchEnd_repeating
  cpi r17, BUTTON_MODE_NORMAL
  breq Button_OnTouchEnd_emitCommand
  cpi r17, BUTTON_MODE_LONG
  brne Button_OnTouchEnd_done
Button_OnTouchEnd_long:
  ldd r17, Y+BUTTON_OFFS_TIMER
  cpi r17, BUTTON_LONGPRESS_TIME100MS
  brcs Button_OnTouchEnd_emitCommand
  ldi r16, WIDGET_SIGNAL_ALTCMD
  rjmp Button_OnTouchEnd_emit
Button_OnTouchEnd_repeating:
  ldd r16, Y+BUTTON_OFFS_STATE
  cpi r16, BUTTON_STATE_REPEATING
  breq Button_OnTouchEnd_done
Button_OnTouchEnd_emitCommand:
  ldi r16, WIDGET_SIGNAL_COMMAND
Button_OnTouchEnd_emit:
  bigcall OBJ_EmitSignal
Button_OnTouchEnd_done:
  clr r16
  std Y+BUTTON_OFFS_TIMER, r16
  ldi r16, BUTTON_STATE_INACTIVE
  std Y+BUTTON_OFFS_STATE, r16
  sec
  ret
; @end



; ---------------------------------------------------------------------------
; @routine Button_OnTouchMove @global
;
; @param Y address of widget
; @param xl param1
; @param xh param2
; @return CFLAG set if signal handled
; @clobbers any, !Y

Button_OnTouchMove:
  sec
  ret
; @end



; ---------------------------------------------------------------------------
; @routine Button_OnTimer @global
;
; @param Y address of widget
; @param xl param1
; @param xh param2
; @return CFLAG set if signal handled
; @clobbers any, !Y

Button_OnTimer:
  ldd r16, Y+BUTTON_OFFS_MODE
  cpi r16, BUTTON_MODE_NORMAL
  breq Button_OnTimer_ret          ; nothing to do in normal mode
  cpi r16, BUTTON_MODE_REPEATED
  breq Button_OnTimer_repeated     ; handle repeats in repeating mode
  cpi r16, BUTTON_MODE_LONG
  brne Button_OnTimer_ret          ; jump on invalid mode
  ; long mode: increment timer counter
  ldd r16, Y+BUTTON_OFFS_TIMER
  inc r16
  breq Button_OnTimer_ret
  std Y+BUTTON_OFFS_TIMER, r16
  rjmp Button_OnTimer_ret
Button_OnTimer_repeated:
  ldd r16, Y+BUTTON_OFFS_TIMER
  tst r16
  breq Button_OnTimer_ret
  dec r16
  std Y+BUTTON_OFFS_TIMER, r16
  brne Button_OnTimer_ret
  ; timer elapsed
  ldd r17, Y+BUTTON_OFFS_STATE
  cpi r17, BUTTON_STATE_WAITREPEAT
  breq Button_OnTimer_waitRepeat
  cpi r17, BUTTON_STATE_REPEATING
  breq Button_OnTimer_repeating
  rjmp Button_OnTimer_ret
Button_OnTimer_waitRepeat:
  ldi r17, BUTTON_STATE_REPEATING
  std Y+BUTTON_OFFS_STATE, r17
Button_OnTimer_repeating:
  ldi r16, BUTTON_REPEAT_TIMER_REPEAT
  std Y+BUTTON_OFFS_TIMER, r16
  ldi r16, WIDGET_SIGNAL_COMMAND
  bigcall OBJ_EmitSignal
Button_OnTimer_ret:
  sec
  ret
; @end



; ---------------------------------------------------------------------------
; @routine Button_OnGetDefaultWidth @global
;
; @param Y address of widget
; @param r17 value requested
; @param xl param1
; @param xh param2
; @return CFLAG set if signal handled
; @return r19:r18 value
; @clobbers any, !Y

Button_OnGetDefaultWidth:
  bigcall OBJ_GetFirstChild
  brcc Button_OnGetDefaultWidth_ret
  push yl
    push yh
      mov yl, r18
      mov yh, r19
      bigcall Widget_GetDefaultWidth
    pop yh
  pop yl
Button_OnGetDefaultWidth_ret:
  sec
  ret
; @end



; ---------------------------------------------------------------------------
; @routine Button_OnGetDefaultHeight @global
;
; @param Y address of widget
; @param xl param1
; @param xh param2
; @return CFLAG set if signal handled
; @return r19:r18 value
; @clobbers any, !Y

Button_OnGetDefaultHeight:
  bigcall OBJ_GetFirstChild
  brcc Button_OnGetDefaultHeight_ret
  push yl
    push yh
      mov yl, r18
      mov yh, r19
      bigcall Widget_GetDefaultHeight
    pop yh
  pop yl
Button_OnGetDefaultHeight_ret:
  sec
  ret
; @end



; ---------------------------------------------------------------------------
; @routine Button_OnLayout @global
;
; @param Y address of widget
; @return CFLAG set if signal handled
; @clobbers any, !Y

Button_OnLayout:
  bigcall OBJ_GetFirstChild
  brcc Button_OnLayout_ret
  
  ldd r16, Y+WIDGET_OFFS_WIDTH_LO
  ldd r17, Y+WIDGET_OFFS_WIDTH_HI
  ldd r20, Y+WIDGET_OFFS_HEIGHT_LO
  ldd r21, Y+WIDGET_OFFS_HEIGHT_HI
  push yl
    push yh
      mov yl, r18
      mov yh, r19
      std Y+WIDGET_OFFS_WIDTH_LO, r16
      std Y+WIDGET_OFFS_WIDTH_HI, r17
      std Y+WIDGET_OFFS_HEIGHT_LO, r20
      std Y+WIDGET_OFFS_HEIGHT_HI, r21
      clr r16
      std Y+WIDGET_OFFS_X_LO, r16
      std Y+WIDGET_OFFS_X_HI, r16
      std Y+WIDGET_OFFS_Y_LO, r16
      std Y+WIDGET_OFFS_Y_HI, r16
      ; force layout and redraw
      ldd r16, Y+OBJECT_OFFS_FLAGS
      ori r16, (1<<WIDGET_FLAGS_LAYOUT_BIT) | (1<<WIDGET_FLAGS_DIRTY_BIT)
      std Y+OBJECT_OFFS_FLAGS, r16
    pop yh
  pop yl  

Button_OnLayout_ret:
  ldd r16, Y+OBJECT_OFFS_FLAGS
  cbr r16, (1<<WIDGET_FLAGS_LAYOUT_BIT)
  std Y+OBJECT_OFFS_FLAGS, r16

  sec
  ret
; @end





; ***************************************************************************
; data in FLASH

Button_DefaultSignalmap:
  ; header
  .dw Widget_DefaultSignalmap*2  ; next table to use
  ; entries
  .db 0,                           OBJECT_SIGNAL_TIMER,       LOW(Button_OnTimer),            HIGH(Button_OnTimer)
  .db 0,                           WIDGET_SIGNAL_DRAW,        LOW(Widget_OnDrawNop),          HIGH(Widget_OnDrawNop)
  .db 0,                           WIDGET_SIGNAL_TOUCH,       LOW(Widget_OnTouch),            HIGH(Widget_OnTouch)
  .db 0,                           WIDGET_SIGNAL_TOUCH_BEGIN, LOW(Button_OnTouchBegin),       HIGH(Button_OnTouchBegin)
  .db 0,                           WIDGET_SIGNAL_TOUCH_END,   LOW(Button_OnTouchEnd),         HIGH(Button_OnTouchEnd)
  .db 0,                           WIDGET_SIGNAL_TOUCH_MOVE,  LOW(Button_OnTouchMove),        HIGH(Button_OnTouchMove)
  .db 0,                           WIDGET_SIGNAL_LAYOUT,      LOW(Button_OnLayout),           HIGH(Button_OnLayout)
  .db WIDGET_VALUE_DEFAULT_WIDTH,  WIDGET_SIGNAL_GETVALUE,    LOW(Button_OnGetDefaultWidth),  HIGH(Button_OnGetDefaultWidth)
  .db WIDGET_VALUE_DEFAULT_HEIGHT, WIDGET_SIGNAL_GETVALUE,    LOW(Button_OnGetDefaultHeight), HIGH(Button_OnGetDefaultHeight)
  .db WIDGET_VALUE_STYLE,          WIDGET_SIGNAL_SETVALUE,    LOW(Button_OnSetValueStyle),    HIGH(Button_OnSetValueStyle)

  .db 0, 0, 0, 0  ; end of table


#endif

