uglobal
  hEHelp dd ?
  hEHFont dd ?
endg

iglobal
  cEHelpFont du 'Courier New', 0
endg

initialize InitEHelp
begin
        xor     ebx, ebx
        invoke  CreateFontW, -12, ebx, ebx, ebx, FW_DONTCARE,                                 \
                            ebx, ebx, ebx,                                                   \
                            1, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,     \
                            FIXED_PITCH or FF_DONTCARE, cEHelpFont
        mov     [hEHFont], eax
        return
endp


finalize FreeEHelp
begin
        invoke  DeleteObject, [hEHFont]
        return
endp




proc CreateEHWindow, .parent
begin
        cmp     [hEHelp], 0
        jne     .exit

        invoke  CreateWindowExW, 0, cStaticClassName, 0,                   \
                WS_POPUP or WS_BORDER or SS_OWNERDRAW or SS_LEFT,         \
                10, 10, 564, 300, [.parent], 0, [hInstance], 0
        mov     [hEHelp], eax
        stdcall SubclassWindow, eax, procEHelp

.exit:
        invoke  SetPropW, [hEHelp], [propOwnerAsmEdit], [.parent]
        call    UpdateEHelp
        return
endp



proc UpdateEHelp

  .info MONITORINFO

  .edit dd ?
  .help dd ?

  .height dd ?
  .caret AECARETXY

begin
        cmp     [hEHelp], 0
        je      .exit

        invoke  GetPropW, [hEHelp], [propOwnerAsmEdit]
        mov     [.edit], eax

; create the text content of the window

        stdcall GetDefinition
        jc      .notfound

        push    eax
        stdcall ExtractEmbededHelp, eax, edx
        stdcall StrDel ; from the stack
        test    eax, eax
        jz      .notfound

        mov     [.help], eax

; then set the possition
  ; first, compute the size of the window.

        stdcall SetControlTextUtf8, [hEHelp], [.help]

        invoke  GetDC, [hEHelp]
        push    eax
        invoke  CreateCompatibleDC, eax
        push    eax
        stdcall PaintEHelp, [hEHelp], eax
        mov     [.height], eax
        invoke  DeleteDC ; from the stack
        invoke  ReleaseDC, [hEHelp] ; from the stack

  ; then compute the place

        lea     ebx, [.caret]
        invoke  SendMessageW, [.edit], AEM_GETCARETXY, ebx, 0
        invoke  ClientToScreen, [.edit], ebx
        add     ebx, 8
        invoke  ClientToScreen, [.edit], ebx
        sub     ebx, 8

        invoke  MonitorFromWindow, [.edit], 0

        mov     [.info.cbSize], sizeof.MONITORINFO
        lea     ecx, [.info]
        invoke  GetMonitorInfoW, eax, ecx

        mov     eax, 564
        mov     edx, [.height]

        mov     ecx, [ebx+AECARETXY.x0]
        add     eax, ecx
        sub     [.info.rcWork.right], eax
        jns     @f
        add     ecx, [.info.rcWork.right]
@@:
        mov     [.info.rcWork.right], ecx

        mov     ecx, [ebx+AECARETXY.y1]
        sub     [.info.rcWork.bottom], edx
        cmp     ecx, [.info.rcWork.bottom]
        jbe     @f
        mov     ecx, [ebx+AECARETXY.y0]
        sub     ecx, edx
@@:
        mov     [.info.rcWork.top], ecx

        invoke  SetWindowPos, [hEHelp], 0, [.info.rcWork.right], ecx, 564, [.height], SWP_NOZORDER or SWP_NOACTIVATE or SWP_SHOWWINDOW
        invoke  InvalidateRect, [hEHelp], 0, TRUE

.finish:
        stdcall StrDel, [.help]

.exit:
        return

.notfound:
        invoke  PostMessageW, [hEHelp], WM_CLOSE, 0, 0
        jmp     .finish
endp



proc procEHelp, .hwnd, .wmsg, .wparam, .lparam
begin

        mov     ebx, [.wmsg]
        dispatch ebx

        stc
        return

;___________________________________________________________________________
;
oncase WM_ERASEBKGND
cEHBackground = $ffffd0
locals
  .bkrect RECT
endl
        lea     ebx, [.bkrect]
        invoke  GetClientRect, [.hwnd], ebx
        invoke  CreateSolidBrush, cEHBackground
        push    eax
        invoke  FillRect, [.wparam], ebx, eax
        invoke  DeleteObject ; from the stack
        xor     eax, eax
        dec     eax
        clc
        return

;___________________________________________________________________________
;
oncase WM_PAINT
locals
  .update RECT
  .ps     PAINTSTRUCT
endl

        lea     eax, [.update]
        invoke  GetUpdateRect, [.hwnd], eax, TRUE
        test    eax, eax
        jz      .finishpaint

        lea     esi, [.ps]
        invoke  BeginPaint, [.hwnd], esi
        test    eax, eax
        jz      .endpaint

        stdcall PaintEHelp, [.hwnd], [esi+PAINTSTRUCT.hdc]

.endpaint:
        invoke  EndPaint, [.hwnd], esi

.finishpaint:
        xor     eax, eax
        clc
        return


;___________________________________________________________________________
;
oncase WM_CLOSE
        invoke  DestroyWindow, [.hwnd]
        mov     [hEHelp], 0
        clc
        return

        enddispatch
endp




; paints the embeded help window and returns the size needed.
; eax: height of the window needed.
proc PaintEHelp, .hwnd, .hdc
.str dd ?
.deffont dd ?
.point POINT
.metrics TEXTMETRIC
.rect RECT
begin
        push    ebx edi

        stdcall GetControlTextW, [.hwnd]
        mov     edi, eax

        invoke  SetTextColor, [.hdc], $000000
        invoke  SetBkMode, [.hdc], TRANSPARENT
        invoke  SelectObject, [.hdc], [hEHFont]
        mov     [.deffont], eax

        lea     ebx, [.rect]
        invoke  GetClientRect, [.hwnd], ebx
        add     [ebx+RECT.left], 2
        add     [ebx+RECT.top], 2
        sub     [ebx+RECT.right], 2
        mov     [ebx+RECT.bottom], 10000
        invoke  DrawTextW, [.hdc], edi, -1, ebx, DT_LEFT or DT_EXTERNALLEADING

        add     eax, 8
        pop     edi ebx
        return
endp




proc ExtractEmbededHelp, .hFilename, .LineNumber
begin
        pushad

        dec     [.LineNumber]
        stdcall CreateArray, 4
        mov     edx, eax

        stdcall FileOpen, [.hFilename]
        jc      .error
        mov     ebx, eax

.read_loop:
        stdcall FileReadLine, ebx
        test    eax, eax
        jz      .end_of_file

        push    eax
        stdcall AddArrayItems, edx, 1
        popd    [eax]
        jmp     .read_loop

.end_of_file:
        stdcall FileClose, ebx

        mov     ecx, [.LineNumber]
        cmp     ecx, [edx+TArray.count]
        jae     .error

.search_start:
        dec     ecx
        js      .end_found

        mov     ebx, [edx+TArray.array+4*ecx]

        stdcall StrClipSpacesR, ebx
        stdcall StrClipSpacesL, ebx
        stdcall StrLen, ebx
        test    eax, eax
        jz      .search_start

        inc     ecx

.search_loop:
        dec     ecx
        js      .end_found

        mov     ebx, [edx+TArray.array+4*ecx]

        stdcall StrClipSpacesR, ebx
        stdcall StrClipSpacesL, ebx
        stdcall StrPtr, ebx

        cmp     byte [eax], ';'
        je      .search_loop

.end_found:
        stdcall StrNew
        mov     ebx, eax

.skip_leading_empty_lines:
        inc     ecx
        cmp     ecx, [.LineNumber]
        jae     .end_compose

        stdcall StrLen, [edx+TArray.array+4*ecx]
        test    eax, eax
        jz      .skip_leading_empty_lines

        dec     ecx

.compose_loop:
        inc     ecx
        cmp     ecx, [.LineNumber]
        jae     .end_compose

        stdcall StrCat, ebx, [edx+TArray.array+4*ecx]
        stdcall StrCharCat, ebx, $0a0d
        jmp     .compose_loop

.end_compose:
        stdcall StrClipSpacesL, ebx
        mov     [esp+4*regEAX], ebx

        stdcall StrPtr, ebx
        mov     ecx, [eax+string.len]

.skip_trailing:
        dec     ecx
        js      .end_trailing

        cmp     byte [eax+ecx], ' '
        je      .skip_trailing
        cmp     byte [eax+ecx], $0d
        je      .skip_trailing
        cmp     byte [eax+ecx], $0a
        je      .skip_trailing

.end_trailing:
        inc     ecx
        jecxz   .error

        mov     dword [eax+ecx], 0
        mov     [eax+string.len], ecx

.finish:
        stdcall ListFree, edx, StrDel
        popad
        return

.error:
        stdcall StrDel, [esp+4*regEAX]
        mov     dword [esp+4*regEAX], 0
        jmp     .finish
endp