; ***************************************************************************
; 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_KEYPAD_ASM
#define AQH_AVR_GUI2_KEYPAD_ASM



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

.equ KEYPAD_OFFS_BEGIN       =  WIDGET_SIZE
.equ KEYPAD_OFFS_KEYWIDTH    =  KEYPAD_OFFS_BEGIN+0
.equ KEYPAD_OFFS_KEYHEIGHT   =  KEYPAD_OFFS_BEGIN+1
.equ KEYPAD_OFFS_KEYHDIST    =  KEYPAD_OFFS_BEGIN+2
.equ KEYPAD_OFFS_KEYVDIST    =  KEYPAD_OFFS_BEGIN+3
.equ KEYPAD_OFFS_TXTOFFS_X   =  KEYPAD_OFFS_BEGIN+4
.equ KEYPAD_OFFS_TXTOFFS_Y   =  KEYPAD_OFFS_BEGIN+5
.equ KEYPAD_OFFS_KEYSPERROW  =  KEYPAD_OFFS_BEGIN+6
.equ KEYPAD_OFFS_ROWS        =  KEYPAD_OFFS_BEGIN+7
.equ KEYPAD_OFFS_KEYS_LO     =  KEYPAD_OFFS_BEGIN+8
.equ KEYPAD_OFFS_KEYS_HI     =  KEYPAD_OFFS_BEGIN+9
.equ KEYPAD_OFFS_SELECTEDKEY =  KEYPAD_OFFS_BEGIN+10
.equ KEYPAD_SIZE             =  KEYPAD_OFFS_BEGIN+11





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

.cseg



; ---------------------------------------------------------------------------
; @routine KeyPad_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 keys per row
; @param  r21     number of rows
; @param  r23:r22 pointer to null-terminated string containing keys
; @clobbers any

KeyPad_new:
  push r20
    push r21
      push r22
        push r23
          ldi r24, LOW(KEYPAD_SIZE)
          ldi r25, HIGH(KEYPAD_SIZE)
          bigcall Object_Alloc           ; (!r16, !r17, !X)
        pop r23
      pop r22
    pop r21
  pop r20
  brcc KeyPad_new_ret
  rcall KeyPad_Init
  sec
KeyPad_new_ret:
  ret
; @end




; ---------------------------------------------------------------------------
; @routine KeyPad_Init @global
;
; @param  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 keys per row
; @param  r21     number of rows
; @param  r23:r22 pointer to null-terminated string containing keys
; @clobbers any

KeyPad_Init:
  push r20
    push r21
      push r22
        push r23
          ; call base class
          bigcall Widget_Init                       ; (r16, r17, X)
        pop r23
      pop r22
    pop r21
  pop r20
  
  std Y+KEYPAD_OFFS_KEYSPERROW, r20
  std Y+KEYPAD_OFFS_ROWS, r21
  std Y+KEYPAD_OFFS_KEYS_LO, r22
  std Y+KEYPAD_OFFS_KEYS_HI, r23
  
  ; 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(KeyPad_DefaultSignalmap*2)
  std Y+OBJECT_OFFS_SIGNALMAP_LO, r16
  ldi r16, HIGH(KeyPad_DefaultSignalmap*2)
  std Y+OBJECT_OFFS_SIGNALMAP_HI, r16

  ; set default style
  ldi r16, LOW(KeyPad_DefaultStyle*2)
  std Y+WIDGET_OFFS_STYLE_LO, r16
  ldi r16, HIGH(KeyPad_DefaultStyle*2)
  std Y+WIDGET_OFFS_STYLE_HI, r16
  
  ret
; @end





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


KeyPad_OnLayout:
  rcall keyPadLayoutX        ; (r16, r18, r19)
  rcall keyPadLayoutY        ; (r16, r18, r19)

  ldd r17, Y+OBJECT_OFFS_FLAGS
  ; only clear layout bit
  cbr r17, (1<<WIDGET_FLAGS_LAYOUT_BIT)
  std Y+OBJECT_OFFS_FLAGS, r17
  sec
  ret
; @end



KeyPad_OnGetDefaultWidth:
  rcall keyPadLayoutX                   ; (r16, r18, r19)

  ldd r18, Y+KEYPAD_OFFS_KEYHDIST
  ldd r19, Y+KEYPAD_OFFS_KEYSPERROW
  bigcall Utils_Mulu8x8_16            ; R17:R16 = R18 * R19 (r19-r21)
  mov r18, r16
  mov r19, r17

  rcall keyPadAdjustForBordersAndSpacings
  sec
  ret
; @end



KeyPad_OnGetDefaultHeight:
  rcall keyPadLayoutY                 ; (r16, r18, r19)
  ldd r18, Y+KEYPAD_OFFS_KEYVDIST
  ldd r19, Y+KEYPAD_OFFS_ROWS
  bigcall Utils_Mulu8x8_16            ; R17:R16 = R18 * R19 (r19-r21)
  mov r18, r16
  mov r19, r17

  rcall keyPadAdjustForBordersAndSpacings
  sec
  ret
; @end



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

KeyPad_OnDraw:
  ldd r17, Y+OBJECT_OFFS_FLAGS
  ; check whether widget is visible
  sbrs r17, WIDGET_FLAGS_VISIBLE_BIT
    rjmp KeyPad_OnDraw_ret
  ; check whether widget is dirty
  sbrs r17, WIDGET_FLAGS_DIRTY_BIT
    rjmp KeyPad_OnDraw_ret

  ; clear widget background
  bigcall Widget_Clear                    ; (any, !Y)

  ; draw keys
  rcall keyPadDrawKeys                  ; (any, !Y)

  ldd r17, Y+OBJECT_OFFS_OPTS
  sbrs r17, WIDGET_OPTS_BORDER_BIT
    rjmp KeyPad_OnDraw_done
  bigcall Widget_DrawBorder

KeyPad_OnDraw_done:
  ; clear dirty bit
  cbr r17, (1<<WIDGET_FLAGS_DIRTY_BIT)
  std Y+OBJECT_OFFS_FLAGS, r17

KeyPad_OnDraw_ret:
  sec
  ret
; @end




KeyPad_OnTimer:
KeyPad_OnTouchBegin:
KeyPad_OnTouchEnd:
  sec
  ret
; @end



; ---------------------------------------------------------------------------
; @routine keyPadAdjustForBordersAndSpacings
;
; @param  Y       widget
; @param r19:r18 size so far (width or height)
; @return r19:r18 adjusted size
; @clobbers r16, r17, r20, r21

keyPadAdjustForBordersAndSpacings:
  push zl
    push zh
      ldd zl, Y+WIDGET_OFFS_STYLE_LO
      ldd zh, Y+WIDGET_OFFS_STYLE_HI
      adiw zh:zl, WIDGET_STYLE_OFFS_SPACING
      lpm r16, Z
    pop zh
  pop zl
  clr r17
  sub r18, r16                          ; subtract one spacing
  sbc r19, r17
  bigcall Widget_AddOuterStyleBorders ; (r20, r21)
  ret
; @end




; ---------------------------------------------------------------------------
; @routine keyPadLayoutX
;
; @param  Y       widget
; @clobbers r16-r23, r25

keyPadLayoutX:
  bigcall Widget_GetCharWidth         ; r16=width (none)
  clr r19
  mov r18, r16
  lsl r18        ; x2
  rol r19
  add r18, r16   ; x3
  adc r19, r16
  sub r19, r16
  std Y+KEYPAD_OFFS_KEYWIDTH, r18
  std Y+KEYPAD_OFFS_TXTOFFS_X, r16

  push zl
    push zh
      ldd zl, Y+WIDGET_OFFS_STYLE_LO
      ldd zh, Y+WIDGET_OFFS_STYLE_HI
      adiw zh:zl, WIDGET_STYLE_OFFS_SPACING
      lpm r16, Z
    pop zh
  pop zl
  add r18, r16
  std Y+KEYPAD_OFFS_KEYHDIST, r18
  ret
; @end



; ---------------------------------------------------------------------------
; @routine keyPadLayoutY
;
; @param  Y       widget
; @clobbers r16, r18, r19

keyPadLayoutY:
  bigcall Widget_GetCharHeight        ; r16=height (none)
  clr r19
  mov r18, r16
  lsl r18        ; x2
  rol r19
  add r18, r16   ; x3
  adc r19, r16
  sub r19, r16
  std Y+KEYPAD_OFFS_KEYHEIGHT, r18
  std Y+KEYPAD_OFFS_TXTOFFS_Y, r16

  push zl
    push zh
      ldd zl, Y+WIDGET_OFFS_STYLE_LO
      ldd zh, Y+WIDGET_OFFS_STYLE_HI
      adiw zh:zl, WIDGET_STYLE_OFFS_SPACING
      lpm r16, Z
    pop zh
  pop zl
  add r18, r16
  std Y+KEYPAD_OFFS_KEYVDIST, r18
  ret
; @end



keyPadDrawKeys:
  ; get start pos
  ldd zl, Y+WIDGET_OFFS_STYLE_LO
  ldd zh, Y+WIDGET_OFFS_STYLE_HI
  adiw zh:zl, WIDGET_STYLE_OFFS_OUTERBORDERSIZE
  lpm r20, Z
  clr r21
  mov r4, r20  ; X
  clr r5
  mov r6, r20  ; Y
  clr r7

  ; get key contents
  ldd zl, Y+KEYPAD_OFFS_KEYS_LO
  ldd zh, Y+KEYPAD_OFFS_KEYS_HI
  mov r16, zl
  or r16, zh
  breq keyPadDrawKeys_ret
  ldd r16, Y+KEYPAD_OFFS_ROWS
  tst r16
  breq keyPadDrawKeys_ret
  ldd r16, Y+KEYPAD_OFFS_KEYSPERROW
  tst r16
  breq keyPadDrawKeys_ret

  ; draw keys
  ldd r20, Y+KEYPAD_OFFS_ROWS             ; row counter
  clr r21                                 ; key idx
keyPadDrawKeys_loop1:                     ; loop for every row
    push r20
      ldd r20, Y+KEYPAD_OFFS_KEYSPERROW   ; key counter per row
      push r4                             ; X
        push r5
keyPadDrawKeys_loop2:                     ; loop for every key in a row
            lpm r16, Z
            tst r16
            breq keyPadDrawKeys_loop2End  ; end reached
            cpi r16, 0xff
            breq keyPadDrawKeys_loop2Next ; no key, ignore
            push r4                       ; X
              push r5
                push r6                   ; Y
                  push r7
                    push r20              ; key counter per row
                      push r21            ; key idx
                        rcall keyPadDrawKey
                      pop r21
                    pop r20
                  pop r7
                pop r6
              pop r5
            pop r4
keyPadDrawKeys_loop2Next:
            ldd r16, Y+KEYPAD_OFFS_KEYHDIST ; increment X
            add r4, r16
            adc r5, r16
            sub r5, r16
            adiw zh:zl, 1
            inc r21
            dec r20
            breq keyPadDrawKeys_loop2End
          rjmp keyPadDrawKeys_loop2
keyPadDrawKeys_loop2End:
        pop r5
      pop r4  
    pop r20
    dec r20
    breq keyPadDrawKeys_loop1End
    ldd r16, Y+KEYPAD_OFFS_KEYVDIST ; increment Y
    add r6, r16
    adc r7, r16
    sub r7, r16
  rjmp keyPadDrawKeys_loop1
keyPadDrawKeys_loop1End:

keyPadDrawKeys_ret:
  ret
; @end


; ---------------------------------------------------------------------------
; @routine keyPadDrawKey
;
; @param  Y     widget
; @param r5:r4 X
; @param r7:r6 Y
; @param r21 key idx
; @param Z pointer to current key byte

keyPadDrawKey:
  ; get colors
  mov r16, r21
  rcall keyPadGetColsForKey ; (r17)
  push r4
    push r5
      push r6
        push r7
          push r21
            push zl
              push zh
                ldd r8, Y+KEYPAD_OFFS_KEYWIDTH
                clr r9
                ldd r10, Y+KEYPAD_OFFS_KEYHEIGHT
                clr r11
                mov r2, r0
                mov r3, r1
                bigcall Widget_FillRect
              pop zh
            pop zl
          pop r21
        pop r7
      pop r6
    pop r5
  pop r4

  ; get colors again
  mov r16, r21
  rcall keyPadGetColsForKey ; (r17)

  push r4
    push r5
      push r6
        push r7
          push r21
            push zl
              push zh
                ldd r8, Y+KEYPAD_OFFS_KEYWIDTH
                clr r9
                ldd r10, Y+KEYPAD_OFFS_KEYHEIGHT
                clr r11
                bigcall Widget_DrawRect
              pop zh
            pop zl
          pop r21
        pop r7
      pop r6
    pop r5
  pop r4

  ; get colors again
  mov r16, r21
  rcall keyPadGetColsForKey ; (r17)

  ; adjust XPos
  clr r17
  ldd r16, Y+KEYPAD_OFFS_TXTOFFS_X
  add r4, r16
  adc r5, r17
  ; adjust YPos
  ldd r16, Y+KEYPAD_OFFS_TXTOFFS_Y
  add r6, r16
  adc r7, r17
  lpm r16, Z
  push zl
    push zh
      bigcall Widget_DrawCharAt
    pop zh
  pop zl
  ret
; @end



; ---------------------------------------------------------------------------
; @routine keyPadGetColsForKey
;
; @param  Y     widget
; @param  r16   key idx (starting at 0)
; @return r1:r0 background color
; @return r3:r2 foreground color
; @clobbers r17

keyPadGetColsForKey:
  ldd r17, Y+KEYPAD_OFFS_SELECTEDKEY
  cp r16, r17
  breq keyPadGetColsForKey_activated
  bigcall Widget_GetNormalColors     ; (none)
  rjmp keyPadGetColsForKey_ret
keyPadGetColsForKey_activated:
  bigcall Widget_GetActivatedColors  ; (none)
keyPadGetColsForKey_ret:
  ret
; @end



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


KeyPad_DefaultStyle:
  .dw DISPLAY_COLOR_BLACK        ; frontCol_norm
  .dw DISPLAY_COLOR_LIGHTGREY    ; 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 4, 8                       ; outerBorderSize, innerBorderSize
  .dw ili9341Font12x16_1*2       ; font
  .db 12, 16                     ; charWidth, charHeight
; @end



KeyPad_DefaultSignalmap:
  ; header
  .dw Widget_DefaultSignalmap*2  ; next table to use
  ; entries
  .db 0,                           OBJECT_SIGNAL_TIMER,       LOW(KeyPad_OnTimer),            HIGH(KeyPad_OnTimer)
  .db 0,                           WIDGET_SIGNAL_DRAW,        LOW(KeyPad_OnDraw),             HIGH(KeyPad_OnDraw)
  .db 0,                           WIDGET_SIGNAL_TOUCH_BEGIN, LOW(KeyPad_OnTouchBegin),       HIGH(KeyPad_OnTouchBegin)
  .db 0,                           WIDGET_SIGNAL_TOUCH_END,   LOW(KeyPad_OnTouchEnd),         HIGH(KeyPad_OnTouchEnd)
  .db 0,                           WIDGET_SIGNAL_LAYOUT,      LOW(KeyPad_OnLayout),           HIGH(KeyPad_OnLayout)
  .db WIDGET_VALUE_DEFAULT_WIDTH,  WIDGET_SIGNAL_GETVALUE,    LOW(KeyPad_OnGetDefaultWidth),  HIGH(KeyPad_OnGetDefaultWidth)
  .db WIDGET_VALUE_DEFAULT_HEIGHT, WIDGET_SIGNAL_GETVALUE,    LOW(KeyPad_OnGetDefaultHeight), HIGH(KeyPad_OnGetDefaultHeight)

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






#endif

