;**********************************************************************
; Returns string with source code for given window Template and all its
; children.
; Arguments:
;       ptrWinTemplateDT - pointer to TWinTemplateDT structure.
;
; Returns:
;       eax - handle to the string with source code.
;**********************************************************************
proc CreateSourceTemplate, .ptrWinTemplateDT
begin
        stdcall StrNew
        push    eax
        stdcall _DoCreateSourceTemplate, [.ptrWinTemplateDT], eax, FALSE
        pop     eax
        return
endp


iglobal
  cFormMacro  db 'Window ',0
  cFormNULL   db '0',0
  cFormNameNone db 'NONE',0
endg



proc _DoCreateSourceTemplate, .ptrWinTemplateDT, .res, .fNotLast
begin
        push    esi edi ebx
        mov     esi, [.ptrWinTemplateDT]

; create the line for parent window
;-------------------------------------------------------
        stdcall StrCat, [.res], cFormMacro

        mov     eax, [esi+TWinTemplateDT.hName] ; Or maybe empty string?
        test    eax, eax
        jnz     @f
        mov     eax, cFormNameNone
@@:
        stdcall StrCat, [.res], eax
        stdcall StrCharCat, [.res], ', '

; Add wtXXX flags
        xor     eax, eax
        mov     ecx, [esi+TWinTemplateDT.children]
        cmp     [ecx+TArray.count], 0
        je      @f
        or      eax, wtParent
@@:
        cmp     [.fNotLast], 0
        jne     @f
        or      eax, wtEnd
@@:
        stdcall NumToStr, eax, ntsDec or ntsUnsigned
        stdcall StrCat, [.res], eax
        stdcall StrDel, eax
        stdcall StrCharCat, [.res], ', '

; Add Align:
        movzx   eax, [esi+TWinTemplateDT.Align]
        stdcall NumToStr, eax, ntsDec or ntsUnsigned
        stdcall StrCat, [.res], eax
        stdcall StrDel, eax
        stdcall StrCharCat, [.res], ', '
;
; Add Class name
        stdcall StrCharCat, [.res], "'"
        stdcall StrCat, [.res], [esi+TWinTemplateDT.ptrClass]
        stdcall StrCharCat, [.res], "'"
        stdcall StrCharCat, [.res], ', '

; Add window text
        stdcall StrCharCat, [.res], "'"
        stdcall StrCat, [.res], [esi+TWinTemplateDT.ptrText]
        stdcall StrCharCat, [.res], "'"
        stdcall StrCharCat, [.res], ', '

; Add Style and StyleEx
        stdcall NumToStr, [esi+TWinTemplateDT.style], ntsHex or ntsUnsigned
        stdcall StrCharCat, [.res], '$'
        stdcall StrCat, [.res], eax
        stdcall StrDel, eax
        stdcall StrCharCat, [.res], ', '

        stdcall NumToStr, [esi+TWinTemplateDT.styleEx], ntsHex or ntsUnsigned
        stdcall StrCharCat, [.res], '$'
        stdcall StrCat, [.res], eax
        stdcall StrDel, eax
        stdcall StrCharCat, [.res], ', '

; Add ID
        movzx   eax, [esi+TWinTemplateDT.ID]
        stdcall NumToStr, eax, ntsDec or ntsUnsigned
        stdcall StrCat, [.res], eax
        stdcall StrDel, eax
        stdcall StrCharCat, [.res], ', '

; Add coordinates/size
        movsx   eax, [esi+TWinTemplateDT.left]
        stdcall NumToStr, eax, ntsDec
        stdcall StrCat, [.res], eax
        stdcall StrDel, eax
        stdcall StrCharCat, [.res], ', '

        movsx   eax, [esi+TWinTemplateDT.top]
        stdcall NumToStr, eax, ntsDec
        stdcall StrCat, [.res], eax
        stdcall StrDel, eax
        stdcall StrCharCat, [.res], ', '

        movsx   eax, [esi+TWinTemplateDT.width]
        stdcall NumToStr, eax, ntsDec
        stdcall StrCat, [.res], eax
        stdcall StrDel, eax
        stdcall StrCharCat, [.res], ', '

        movsx   eax, [esi+TWinTemplateDT.height]
        stdcall NumToStr, eax, ntsDec
        stdcall StrCat, [.res], eax
        stdcall StrDel, eax
        stdcall StrCharCat, [.res], ', '

; Add user WinProc
        mov     ecx, [esi+TWinTemplateDT.WinProc]
        stdcall StrLen, ecx
        test    eax, eax
        jnz     @f
        mov     ecx, cFormNULL
@@:
        stdcall StrCat, [.res], ecx
        stdcall StrCharCat, [.res], ';'+13*$100+10*$10000

; recursive creation of children
        mov     esi, [esi+TWinTemplateDT.children]
        mov     ebx, [esi+TArray.count]

.loop:
        dec     ebx
        js      .endloop
        stdcall _DoCreateSourceTemplate, [esi+TArray.array], [.res], ebx
        add     esi, 4
        jmp     .loop

.endloop:
        pop     ebx edi esi
        return
endp




;------------------------------------
; Retrives the TBaseWin structure
; pointer from the given window.
; Returns NULL if the window have no
; atached such structure.
;------------------------------------
;proc GetBasePointer, hwnd
;begin
;        push    esi
;        invoke  GetWindowLongA, [.hwnd], GWL_USERDATA
;        test    eax, eax
;        jz      .exit
;        mov     esi, eax
;        invoke  IsBadReadPtr, esi, sizeof.TAlignData
;        test    eax, eax
;        jnz     .false
;
;        mov     esi, [esi+TAlignData.UserData]
;        invoke  IsBadReadPtr, esi, sizeof.TBaseWin
;        test    eax, eax
;        jnz     .false
;
;        mov     eax, esi
;        pop     esi
;        return
;
;.false:
;        xor     eax, eax
;.exit:
;        pop     esi
;        return
;endp
;




;------------------------------------------------------------------------
; Create binary structure TWinTemplateDT structure from
; source text in FASM syntax.
;
; Arguments:
;   ptrSource: pointer to the source text.
;
; Returns:
;   eax: TWinTemplateDT structure containing all children etc.
;   edx: pointer to the next source line after the last processed.
;------------------------------------------------------------------------
proc CompileSourceTemplate, .ptrSource
begin
        push    esi edi ebx

        mov     esi, [.ptrSource]
        invoke  IsBadStringPtrA, esi, -1
        test    eax, eax
        jnz     .errorformat

.compileloop:
        stdcall GetMem, sizeof.TWinTemplateDT
        mov     edi, eax

        stdcall CreateArray, 4
        mov     [edi+TWinTemplateDT.children], eax

        cmp     dword [esi], 'Wind'
        jne     .errorformat

        cmp     word [esi+4], 'ow'
        jne     .errorformat

        cmp     byte [esi+6], ' '
        jne     .errorformat

        add     esi, 7
        mov     ebx, esi        ; begin of the label

.search:
        call    SearchComma
        jc      .errorformat

.getname:
        mov     byte [esi-1], 0

        stdcall StrDup, ebx     ; creates string with name of the window.
        mov     [edi+TWinTemplateDT.hName], eax

; get TWinTemplateDT.flags value
        call    SearchNonSpace
        jc      .errorformat
        mov     ebx, esi
        call    SearchComma
        jc      .errorformat
        mov     byte [esi-1], 0

        stdcall StrToFasmNum, ebx
        jc      .errorformat

        mov     [edi+TWinTemplateDT.flags], al

; get TWinTemplateDT.Align value
        call    SearchNonSpace
        jc      .errorformat
        mov     ebx, esi
        call    SearchComma
        jc      .errorformat
        mov     byte [esi-1], 0

        stdcall StrToFasmNum, ebx
        jc      .errorformat

        mov     [edi+TWinTemplateDT.Align], al

; get TWinTemplateDT.ptrClass
        call    SearchNonSpace
        jc      .errorformat
        cmp     byte [esi], "'"
        jne     .errorformat

        inc     esi
        mov     ebx, esi
        call    SearchQuote
        jc      .errorformat

        mov     byte [esi-1], 0

        stdcall StrDup, ebx
        mov     [edi+TWinTemplateDT.ptrClass], eax

; get TWinTemplateDT.ptrText
        call    SearchQuote
        jc      .errorformat

        mov     ebx, esi
        call    SearchQuote
        jc      .errorformat

        mov     byte [esi-1], 0
        stdcall StrDup, ebx
        mov     [edi+TWinTemplateDT.ptrText], eax

; get TWinTemplateDT.style
        add     esi, 2
        call    SearchNonSpace
        jc      .errorformat

        mov     ebx, esi
        call    SearchComma
        jc      .errorformat

        mov     byte [esi-1], 0
        stdcall StrToFasmNum, ebx
        jc      .errorformat

        mov     [edi+TWinTemplateDT.style], eax

; get TWinTemplateDT.styleEx

        call    SearchNonSpace
        jc      .errorformat
        mov     ebx, esi

        call    SearchComma
        jc      .errorformat

        mov     byte [esi-1], 0
        stdcall StrToFasmNum, ebx
        jc      .errorformat

        mov     [edi+TWinTemplateDT.styleEx], eax

; get  TWinTemplateDT.ID

        call    SearchNonSpace
        jc      .errorformat
        mov     ebx, esi

        call    SearchComma
        jc      .errorformat

        mov     byte [esi-1], 0
        stdcall StrToFasmNum, ebx
        jc      .errorformat

        mov     [edi+TWinTemplateDT.ID], ax

; get  TWinTemplateDT.left
        call    SearchNonSpace
        jc      .errorformat
        mov     ebx, esi

        call    SearchComma
        jc      .errorformat

        mov     byte [esi-1], 0
        stdcall StrToFasmNum, ebx
        jc      .errorformat

        mov     [edi+TWinTemplateDT.left], ax

; get  TWinTemplateDT.top

        call    SearchNonSpace
        jc      .errorformat
        mov     ebx, esi

        call    SearchComma
        jc      .errorformat

        mov     byte [esi-1], 0
        stdcall StrToFasmNum, ebx
        jc      .errorformat

        mov     [edi+TWinTemplateDT.top], ax

; get  TWinTemplateDT.width

        call    SearchNonSpace
        jc      .errorformat
        mov     ebx, esi

        call    SearchComma
        jc      .errorformat

        mov     byte [esi-1], 0
        stdcall StrToFasmNum, ebx
        jc      .errorformat

        mov     [edi+TWinTemplateDT.width], ax

; get  TWinTemplateDT.height

        call    SearchNonSpace
        jc      .errorformat
        mov     ebx, esi

        call    SearchComma
        jc      .errorformat

        mov     byte [esi-1], 0
        stdcall StrToFasmNum, ebx
        jc      .errorformat

        mov     [edi+TWinTemplateDT.height], ax

; get TWinTemplateDT.WinProc, i.e. the label to the user procedure.
        stdcall StrNew
        mov     [edi+TWinTemplateDT.WinProc], eax

        call    SearchNonSpace
        jc      .errorformat
        cmp     byte [esi], '0'
        je      .nowinproc

        mov     ebx, esi
        call    SearchComment
        jc      .errorformat

        mov     byte [esi-1], 0
        stdcall StrCopy, [edi+TWinTemplateDT.WinProc], ebx

.nowinproc:
        call    SkipToNextLine  ; go to the next line to get next window.

; the window template is created, so what to do?
        test    [edi+TWinTemplateDT.flags], wtParent
        jz      .childrenok

.childrenloop:
        stdcall CompileSourceTemplate, esi
        mov     esi, edx                     ; next line of source
        mov     ebx, eax                     ; TWinTemplateDT of child.

        test    ebx, ebx
        jz      .errorformat

        stdcall AddArrayItems, [edi+TWinTemplateDT.children], 1
        mov     [edi+TWinTemplateDT.children], edx
        mov     [eax], ebx

        test    [ebx+TWinTemplateDT.flags], wtEnd
        jz      .childrenloop

.childrenok:
        stdcall VacuumArray, [edi+TWinTemplateDT.children]
        mov     [edi+TWinTemplateDT.children], edx

        mov     eax, edi
        mov     edx, esi

        pop     ebx edi esi
        return

.errorformat:
        xor     eax, eax
        pop     ebx edi esi
        return
endp


proc SearchComma
begin
.search:
        lodsb
        cmp     al, ','
        je      .found

        cmp     al, $0d
        je      .error
        cmp     al, $0a
        je      .error
        cmp     al, ' '
        je      .error
        cmp     al, $09
        jne     .search

.error:
        stc
        return

.found:
        clc
        return
endp


proc SkipToNextLine
begin
.searchloop:
        lodsb
        test    al,al
        jz      .error
        cmp     al, $0d         ; 0000 1101
        je      .found
        xor     al, $0a         ; 0000 1010
        jne     .searchloop
.found:
        xor     al, $07         ; CR <-> LF
        cmp     [esi], al
        jne     .exit
        inc     esi
.exit:
        clc
        return

.error:
        stc
        dec     esi
        return
endp


proc SearchNonSpace
begin
.loop:
        lodsb
        test    al,al
        jz      .error
        cmp     al, $0d
        je      .error
        cmp     al, $0a
        je      .error

        cmp     al, ' '
        je      .loop
        cmp     al, $09
        je      .loop

        dec     esi
        clc
        return

.error:
        dec     esi
        stc
        return
endp


proc SearchQuote
begin
.search:
        lodsb
        cmp     al, "'"
        je      .found

        cmp     al, $0d
        je      .error
        cmp     al, $0a
        jne     .search

.error:
        stc
        return

.found:
        clc
        return
endp

proc SearchComment
begin
.search:
        lodsb
        cmp     al, ";"
        je      .found

        cmp     al, $0d
        je      .error
        cmp     al, $0a
        je      .error
        test    al, al
        jnz     .search
;        cmp     al, ' '        ; Not needed. IMO. maybe.
;        je      .error
;        cmp     al, $09
;        jne     .search

.error:
        stc
        return

.found:
        clc
        return
endp






;--------------------------------------------------
; Creates the form from design window template
;--------------------------------------------------
proc CreateDesignWindow, .ptrTemplate
begin
        push    esi

        DebugMsg "Create design window"

        mov     esi, [.ptrTemplate]
        cmp     [esi+TWinTemplateDT.hwnd], 0
        je      @f

        invoke  DestroyWindow, [esi+TWinTemplateDT.hwnd]

@@:
        cmp     [esi+TWinTemplateDT.hSizer], 0
        je      @f

; deal with sizer window

; crashes with this !!!
;        invoke  DestroyWindow, [esi+TWinTemplateDT.hSizer]
;        mov     [esi+TWinTemplateDT.hSizer], 0

@@:
        xor     edx, edx
        mov     eax, [.ptrTemplate]
        mov     esi, [esi+TWinTemplateDT.parent]
        test    esi, esi
        jz      @f
        mov     edx, [esi+TWinTemplateDT.hwnd]
@@:
        stdcall _DoCreateDesignWindow, edx, [.ptrTemplate]
        push    eax

        mov     eax, [.ptrTemplate]
        stdcall GetSizer
        invoke  SendMessageW, eax, SZM_UPDATESIZE, 0, 0

        mov     eax, [.ptrTemplate]
        stdcall ReorderChildren, [eax+TWinTemplateDT.parent]

        pop     eax
        pop     esi
        return
endp


proc ReorderChildren, .ptrTemplate
begin
        push    esi edi ebx
        cmp     [.ptrTemplate], 0
        je      .finish

        mov     esi, [.ptrTemplate]
        mov     esi, [esi+TWinTemplateDT.children]
        mov     ebx, [esi+TArray.count]

        mov     edi, HWND_BOTTOM

.loop:
        dec     ebx
        js      .endloop

        mov     eax, dword [esi+TArray.array]
        push    SWP_NOSIZE or SWP_NOMOVE
        push    0 0 0 0
        push    edi
        push    [eax+TWinTemplateDT.hwnd]
        mov     edi, [eax+TWinTemplateDT.hwnd]
        invoke  SetWindowPos
        add     esi, 4
        jmp     .loop

.endloop:
        mov     eax, [.ptrTemplate]

.finish:
        pop     ebx edi esi
        return
endp



;----- internally called from CreateDesignWindow -----
proc _DoCreateDesignWindow, .hParent, .ptrTemplate
.free rd 4
begin
        push    esi edi ebx

        mov     esi, [.ptrTemplate]

        movsx   eax, [esi+TWinTemplateDT.left]
        movsx   ebx, [esi+TWinTemplateDT.top]
        movsx   ecx, [esi+TWinTemplateDT.width]
        movsx   edx, [esi+TWinTemplateDT.height]
        movzx   edi, [esi+TWinTemplateDT.ID]

        cmp     [.hParent], 0
        cmove   edi, [.hParent]                 ; the ID for the main window is always 0

        push    NULL                                                    ; lpParam
        push    [hInstance]                                             ; hInstance
        push    edi                                                     ; ID

        mov     edi, [.hParent]
        test    edi, edi
        cmovz   edi, [hMainForm]

        push    edi                                                     ; parent

        push    edx ecx ebx eax                                         ; height, width, y, x
        mov     eax, [esi+TWinTemplateDT.style]
        and     eax, not WS_VISIBLE

        cmp     [.hParent], 0
        jne     @f
        or      eax, WS_SIZEBOX
        and     eax, not WS_CHILD
@@:
        push    eax                                                     ; Style

        stdcall utf8ToWideChar, [esi+TWinTemplateDT.ptrText]
        mov     [.free], eax
        push    eax                                                     ; window text

        stdcall utf8ToWideChar, [esi+TWinTemplateDT.ptrClass]
        mov     [.free+4], eax
        push    eax                                                     ; class name

; Set WS_EX_NOACTIVATE style for the root design window. This is actually fix for WINE/Linux problems with the
; window manager, silently activating new windows, even when SW_SHOWNA (SWP_NOACTIVATE) is specified.

        mov     eax, [esi+TWinTemplateDT.styleEx]
        cmp     [.hParent], 0
        jnz     @f
        or      eax, WS_EX_NOACTIVATE
@@:
        push    eax                                                     ; StyleEx

        invoke  CreateWindowExW

        stdcall FreeMem, [.free]
        stdcall FreeMem, [.free+4]

        test    eax, eax
        jz      .errorcreate

        mov     [esi+TWinTemplateDT.hwnd], eax

; disable all internal windows
        invoke  EnumChildWindows, [esi+TWinTemplateDT.hwnd], procDisableCallback, 0

        cmp     [esi+TWinTemplateDT.parent], 0
        jne     .sizerok

; This is not proper way of handling sizer window in all cases.
; for example when there is already a sizer in the window and it
; have some windows selected.
        invoke  CreateWindowExW, WS_EX_TRANSPARENT, cSizerClassName, NULL,        \
                                WS_CHILD, 0, 0, 0, 0, [esi+TWinTemplateDT.hwnd], \
                                0, [hInstance], 0
        mov     [esi+TWinTemplateDT.hSizer], eax

.sizerok:
        stdcall SubclassWindow, [esi+TWinTemplateDT.hwnd], procDesignControl
        invoke  SetPropW, [esi+TWinTemplateDT.hwnd], [propTemplateDT], esi
        movzx   eax, [esi+TWinTemplateDT.Align]

        invoke  SendMessageW, [.hParent], WM_GETFONT, 0, 0
        stdcall ControlSetFont, [esi+TWinTemplateDT.hwnd], eax

        mov     eax, [esi+TWinTemplateDT.children]
        cmp     [eax+TArray.count], 0
        je      .childrenOK

        mov     edi, [esi+TWinTemplateDT.children]
        mov     ebx, [edi+TArray.count]

.childrenloop:
        dec     ebx
        js      .childrenOK

        mov     eax, [edi+TArray.array]
        mov     [eax+TWinTemplateDT.parent], esi

        stdcall _DoCreateDesignWindow, [esi+TWinTemplateDT.hwnd], eax
        add     edi, 4
        jmp     .childrenloop

.childrenOK:
        stdcall SearchForDesignTimeInfo, [esi+TWinTemplateDT.ptrClass], [esi+TWinTemplateDT.style]
        mov     [esi+TWinTemplateDT.DesignInfo], eax

        invoke  ShowWindow, [esi+TWinTemplateDT.hwnd], SW_SHOWNA

        mov     eax, esi
        pop     ebx edi esi
        return

.errorcreate:
        stdcall LastError
        jmp     .childrenOK
endp


proc procDisableCallback, .hwnd, .dummy
begin
        invoke  EnableWindow, [.hwnd], FALSE
        mov     eax, 1
        return
endp


proc CountDesignWindows, .ptrWinTemplateDT
begin
        push    esi edi ebx

        mov     eax, [.ptrWinTemplateDT]
        test    eax, eax
        jz      .finish

        mov     esi, [eax+TWinTemplateDT.children]
        mov     ebx, [esi+TArray.count]
        xor     edi, edi

.childrenloop:
        dec     ebx
        js      .endchildren
        stdcall CountDesignWindows, [esi+4*ebx+TArray.array]
        add     edi, eax
        jmp     .childrenloop

.endchildren:
        lea     eax, [edi+1]

.finish:
        pop     ebx edi esi
        return
endp


iglobal
  property propTemplateDT, 'TemplateDT'
endg