; ***************************************************************************
; copyright   : (C) 2025 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_COMMON_TREE_H
#define AQH_AVR_COMMON_TREE_H




; ***************************************************************************
; defs

.equ TREE_OFFS_LIST        = 0
.equ TREE_OFFS_PARENT_LO   = TREE_OFFS_LIST+LIST_SIZE
.equ TREE_OFFS_PARENT_HI   = TREE_OFFS_LIST+LIST_SIZE+1
.equ TREE_OFFS_CHILD_LO    = TREE_OFFS_LIST+LIST_SIZE+2
.equ TREE_OFFS_CHILD_HI    = TREE_OFFS_LIST+LIST_SIZE+3
.equ TREE_SIZE             = TREE_OFFS_LIST+LIST_SIZE+4




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

.cseg



; ---------------------------------------------------------------------------
; @routine Tree_InitObject @global

; @param Y      pointer to object to add
; @clobbers r16

Tree_InitObject:
  rcall List_InitObject                          ; (R16)
  clr r16                                        ; clear this->TREE data
  std Y+TREE_OFFS_LIST+TREE_OFFS_PARENT_LO, r16
  std Y+TREE_OFFS_LIST+TREE_OFFS_PARENT_HI, r16
  std Y+TREE_OFFS_LIST+TREE_OFFS_CHILD_LO, r16
  std Y+TREE_OFFS_LIST+TREE_OFFS_CHILD_HI, r16
  ret
; @end



; ---------------------------------------------------------------------------
; @routine Tree_GetParentObject @global

; @param  Y pointer to object
; @return X pointer to parent object
; @clobbers none

Tree_GetParentObject:
  ldd xl, Y+TREE_OFFS_PARENT_LO
  ldd xh, Y+TREE_OFFS_PARENT_HI
  ret
; @end



; ---------------------------------------------------------------------------
; @routine Tree_GetFirstChildObject @global

; @param  Y pointer to object
; @return X pointer to first child object
; @clobbers none

Tree_GetFirstChildObject:
  ldd xl, Y+TREE_OFFS_CHILD_LO
  ldd xh, Y+TREE_OFFS_CHILD_HI
  ret
; @end



; ---------------------------------------------------------------------------
; @routine Tree_GetLastChildObject @global

; @param  Y pointer to object
; @return X pointer to last child object
; @clobbers none

Tree_GetLastChildObject:
  ldd xl, Y+TREE_OFFS_CHILD_LO
  ldd xh, Y+TREE_OFFS_CHILD_HI
  mov r16, xl
  or r16, xh
  breq Tree_GetLastChildObject_ret
  rcall List_GetLastObject
Tree_GetLastChildObject_ret:
  ret
; @end



; ---------------------------------------------------------------------------
; @routine Tree_GetNextSibling @global

; @param  Y pointer to object
; @return X pointer to successor object
; @clobbers none

Tree_GetNextSibling:
  rjmp List_GetNextObject
; @end



; ---------------------------------------------------------------------------
; @routine Tree_GetObjectBelow @global

; @param  Y pointer to object
; @return X pointer to object below (or NULL)
; @clobbers r16

Tree_GetObjectBelow:
  push yl
    push yh
      rcall treeGetObjectBelow
    pop yh
  pop yl
  ret

treeGetObjectBelow:
    rcall Tree_GetFirstChildObject
    mov r16, xl
    or r16, xh
    brne treeGetObjectBelow_ret    ; got one
    rcall Tree_GetNextSibling
    mov r16, xl
    or r16, xh
    brne treeGetObjectBelow_ret    ; got one
    rcall Tree_GetParentObject
    mov r16, xl
    or r16, xh
    breq treeGetObjectBelow_ret    ; no parent
    mov yl, xl
    mov yh, xh
  rjmp treeGetObjectBelow          ; try with parent
treeGetObjectBelow_ret:
  ret
; @end




; ---------------------------------------------------------------------------
; @routine Tree_AddChildObject @global

; @param Y      pointer to object to add
; @param X      pointer to parent to add to
; @clobbers r16, r17, r18, x

Tree_AddChildObject:
  std Y+TREE_OFFS_PARENT_LO, xl            ; immediately store parent pointer
  std Y+TREE_OFFS_PARENT_HI, xh
  adiw xh:xl, TREE_OFFS_CHILD_LO           ; read pointer to first child
  ld r16, X+
  ld r17, X
  mov r18, r16
  or r18, r17
  brne Tree_AddChildObject_addToChildList
  st X, yh                                 ; no child, set THIS as first
  st -X, yl
  sbiw xh:xl, TREE_OFFS_CHILD_LO
  ret
Tree_AddChildObject_addToChildList:
  mov xl, r16                              ; X=first child
  mov xh, r17
  rjmp List_AddObject                      ; add Y as new object
; @end



; ---------------------------------------------------------------------------
; @routine Tree_UnlinkObject @global

; @param Y      pointer to object to remove
; @clobbers r16, r17, x

Tree_UnlinkObject:
  ldd xl, Y+TREE_OFFS_PARENT_LO
  ldd xh, Y+TREE_OFFS_PARENT_HI
  mov r16, xl
  or r16, xh
  breq Tree_UnlinkObject_ret                     ; not part of a tree
  adiw xh:xl, TREE_OFFS_CHILD_LO                 ; get parent's first child to R17:R16
  ld r16, X+
  ld r17, X
  cp r16, yl                                     ; same as THIS?
  brne Tree_UnlinkObject_inList                  ; nope, need to check childList
  cp r17, yh                                     ; same as THIS?
  brne Tree_UnlinkObject_inList                  ; nope, need to check childList
  ldd r16, Y+TREE_OFFS_LIST+LIST_OFFS_NEXT_HI    ; is first child, set this->NEXT as new first child
  st X, r16
  ldd r16, Y+TREE_OFFS_LIST+LIST_OFFS_NEXT_LO
  st -X, r16
  rjmp Tree_UnlinkObject_clrParentAndSibling
Tree_UnlinkObject_inList:
  mov xl, r16
  mov xh, r17
  rcall List_UnlinkObject                        ; (R16, R17, X)
Tree_UnlinkObject_clrParentAndSibling:
  clr r16                                        ; clear this->PARENT
  std Y+TREE_OFFS_PARENT_LO, r16
  std Y+TREE_OFFS_PARENT_HI, r16
  std Y+LIST_OFFS_NEXT_LO, r16
  std Y+LIST_OFFS_NEXT_HI, r16                   ; clear this->NEXT
Tree_UnlinkObject_ret:
  ret
; @end





#endif ; AQH_AVR_COMMON_TREE_H

