cNamesInitialSize = 65536

uglobal
  ptrLabels dd ?        ; ptr to Root items TArray structure.
  ptrNames  dd ?        ; pointer to memory buffer with name strings

  ptrPreprocessed dd ?  ; pointer to memory with preprocessed source.
  lenPreprocessed dd ?  ; length of the preprocessed source.

  ptrGlobalsList  dd ?  ; index of the global labels organized by files and line numbers.
endg


proc WaitForCompiler
.count dd ?
begin
        cmp     [comp_handle], 0
        je      .finish

        invoke  LoadCursorW, 0, IDC_WAIT
        invoke  SetCursor, eax
        push    eax

        mov     [.count], 500   ; 500 times x 20ms = 10s

.wait:
        cmp     [comp_handle], 0
        je      .exit_ok

        invoke  WaitForSingleObject, [comp_handle], 20
        cmp     eax, WAIT_OBJECT_0
        je      .exit_ok
        cmp     eax, WAIT_FAILED
        je      .exit_ok

        stdcall ProcessMessages
        dec     [.count]
        jnz     .wait

        invoke  SetCursor ; from the stack
        stc
        return

.exit_ok:
        invoke  SetCursor ; from the stack
.finish:
        clc
        return
endp


proc FreePreprocessed
begin
        cmp     [ptrPreprocessed], 0
        je      .exit

        stdcall FreeMem, [ptrPreprocessed]
        mov     [ptrPreprocessed], 0
.exit:
        return
endp


proc CopyPreprocessedSource, .ptrFAS
begin
        pushad

        stdcall FreePreprocessed

        mov     ebx, [.ptrFAS]
        test    ebx, ebx
        jz      .exiterror

        mov     esi, [ebx+TDumpHeader.ofsPreprocessed]
        test    esi, esi
        jz      .exiterror

        add     esi, ebx
        stdcall GetMem, [ebx+TDumpHeader.lenPreprocessed]
        mov     edi, eax
        mov     [ptrPreprocessed], eax

        test    eax, eax
        jz      .exiterror

        cld

        mov     ecx, [ebx+TDumpHeader.lenPreprocessed]
        mov     [lenPreprocessed], ecx

        shr     ecx, 2
        rep movsd
        mov     ecx, [ebx+TDumpHeader.lenPreprocessed]
        and     ecx, 3
        rep movsb

.exitok:
        clc
        popad
        return

.exiterror:
        stc
        popad
        return
endp



proc CreateLabelsTree, .ptrFAS
.symend dd ?
.strend dd ?
.buffer rb 1024
begin
        pushad

        stdcall ClearLabelsTree, [ptrLabels]

; Allocate memory for label names.

        cmp     [ptrNames], 0
        jne     .names_allocated

        stdcall CreateMemoryStream, ptrNames, cNamesInitialSize

.names_allocated:

;; debug +
;        mov     eax, [ptrNames]
;        stdcall Output, 'Names capacity: '
;        stdcall OutputNumber, [eax+TMemoryStream.size], 10, -1
;        stdcall Output, cCRLF
;
;        mov     eax, [ptrLabels]
;        test    eax, eax
;        jz      @f
;
;        stdcall Output, 'Labels root capacity: '
;        stdcall OutputNumber, [eax+TArray.capacity], 10, -1
;        stdcall Output, cCRLF
;
;@@:
;; debug -

        mov     esi, [.ptrFAS]
        cmp     esi, 0
        je      .endofsymbols   ; there is no FAS file loaded.

        mov     edi, [esi+TDumpHeader.ofsSymbolTable]
        cmp     edi, 0
        je      .endofsymbols   ; there is no symbols table in the fas file.

        mov     ebx, [esi+TDumpHeader.lenSymbolTable]

;; debug +
;        mov     eax, ebx
;        shr     eax, 5
;        stdcall Output, 'Labels count: '
;        stdcall OutputNumber, eax, 10, -1
;        stdcall Output, cCRLF
;; debug -

        add     edi, esi    ; pointer to the symbol table
        add     ebx, edi    ; pointer to the end of symbol table

.symbol_loop:
        cmp     edi, ebx
        jae     .endofsymbols

; get name

        mov     edx, [edi+TIntLabel.ofsName]
        test    edx, edx
        js      .instrings

; in preprocessed source; pascal string copy

        add     edx, [esi+TDumpHeader.ofsPreprocessed]
        add     edx, esi

        movzx   ecx, byte [edx]
        mov     [.buffer+ecx], 0

.copypas:
        dec     ecx
        js      .nameok

        mov     al, [edx+ecx+1]
        cmp     al, '?'
        je      .nextlabel              ; this is internal generated label.
        mov     [.buffer+ecx], al
        jmp     .copypas


; in strings table; asciiz string;

.instrings:
        and     edx, $7fffffff
        add     edx, [esi+TDumpHeader.ofsStringTable]
        add     edx, esi
        xor     ecx, ecx

.copy:
        mov     al, byte [edx+ecx]
        mov     [.buffer+ecx], al
        inc     ecx

        cmp     al, '?'
        je      .nextlabel

        cmp     al, 0
        jne     .copy

.nameok:
        lea     eax, [.buffer]
        stdcall AddToTree, eax, ptrLabels       ; returns pointer to TLabel structure

        mov     ecx, eax

        cmp     [ecx+TLabel.pXRefArray], 0
        jne     .xref_array_allocated

        stdcall CreateArray, sizeof.TXRefInfo
        mov     [ecx+TLabel.pXRefArray], eax

.xref_array_allocated:

        mov     dx, [edi+TIntLabel.Flags]
        mov     [ecx+TLabel.flags], dx

        mov     dl, [edi+TIntLabel.DataSize]
        mov     [ecx+TLabel.DataSize], dl
        mov     dl, [edi+TIntLabel.type]
        mov     [ecx+TLabel.type], dl

        mov     edx, [edi+TIntLabel.SIBEx]
        mov     [ecx+TLabel.SIBEx], edx

        mov     edx, dword [edi+TIntLabel.Value]
        mov     [ecx+TLabel.ValueLo], edx

        mov     edx, dword [edi+TIntLabel.Value+4]
        mov     [ecx+TLabel.ValueHi], edx

        stdcall AddArrayItems, [ecx+TLabel.pXRefArray], 1
        mov     [ecx+TLabel.pXRefArray], edx

        mov     edx, [edi+TIntLabel.ofsDefinition]
        mov     [eax+TXRefInfo.ofsSource], edx
        mov     [eax+TXRefInfo.flags], fxrefDefinition

        mov     eax, edi
        sub     eax, esi
        sub     eax, [esi+TDumpHeader.ofsSymbolTable]
        stdcall AppendXrefList, [.ptrFAS], ecx, eax

.nextlabel:
        add     edi, sizeof.TIntLabel
        jmp     .symbol_loop

.endofsymbols:
        popad
        return
endp




proc AppendXrefList, .ptrFAS, .ptrLabel, .ofsSymbol
.end dd ?
begin
        pushad

        mov     esi, [.ptrFAS]
        test    esi, esi
        jz      .finish

        mov     edi, [.ptrLabel]

        mov     ecx, [esi+TDumpHeader.lenXRef]
        add     esi, [esi+TDumpHeader.ofsXRef]
        mov     [.end], esi
        add     [.end], ecx

        shr     ecx, 3  ; count

; search the start

        jecxz   .finish

        dec     ecx        ; end
        xor     ebx, ebx   ; start

.loop:                          ; binary search loop
        cmp     ebx, ecx
        jg      .finish

        mov     edx, ebx
        add     edx, ecx
        shr     edx, 1           ; middle of the array...

        mov     eax, [.ofsSymbol]
        cmp     eax, [esi+sizeof.TIntXRef*edx+TIntXRef.ofsSymbol]
        jb      .less
        ja      .more

        test    edx, edx
        jz      .found

        cmp     eax, [esi+sizeof.TIntXRef*edx+TIntXRef.ofsSymbol - sizeof.TIntXRef]     ; previous
        ja      .found

.less:
        mov     ecx, edx
        dec     ecx
        jmp     .loop

.more:
        mov     ebx, edx
        inc     ebx
        jmp     .loop


.found:
        lea     esi, [esi+sizeof.TIntXRef*edx]

.add_loop:
        mov     eax, [esi+TIntXRef.ofsSymbol]
        cmp     eax, [.ofsSymbol]
        jne     .finish                                 ; the array is sorted now.

; add the source line to the xref array
        stdcall AddArrayItems, [edi+TLabel.pXRefArray], 1
        mov     [edi+TLabel.pXRefArray], edx
        mov     ebx, eax

        mov     eax, [esi+TIntXRef.ofsAssembly]
        mov     edx, [.ptrFAS]
        add     eax, [edx+TDumpHeader.ofsAsmDump]
        add     eax, edx

        mov     eax, [eax+TAsmDumpElement.ofsPreprocessed]
        mov     [ebx+TXRefInfo.ofsSource], eax
        mov     [ebx+TXRefInfo.flags], 0

        add     esi, sizeof.TIntXRef
        cmp     esi, [.end]
        jb      .add_loop

.finish:
        popad
        return
endp




proc AddToNames, .ptrString
begin
        push    esi edi ebx

        stdcall StrLen, [.ptrString]
        lea     eax, [eax+5]
        and     al, $fc
        stdcall GetStreamRoom, ptrNames, eax

        mov     esi, [.ptrString]

        push    edi
        lea     edi, [ebx+edi]

.copyloop:
        lodsb
        stosb
        test    al, al
        jnz     .copyloop

        pop     eax
        pop     ebx edi esi
        return
endp



;---------------------------------------------------------------
; Returns:
;    eax - The name of the label without the first parent name.
;    edx - First parent.
;---------------------------------------------------------------
proc GetLabelParent, .hLabel
begin
        push    esi ebx

        stdcall StrPtr, [.hLabel]
        mov     esi, eax
        push    esi

        cmp     word [esi], '..'        ; Begins with ".."
        jne     .search

        add     esi, 2

.search:
        mov     al, [esi]
        test    al,al
        jz      .notfound
        cmp     al, '.'
        je      .found

        cmp     al, ':' ; for macro separators (for example in the OOP macros)
        je      .found

        inc     esi
        jmp     .search

.found:
        mov     byte [esi], 0

        lea     eax, [esi+1]
        pop     edx

        pop     ebx esi
        return

.notfound:
        pop     eax
        xor     edx, edx

        pop     ebx esi
        return
endp




proc SearchCreateLabel, .ptrVarArray, .ptrLabel
.hash dd ?
begin
        push    esi edi ebx edx


; compute FNV1b hash

        mov     esi, [.ptrLabel]
        mov     ecx, $811C9DC5                  ; 2166136261              ; FNV offset basis
        xor     eax, eax

.hashloop:
        lodsb
        test    eax, eax
        jz      .end_hash

        xor     ecx, eax
        imul    ecx, $01000193    ;              ;   16777619              ; FNV prime
        jmp     .hashloop

.end_hash:
        mov     [.hash], ecx

        mov     edx, [.ptrVarArray]
        mov     esi, [edx]
        test    esi, esi
        jnz     .arrayok

        stdcall CreateArray, sizeof.TLabel
        mov     edx, [.ptrVarArray]
        mov     [edx], eax
        mov     esi, eax        ; pointer to TArray structure

        stdcall CreateArray, 8               ; Array for search tree.
        stdcall AddArrayItems, eax, 1        ; First empty element of the tree.
        mov     [esi+TArray.lparam], edx
        mov     dword [eax], 0
        mov     dword [eax+4], 0

.arrayok:
        mov     edi, [esi+TArray.lparam]         ; the parent array.
        mov     ecx, 32                          ; the depth of the tree
        mov     ebx, TArray.array

.treeseek:
        xor     eax, eax
        ror     [.hash], 1
        adc     eax, 0
        lea     edx, [ebx+4*eax]

        mov     edx, [edi + edx]        ; edx is offset from the begining of the array.
        test    edx, edx
        jz      .notfound

        mov     ebx, edx
        loop    .treeseek

.found:                         ; the offset of the item is in the ebx register
        lea     eax, [edx+4*eax]  ; the last bit is in eax
        mov     eax, [edi+eax]
        lea     eax, [esi+eax]
        pop     edx ebx edi esi
        return


.addloop:
        xor     eax, eax
        ror     [.hash], 1
        adc     eax, 0

.notfound:      ; edi - pointer to the tree array,  ebx - offset of the last found element.
                ; eax - 0 or 1 depending of the last bit checked. ecx - remaining bits count.
                ; edx = 0

        push    ecx eax

        stdcall AddArrayItems, [esi+TArray.lparam], 1
        mov     [esi+TArray.lparam], edx
        mov     dword [eax], 0
        mov     dword [eax+4], 0

        mov     edi, edx                ; new base for the array.
        sub     eax, edx                ; offset of the new element

        xchg    eax, [esp]              ; this is the old eax
        lea     edx, [ebx+ 4*eax]

        pop     ebx
        mov     [edi+edx], ebx          ; the new offset is properly set.

        pop     ecx
        loop    .addloop

        push    eax
        mov     edx, [edi+edx]
        lea     ebx, [edi+edx]  ; address of the last element.

        mov     ecx, [.ptrVarArray]
        stdcall AddArrayItems, [ecx], 1
        mov     [ecx], edx

        mov     esi, eax
        sub     eax, edx        ; offset of the TLabel item.

        pop     ecx
        mov     [ebx+4*ecx], eax  ; stored.

        xor     eax, eax
;        mov     [esi+TLabel.Children], eax     ; don't clear this!
        mov     [esi+TLabel.type], al
        mov     [esi+TLabel.flags], ax
        mov     [esi+TLabel.ValueLo], eax
        mov     [esi+TLabel.ValueHi], eax
;        mov     [esi+TLabel.ptrIntLabel], eax

; This should be the only place where the .iName and .hashName of TLabel is set.
        stdcall AddToNames, [.ptrLabel]
        mov     [esi+TLabel.iName], eax
        push    [.hash]
        pop     [esi+TLabel.hashName]

        mov     eax, esi
        pop     edx ebx edi esi
        return

endp

;------------------------------------------------------
; Returns: eax - pointer to new created TLabel
;          [hLabelName] content of the string will be
;          destroyed.
;------------------------------------------------------
proc AddToTree, .ptrLabelName, .ptrVarTree
begin
        push    esi edi ebx ecx

        mov     edi, [.ptrVarTree]
        mov     ebx, [.ptrLabelName]

;        int3
.addloop:
        stdcall GetLabelParent, ebx
        test    edx, edx
        jz      .addhere

        mov     ebx, eax
        stdcall SearchCreateLabel, edi, edx

        lea     edi, [eax+TLabel.Children]
        jmp     .addloop

.addhere:
        stdcall SearchCreateLabel, edi, eax

        pop     ecx ebx edi esi
        return
endp




proc SearchLabel, .ptrVarArray, .hLabel
.hash dd ?
begin
        push    esi edi ebx edx

        mov     edx, [.ptrVarArray]
        mov     esi, [edx]
        test    esi, esi
        jz      .notfound

        stdcall StrHash, [.hLabel]
        mov     [.hash], eax

        mov     edi, [esi+TArray.lparam]
        mov     ecx, 32         ; the depth of the tree
        mov     ebx, TArray.array

.treeseek:
        xor     eax, eax
        ror     [.hash], 1
        adc     eax, 0
        lea     edx, [ebx+4*eax]

        mov     edx, [edi + edx]        ; eax is offset from the begining of the array.
        test    edx, edx
        jz      .notfound

        mov     ebx, edx
        loop    .treeseek

.found:                         ; the offset of the item is in the ebx register
        lea     eax, [edx+4*eax]  ; the last bit of the hash in eax
        mov     eax, [edi+eax]
        lea     eax, [esi + eax]

        pop     edx ebx edi esi
        return

.notfound:
        xor     eax, eax
        pop     edx ebx edi esi
        return
endp



proc SearchTree, .hLabelName, .ptrVarTree
begin
        push    edi ebx ecx

        mov     edi, [.ptrVarTree]
        mov     ebx, [.hLabelName]

.searchloop:
        stdcall GetLabelParent, ebx
        test    edx, edx
        jz      .here

        mov     ebx, eax

        stdcall SearchLabel, edi, edx
        test    eax, eax
        jz      .finish

        lea     edi, [eax+TLabel.Children]
        jmp     .searchloop

.here:
        mov     edx, eax
        mov     eax, edi

.finish:
        pop     ecx ebx edi
        return
endp

; returns pointer to TLabel or NULL
; (!) Destroys the string on .ptrLabelName
proc SearchTreeExact, .ptrLabelName, .ptrVarTree
begin
        push    edi ebx ecx

        mov     edi, [.ptrVarTree]
        mov     ebx, [.ptrLabelName]

.searchloop:
        stdcall GetLabelParent, ebx
        test    edx, edx
        jz      .here

        mov     ebx, eax
        stdcall SearchLabel, edi, edx
        test    eax, eax
        jz      .finish

        lea     edi, [eax+TLabel.Children]
        jmp     .searchloop

.here:
        stdcall SearchLabel, edi, eax

.finish:
        pop     ecx ebx edi
        return
endp




proc ClearLabelsTree, .ptrArray
begin
        push    esi ebx

        cmp     [hLabelExplorer], 0
        je      .oldwin_ok

        invoke  SendMessageW, [hLabelExplorer], WM_CLOSE, 0, 0
        mov     [hLabelExplorer], 0

.oldwin_ok:
        cmp     [hLabelInfo], 0
        je      .oldwin_ok2

        invoke  SendMessageW, [hLabelInfo], WM_CLOSE, 0, 0
        mov     [hLabelInfo], 0

.oldwin_ok2:

        stdcall __DoClearLabelsTree, [.ptrArray]

        cmp     [ptrNames], 0
        je      .finish

        mov     eax, [ptrNames]
        mov     [eax+TMemoryStream.offset], TMemoryStream.data

.finish:
        pop     ebx esi
        return
endp




proc __DoClearLabelsTree, .ptrArray
begin
        push    esi ebx

        mov     esi, [.ptrArray]
        test    esi, esi
        jz      .finish

        mov     ebx, [esi+TArray.count]
        lea     esi, [esi+TArray.array]

.clearlist:
        dec     ebx
        js      .endoflist

        cmp     dword [esi+TLabel.pXRefArray], 0
        je      .xrefok

        stdcall ResetArray, [esi+TLabel.pXRefArray]

.xrefok:
        cmp     dword [esi+TLabel.Children], 0
        je      .next
        stdcall __DoClearLabelsTree, [esi+TLabel.Children]      ; for .Children field of the TLabel
.next:
        add     esi, sizeof.TLabel
        jmp     .clearlist

.endoflist:
        mov     ebx, [.ptrArray]

        stdcall ResetArray, [ebx+TArray.lparam]  ; It is the binary search tree.
        stdcall AddArrayItems, [ebx+TArray.lparam], 1
        mov     [ebx+TArray.lparam], edx
        mov     dword [eax], 0
        mov     dword [eax+4], 0

; sometimes crashes here...
        stdcall ResetArray, ebx

.finish:
        pop     ebx esi
        return
endp



proc ResetArray, .pArray
begin
        push    eax
        mov     eax, [.pArray]
        and     [eax+TArray.count], 0
        pop     eax
        return
endp






proc FreeLabelsTree, .ptrArray
begin
        push    esi ebx

        cmp     [hLabelExplorer], 0
        je      .oldwin_ok

        invoke  SendMessageW, [hLabelExplorer], WM_CLOSE, 0, 0
        mov     [hLabelExplorer], 0

.oldwin_ok:
        cmp     [hLabelInfo], 0
        je      .oldwin_ok2

        invoke  SendMessageW, [hLabelInfo], WM_CLOSE, 0, 0
        mov     [hLabelInfo], 0

.oldwin_ok2:

        stdcall __DoFreeLabelsTree, [.ptrArray]

        cmp     [ptrNames], 0
        je      .quit

        stdcall FreeMem, [ptrNames]
        mov     [ptrNames], 0

.quit:
        pop     ebx esi
        return
endp




proc __DoFreeLabelsTree, .ptrArray
begin
        push    esi ebx

        mov     esi, [.ptrArray]
        test    esi, esi
        jz      .finish

        mov     ebx, [esi+TArray.count]
        lea     esi, [esi+TArray.array]

.clearlist:
        dec     ebx
        js      .endoflist

        cmp     dword [esi+TLabel.pXRefArray], 0
        je      .xrefok

        stdcall FreeMem, [esi+TLabel.pXRefArray]

.xrefok:
        cmp     dword [esi+TLabel.Children], 0
        je      .next
        stdcall __DoFreeLabelsTree, [esi+TLabel.Children]      ; for .Children field of the TLabel
.next:
        add     esi, sizeof.TLabel
        jmp     .clearlist

.endoflist:
        mov     ebx, [.ptrArray]
        stdcall FreeMem, [ebx+TArray.lparam]  ; It is the binary search tree.
        stdcall FreeMem, ebx

.finish:
        pop     ebx esi
        return
endp

















; Doesn't work properly!
; to be fixed.
;********************************
proc ShowNotUsed
.frm TFreshMsg
begin
        push    esi edi ebx

        mov     esi, [ptrLabels]
        test    esi, esi
        jz      .finish

        mov     ebx, [esi+TArray.count]
        test    ebx, ebx
        jz      .finish

        lea     esi, [esi+TArray.array]

.valuesloop:
        test    [esi+TLabel.flags], lfCanRedefine
        jnz     .next

        test    [esi+TLabel.flags], lfDefined
        jz      .next

        test    [esi+TLabel.flags], lfUsed
        jnz     .next

        cmp     [esi+TLabel.iName], 0
        je      .next

        stdcall StrNew
        push    eax

        stdcall StrCopy, eax, cHintPrefix
        mov     ecx, [esi+TLabel.iName]
        add     ecx, [ptrNames]
        stdcall StrCat, eax, ecx
        stdcall StrCat, eax, cHintSuffix

        mov     [.frm.type], mtInfo
        mov     [.frm.link.hFileName], 0
        mov     [.frm.hMessage], eax

        lea     eax, [.frm]
        invoke  SendMessageW, [frmMsg], MWM_ADDMESSAGE, eax, 0
        stdcall StrDel ; from the stack.

.next:
        add     esi, sizeof.TLabel
        dec     ebx
        jnz     .valuesloop

.finish:
        pop     ebx edi esi
        return
endp

iglobal
  cHintPrefix db 'The label "',0
  cHintSuffix db '" is defined but not used.',0
endg



proc SortCrossReference, .ptrFAS
begin
        pushad

        mov     esi, [.ptrFAS]
        mov     eax, [esi+TDumpHeader.lenXRef]
        add     esi, [esi+TDumpHeader.ofsXRef]

        mov     edi, CompareXRef
        mov     ebx, sizeof.TIntXRef

        shr     eax, 3          ; 8 bytes per TIntXRef element
        jz      .exit
        dec     eax             ; the index of the last element.
        xor     ecx, ecx

        stdcall __DoQSort, ecx, eax

; debug - check the array...
;
;        mov     esi, [.ptrFAS]
;        mov     ecx, [esi+TDumpHeader.lenXRef]
;        add     esi, [esi+TDumpHeader.ofsXRef]
;        shr     ecx, 3
;
;        jecxz   .exit
;        jmp     .check
;
;.loop:
;        cmp     eax, [esi+TIntXRef.ofsSymbol]
;        ja      .error
;
;.check:
;        mov     eax, [esi+TIntXRef.ofsSymbol]
;        add     esi, sizeof.TIntXRef
;        dec     ecx
;        jnz     .loop
;
.exit:
        popad
        return

;.error:
;        int3
;        jmp     .exit
endp


proc CompareXRef, .ptrElement1, .ptrElement2
begin
        push    eax ecx

        mov     eax, [.ptrElement1]
        mov     ecx, [.ptrElement2]

        mov     eax, [eax+TIntXRef.ofsSymbol]
        cmp     eax, [ecx+TIntXRef.ofsSymbol]
        jae     .not

        clc
        pop     ecx eax
        return

.not:
        stc
        pop     ecx eax
        return
endp