; ***************************************************************************
; 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_GUI_HSPINNER_ASM
#define AQH_AVR_GUI_HSPINNER_ASM


; ***************************************************************************
; ***************************************************************************




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

.equ HSPINNER_OFFS_BEGIN        =  HLAYOUT_SIZE
.equ HSPINNER_OFFS_CURVALUE_LO  =  HSPINNER_OFFS_BEGIN+0
.equ HSPINNER_OFFS_CURVALUE_HI  =  HSPINNER_OFFS_BEGIN+1
.equ HSPINNER_OFFS_MINVALUE_LO  =  HSPINNER_OFFS_BEGIN+2
.equ HSPINNER_OFFS_MINVALUE_HI  =  HSPINNER_OFFS_BEGIN+3
.equ HSPINNER_OFFS_MAXVALUE_LO  =  HSPINNER_OFFS_BEGIN+4
.equ HSPINNER_OFFS_MAXVALUE_HI  =  HSPINNER_OFFS_BEGIN+5
.equ HSPINNER_OFFS_FLAGS        =  HSPINNER_OFFS_BEGIN+6
.equ HSPINNER_SIZE              =  HSPINNER_OFFS_BEGIN+7


; selectors
.equ HSPINNER_SEL_LEFT       = 1
.equ HSPINNER_SEL_RIGHT      = 2


; child widgets
.equ HSPINNER_CHILDIDX_LEFT  = 0
.equ HSPINNER_CHILDIDX_VALUE = 1
.equ HSPINNER_CHILDIDX_EIGHT = 2


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

.cseg



; ---------------------------------------------------------------------------
; @routine HSpinner_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 number of postkomma digits
; @param  r21 total number of digits (including komma, if r20>0)
; @clobbers any

HSpinner_new:
  ldi r24, LOW(HSPINNER_SIZE)
  ldi r25, HIGH(HSPINNER_SIZE)
  push r20
    push r21
      bigcall Object_Alloc           ; (!r16, !r17, !X)
    pop r21
  pop r20
  brcc HSpinner_new_ret
  rcall HSpinner_Init               ; (any, !Y)
  sec
HSpinner_new_ret:
  ret
; @end



; ---------------------------------------------------------------------------
; @routine HSpinner_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 number of postkomma digits
; @param r21 total number of digits (including komma, if r20>0)
; @clobbers any, !Y

HSpinner_Init:
  ; call base class
  push r20
    push r21
      ldi r20, HLAYOUT_MODE_EXPAND
      bigcall HLayout_Init
    pop r21
  pop r20
  brcc HSpinner_Init_ret

  ; set default signal map
  ldi r16, LOW(HSpinner_DefaultSignalmap*2)
  std Y+OBJECT_OFFS_SIGNALMAP_LO, r16
  ldi r16, HIGH(HSpinner_DefaultSignalmap*2)
  std Y+OBJECT_OFFS_SIGNALMAP_HI, r16

  rcall hSpinnerCreateChildren               ; (any, !Y)
HSpinner_Init_ret:
  ret
; @end



; ---------------------------------------------------------------------------
; @routine hSpinnerCreateChildren
;
; @param Y spinner object
; @param r20 number of postkomma digits
; @param r21 total number of digits (including komma, if r20>0)
; @clobbers any, !Y

hSpinnerCreateChildren:
  push yl
    push yh
      mov xl, yl                              ; parent
      mov xh, yh

      ; create left button
      push r20
        push r21
          call hSpinnerCreateLeftButton
        pop r21
      pop r20
      brcc hSpinnerCreateChildren_popRet
      ; create value label
      ldi r16, 0                                                                                           ; OPTS
      ldi r17, (WIDGET_PACK_END<<WIDGET_PACK_HSELF0_BIT) | (WIDGET_PACK_CENTER<<WIDGET_PACK_VSELF0_BIT) | \
               (WIDGET_PACK_END<<WIDGET_PACK_HCONTENT0_BIT) | (WIDGET_PACK_CENTER<<WIDGET_PACK_VCONTENT0_BIT)   ; PACK
      push xl
        push xh
          bigcall ValueLabel_new
        pop xh
      pop xl
      brcc hSpinnerCreateChildren_popRet

      ; create right button
      call hSpinnerCreateRightButton
hSpinnerCreateChildren_popRet:
    pop yh
  pop yl
  ret
; @end



hSpinnerCreateLeftButton:
  push xl
    push xh
      ; create button
      ldi r16, (1<<WIDGET_OPTS_BORDER_BIT)                                                                ; OPTS
      ldi r17, (WIDGET_PACK_BEGIN<<WIDGET_PACK_HSELF0_BIT) | (WIDGET_PACK_CENTER<<WIDGET_PACK_VSELF0_BIT) ; PACK
      ldi r20, LOW(RESSOURCE_TXT_LESSERTHAN_C)   ; text ressource
      ldi r21, HIGH(RESSOURCE_TXT_LESSERTHAN_C)
      ldi r22, HSPINNER_SEL_LEFT                 ; selector
      ldi r23, BUTTON_MODE_REPEATED
      bigcall TextButton_new
      brcc hSpinnerCreateLeftButton_done
    
      ; set style in Label part of the text button
      bigcall OBJ_GetFirstChild
      brcc hSpinnerCreateLeftButton_done
      mov yl, r18
      mov yh, r19
      ldi r16, LOW(HSpinner_ButtonStyle*2)
      std Y+WIDGET_OFFS_STYLE_LO, r16
      ldi r16, HIGH(HSpinner_ButtonStyle*2)
      std Y+WIDGET_OFFS_STYLE_HI, r16
hSpinnerCreateLeftButton_done:
    pop xh
  pop xl
  ret
; @end



hSpinnerCreateRightButton:
  push xl
    push xh
      ; create button
      ldi r16, (1<<WIDGET_OPTS_BORDER_BIT)                                                                ; OPTS
      ldi r17, (WIDGET_PACK_BEGIN<<WIDGET_PACK_HSELF0_BIT) | (WIDGET_PACK_CENTER<<WIDGET_PACK_VSELF0_BIT) ; PACK
      ldi r20, LOW(RESSOURCE_TXT_GREATERTHAN_C)   ; text ressource
      ldi r21, HIGH(RESSOURCE_TXT_GREATERTHAN_C)
      ldi r22, HSPINNER_SEL_RIGHT                 ; selector
      ldi r23, BUTTON_MODE_REPEATED
      bigcall TextButton_new
      brcc hSpinnerCreateRightButton_done
    
      ; set style in Label part of the text button
      bigcall OBJ_GetFirstChild
      brcc hSpinnerCreateRightButton_done
      mov yl, r18
      mov yh, r19
      ldi r16, LOW(HSpinner_ButtonStyle*2)
      std Y+WIDGET_OFFS_STYLE_LO, r16
      ldi r16, HIGH(HSpinner_ButtonStyle*2)
      std Y+WIDGET_OFFS_STYLE_HI, r16
hSpinnerCreateRightButton_done:
    pop xh
  pop xl
  ret
; @end




; ---------------------------------------------------------------------------
; @routine hSpinnerSetValue
;
; @param Y widget
; @param X new value
; @clobbers any, !Y

hSpinnerSetValue:
  std Y+HSPINNER_OFFS_CURVALUE_LO, xl
  std Y+HSPINNER_OFFS_CURVALUE_HI, xh
  rjmp hSpinnerSendValue
; @end



hSpinnerSendValue:
  ldi r16, HSPINNER_CHILDIDX_VALUE
  bigcall OBJ_GetChildAt                ; r19:r18=value label (r16)
  brcc HSpinner_OnCmdLeft_ret
  push yl
    push yh
      ldd xl, Y+HSPINNER_OFFS_CURVALUE_LO
      ldd xh, Y+HSPINNER_OFFS_CURVALUE_HI
      mov yl, r18
      mov yh, r19
      bigcall Widget_SetCurrentValue       ; (any, !Y)
    pop yh
  pop yl

  rcall hSpinnerEmitCmdSignal
  ret
; @end




; ---------------------------------------------------------------------------
; @routine hSpinnerValidate
;
; @param Y widget
; @return CFLAG set if value changed, cleared otherwise
; @clobbers r20, r21, X

hSpinnerValidate:
  ldd xl, Y+HSPINNER_OFFS_CURVALUE_LO
  ldd xh, Y+HSPINNER_OFFS_CURVALUE_HI
  ; compare against minimum
  ldd r20, Y+HSPINNER_OFFS_MINVALUE_LO
  ldd r21, Y+HSPINNER_OFFS_MINVALUE_HI
  cp xl, r20
  cpc xh, r21
  brcc hSpinnerValidate_l1
  mov xl, r20
  mov xh, r21
hSpinnerValidate_l1:
  ; compare against maximum
  ldd r20, Y+HSPINNER_OFFS_MAXVALUE_LO
  ldd r21, Y+HSPINNER_OFFS_MAXVALUE_HI
  cp r20, xl
  cpc r21, xh
  brcc hSpinnerValidate_l2
  mov xl, r20
  mov xh, r21
hSpinnerValidate_l2:
  ; check for change
  ldd r18, Y+HSPINNER_OFFS_CURVALUE_LO
  ldd r19, Y+HSPINNER_OFFS_CURVALUE_HI
  cp r18, xl
  brne hSpinnerValidate_l3
  cp r19, xh
  brne hSpinnerValidate_l3
  clc
  rjmp hSpinnerValidate_ret
hSpinnerValidate_l3:
  std Y+HSPINNER_OFFS_CURVALUE_LO, xl
  std Y+HSPINNER_OFFS_CURVALUE_HI, xh
  sec
hSpinnerValidate_ret:
  ret
; @end



; ---------------------------------------------------------------------------
; @routine hSpinnerEmitCmdSignal
;
; @param Y widget
; @clobbers any, !Y

hSpinnerEmitCmdSignal:
  ldi r16, WIDGET_SIGNAL_COMMAND
  ldd xl, Y+HSPINNER_OFFS_CURVALUE_LO
  ldd xh, Y+HSPINNER_OFFS_CURVALUE_HI
  bigjmp OBJ_EmitSignal
; @end




; ***************************************************************************
; signal handlers


; ---------------------------------------------------------------------------
; @routine HSpinner_OnCmdLeft @global
;
; @param Y widget

HSpinner_OnCmdLeft:
  ldd xl, Y+HSPINNER_OFFS_CURVALUE_LO
  ldd xh, Y+HSPINNER_OFFS_CURVALUE_HI
  ldd r18, Y+HSPINNER_OFFS_MINVALUE_LO
  ldd r19, Y+HSPINNER_OFFS_MINVALUE_HI
  
  cp r18, xl                  ; current value > minimum?
  cpc r19, xh
  brcc HSpinner_OnCmdLeft_ret ; no, jmp
  sbiw xh:xl, 1

  rcall hSpinnerSetValue
HSpinner_OnCmdLeft_ret:
  sec
  ret
; @end



; ---------------------------------------------------------------------------
; @routine HSpinner_OnCmdRight @global
;
; @param Y widget

HSpinner_OnCmdRight:
  ldd xl, Y+HSPINNER_OFFS_CURVALUE_LO
  ldd xh, Y+HSPINNER_OFFS_CURVALUE_HI
  ldd r18, Y+HSPINNER_OFFS_MAXVALUE_LO
  ldd r19, Y+HSPINNER_OFFS_MAXVALUE_HI
  
  cp xl, r18                   ; max value > current value?
  cpc xh, r19
  brcc HSpinner_OnCmdRight_ret ; no, jmp
  adiw xh:xl, 1

  rcall hSpinnerSetValue
HSpinner_OnCmdRight_ret:
  sec
  ret
; @end



; ---------------------------------------------------------------------------
; @routine HSpinner_OnSetValue @global
;
; @param Y widget
; @param X new value

HSpinner_OnSetValue:
  std Y+HSPINNER_OFFS_CURVALUE_LO, xl
  std Y+HSPINNER_OFFS_CURVALUE_HI, xh
  rcall hSpinnerValidate
  rcall hSpinnerSendValue
  sec
  ret
; @end



; ---------------------------------------------------------------------------
; @routine HSpinner_OnGetValue @global
;
; @param  Y       widget
; @return r19:r18 value

HSpinner_OnGetValue:
  ldd r18, Y+HSPINNER_OFFS_CURVALUE_LO
  ldd r19, Y+HSPINNER_OFFS_CURVALUE_HI
  sec
  ret
; @end



; ---------------------------------------------------------------------------
; @routine HSpinner_OnSetMinValue @global
;
; @param Y widget
; @param X new value

HSpinner_OnSetMinValue:
  std Y+HSPINNER_OFFS_MINVALUE_LO, xl
  std Y+HSPINNER_OFFS_MINVALUE_HI, xh
  rcall hSpinnerValidate
  brcc HSpinner_OnSetMinValue_done
  rcall hSpinnerSendValue
HSpinner_OnSetMinValue_done:
  sec
  ret
; @end



; ---------------------------------------------------------------------------
; @routine HSpinner_OnSetMaxValue @global
;
; @param Y widget
; @param X new value

HSpinner_OnSetMaxValue:
  std Y+HSPINNER_OFFS_MAXVALUE_LO, xl
  std Y+HSPINNER_OFFS_MAXVALUE_HI, xh
  rcall hSpinnerValidate
  brcc HSpinner_OnSetMaxValue_done
  rcall hSpinnerSendValue
HSpinner_OnSetMaxValue_done:
  sec
  ret
; @end







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


HSpinner_ButtonStyle:
  .dw DISPLAY_COLOR_BLACK        ; frontCol_norm
  .dw DISPLAY_COLOR_WHITE        ; backCol_norm
  .dw DISPLAY_COLOR_BLACK        ; borderCol_norm
  .dw DISPLAY_COLOR_WHITE        ; shadowCol_norm
  
  .dw DISPLAY_COLOR_WHITE        ; frontCol_activated
  .dw DISPLAY_COLOR_NAVY         ; backCol_activated
  .dw DISPLAY_COLOR_BLACK        ; borderCol_activated
  .dw DISPLAY_COLOR_WHITE        ; shadowCol_activated
  
  .db 6, 0                       ; outerBorderSize, innerBorderSize
  .dw ili9341Font12x16_1*2       ; font
  .db 12, 16                     ; charWidth, charHeight
; @end



HSpinner_DefaultSignalmap:
  ; header
  .dw HLayout_DefaultSignalmap*2  ; next table to use
  ; entries

  .db HSPINNER_SEL_LEFT,         WIDGET_SIGNAL_COMMAND,  LOW(HSpinner_OnCmdLeft),     HIGH(HSpinner_OnCmdLeft)
  .db HSPINNER_SEL_RIGHT,        WIDGET_SIGNAL_COMMAND,  LOW(HSpinner_OnCmdRight),    HIGH(HSpinner_OnCmdRight)
  .db WIDGET_VALUE_CURRENTVALUE, WIDGET_SIGNAL_SETVALUE, LOW(HSpinner_OnSetValue),    HIGH(HSpinner_OnSetValue)
  .db WIDGET_VALUE_CURRENTVALUE, WIDGET_SIGNAL_GETVALUE, LOW(HSpinner_OnGetValue),    HIGH(HSpinner_OnGetValue)
  .db WIDGET_VALUE_MINVALUE,     WIDGET_SIGNAL_SETVALUE, LOW(HSpinner_OnSetMinValue), HIGH(HSpinner_OnSetMinValue)
  .db WIDGET_VALUE_MAXVALUE,     WIDGET_SIGNAL_SETVALUE, LOW(HSpinner_OnSetMaxValue), HIGH(HSpinner_OnSetMaxValue)
  .db 0, 0, 0, 0  ; end of table



#endif



