cHtmlHelpA  text "HtmlHelpA"
;  cFreshHelp    db "%fresh%\"
;  cFreshDefHelp db "doc\freshguide.chm", 0
;  .size = $ - cFreshDefHelp

iglobal
  cHHCtrlLib  du "HHCTRL.OCX", 0
endg


uglobal
  HtmlHelp     dd ?
  hHHCtrl      dd ?
  ptrHelpFiles dd ?     ; points to array of dwords with handles of strings with help filenames.
endg

proc InitHelp
begin
        invoke  LoadLibraryW, cHHCtrlLib
        mov     [hHHCtrl],eax
        or      eax,eax
        jz      .finish

        invoke  GetProcAddress, eax, cHtmlHelpA

.finish:
        mov     [HtmlHelp],eax
        return
endp



finalize FreeHelpList
begin
        push    eax
        xor     eax, eax
        xchg    eax, [ptrHelpFiles]
        test    eax, eax
        jz      .finish

        shl     [eax+TArray.count], 1              ; turn it to N*2 array or 4 bytes elements.
        mov     [eax+TArray.itemsize], 4
        stdcall ListFree, eax, StrDel

.finish:
        pop     eax
        return
endp



proc RebuildHelpFilesList
begin
        pushad
        stdcall FreeHelpList
        stdcall CreateArray, 8
        mov     [ptrHelpFiles], eax

        stdcall ConvertAliasString, '%FreshHelp%/index.help'
        mov     esi, eax

        stdcall FileOpenAccess, esi, faReadOnly
        jc      .finish

        mov     ebx, eax

.readloop:
        stdcall FileReadLine, ebx
        jc      .eof
        test    eax, eax
        jz      .eof

        push    eax

        stdcall StrClipSpacesR, eax
        stdcall StrClipSpacesL, eax

        stdcall StrPtr, eax
        cmp     dword [eax+string.len], 0
        je      .next                           ; empty line

        cmp     byte [eax], ';'         ; comment line!
        je      .next

        stdcall StrSplitList, eax, '|', FALSE

        push    StrDel
        push    eax             ; arguments for ListFree

        cmp     [eax+TArray.count], 2
        jb      .free_split

        stdcall StrClipSpacesR, [eax+TArray.array]
        stdcall StrClipSpacesL, [eax+TArray.array]
        stdcall StrClipSpacesR, [eax+TArray.array+4]
        stdcall StrClipSpacesL, [eax+TArray.array+4]

        push    [eax+TArray.array+4]    ; the filename
        push    [eax+TArray.array]      ; the title

        stdcall AddArrayItems, [ptrHelpFiles], 1
        mov     [ptrHelpFiles], edx
        mov     edx, eax

        stdcall StrDup ; file title from the stack.
        mov     [edx], eax

        stdcall StrDup ; filename from the stack.
        mov     [edx+4], eax

.free_split:
        stdcall ListFree        ; arguments from the stack.

.next:
        stdcall StrDel ; from the stack
        jmp     .readloop

.eof:
        stdcall FileClose, ebx

.finish:
        popad
        return
endp



;proc _AddOneHelpFile, .ptrName, .dummy
;begin
;        stdcall StrDup, [.ptrName]
;        push    eax
;        stdcall AddArrayItems, [ptrHelpFiles], 1
;        mov     [ptrHelpFiles], edx
;        popd    [eax]
;        clc
;        return
;endp



proc OnHelp, .wparam, .lparam
.cursor    dd ?
.filecount dd ?
.buffw     rw 512
.hWord     dd ?
begin
        invoke  LoadCursorW, 0, IDC_WAIT
        invoke  SetCursor, eax
        mov     [.cursor], eax

        stdcall GetCurrentAsmEdit
        jc      .finish_help

        lea     ebx, [.buffw]
        invoke  SendMessageW, eax, AEM_GETWORDATCARET, 512, ebx

        stdcall WideCharToUtf8, ebx
        mov     [.hWord], eax

        stdcall StrPtr, eax
        mov     ebx, eax


; check for Windows API suffix and remove it, if the corresponding option is set.

        stdcall CfgGetInt, [hCfgFileName], cParamSection, cRemoveSuffix, 0
        test    eax, eax
        jz      .suffix_ok              ; this option is OFF


        stdcall StrLen, [.hWord]

        cmp     eax, 6                  ; heuristic - don't process words shorter than 6 characters.
        jb      .suffix_ok

        cmp     byte [ebx + eax - 1], 'A'
        je      .check_more

        cmp     byte [ebx + eax - 1], 'W'
        jne     .suffix_ok


.check_more:
        cmp     byte [ebx + eax - 2], 'a'
        jb      .suffix_ok

        cmp     byte [ebx + eax - 2], 'z'
        ja      .suffix_ok

        mov     byte [ebx + eax - 1], 0


.suffix_ok:
        mov     esi, [ptrHelpFiles]
        test    esi, esi
        jz      .search_inet

        push    [esi+TArray.count]
        pop     [.filecount]

        cmp     [.filecount], 0
        je      .search_inet

        xor     edi, edi

.searchloop:
        stdcall SearchOneHelp, [esi+TArray.array+8*edi+4], ebx, FALSE
        jc     .finish_help

        inc    edi
        cmp    edi, [.filecount]
        jne    .searchloop

;; Now check the project "Documents" folder:
;.search_project:
;        cmp    [hProjManager], 0
;        je     .project_help_not_found
;
;        invoke  SendMessageA, [hProjManager], PMM_GETCATEGORY, 0, 'Documents'
;
;        test    eax, eax
;        jz      .project_help_not_found
;
;        mov     esi, eax
;        mov     ecx, [esi+TArray.count]
;        lea     esi, [esi+TArray.array]
;        jecxz   .project_help_not_found
;
;.proj_help:
;        mov     eax, [esi]
;        add     esi, 4
;
;        stdcall SearchOneHelp, [eax+TOpenFile.hFileName], ebx, FALSE
;        jc      .finish_help
;
;        dec     ecx
;        jnz     .proj_help
;
;
;.project_help_not_found:


; The offline help failed, so search in internet.
.search_inet:
        stdcall CfgGetInt, [hCfgFileName], cParamSection, cGoogleKey, 0
        test    eax, eax
        jz      .finish_help

        stdcall StrDupMem, 'http://www.google.com/search?q='
        mov     esi, eax

        stdcall StrURLEncode, ebx
        stdcall StrCat, esi, eax
        stdcall StrDel, eax

        stdcall CfgGetInt, [hCfgFileName], cParamSection, cFeelingKey, 0
        test    eax, eax
        jnz     .feeling

        stdcall StrCat, esi, '&btnG=1'
        jmp     .exec

.feeling:
        stdcall StrCat, esi, '&btnI=1&gbv=1'

.exec:
        stdcall OpenWebPage, esi
        stdcall StrDel, esi

.finish_help:
        invoke  SetCursor, [.cursor]
        return
endp


cMarkdownHelpHeader text '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">', CRLF,   \
                         '<html xmlns="http://www.w3.org/1999/xhtml">', CRLF,                                                                                 \
                         '  <head>', CRLF,                                                                                                                    \
                         '    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />', CRLF,                                                   \
                         '    <meta http-equiv="Content-Style-Type" content="text/css" />', CRLF,                                                             \
                         '    <title>Fres IDE help</title>', CRLF,                                                                                            \
                         '    <link rel="stylesheet" href="help.css" type="text/css" />', CRLF,                                                               \
                         '  </head>', CRLF,                                                                                                                   \
                         '  <body>', CRLF


proc SearchOneHelp, .hFileName, .ptrWord, .flagIndex
  .aklink    HH_AKLINK
  .fullname  dd ?
begin
        push    ebx ecx esi edi

        stdcall ConvertAliasString, '%FreshHelp%/'
        stdcall StrCat, eax, [.hFileName]
        mov     [.fullname], eax

        stdcall StrLen, [.hFileName]
        mov     ecx, eax
        stdcall StrPtr, [.hFileName]

.extloop:
        dec     ecx
        js      .notfound

        cmp     byte [eax+ecx], '.'
        jne     .extloop

        mov     ecx, dword [eax+ecx]    ; get the extension
        or      ecx, $20202000          ; case insensitive

        cmp     ecx, '.hlp'
        je      .winhelp

        cmp     ecx, '.chm'
        je      .htmlhelp

        cmp     ecx, '.idx'     ; text index file of keyword = URL for adding external documents.
        je      .index

        cmp     ecx, '.htm'
        je      .html

        cmp     ecx, '.md '
        je      .text_markdown

.notfound:
        clc
        pop     edi esi ecx ebx
        return

;....................................................
; scan the markdown formatted text file for the keyword...
.text_markdown:
        cmp     [.ptrWord], 0
        jne     .search_keyword_md

        cmp     [.flagIndex], 0
        je      .notfound
        jmp     .md_found

.search_keyword_md:
        stdcall LoadBinaryFile, [.fullname]
        jc      .notfound

        mov     esi, eax

        stdcall SearchKeywordMarkdown, esi, [.ptrWord]
        pushf
        stdcall FreeMem, esi
        popf
        jnc     .notfound

        mov     edi, eax

.md_found:
        stdcall StrDup, [cfgHelpURL]
        stdcall StrCat, eax, [.hFileName]

        cmp     [.ptrWord], 0
        je      @f
        stdcall StrCharCat, eax, '#'
        stdcall StrCat, eax, edi
        stdcall StrDel, edi
@@:
        ; now show the markdown file....
        stdcall OpenWebPage, eax
        stdcall StrDel, eax
        jmp     .found

;....................................................
; scan the html file for the keyword...
.html:
        cmp     [.ptrWord], 0
        jne     .search_keyword

        cmp     [.flagIndex], 0
        je      .notfound

        jmp     .keyword_ok


.search_keyword:
        stdcall LoadBinaryFile, [.fullname]
        jc      .notfound

        mov     ebx, eax
        stdcall SearchKeywordHTML, ebx, [.ptrWord]
        stdcall FreeMem, ebx
        cmp     ebx, eax
        je      .notfound  ; SearchKeywordHTML changes eax only if the keyword has been found.

        mov     esi, eax

.keyword_ok:
        stdcall StrDup, [cfgHelpURL]
        mov     edi, eax

        stdcall StrCat, edi, [.hFileName]

        cmp    [.ptrWord], 0
        je      @f

        stdcall StrCharCat, edi, '#'
        stdcall StrCat, edi, esi
        stdcall StrDel, esi

@@:
        stdcall OpenWebPage, edi
        stdcall StrDel, edi
        jmp     .found

;....................................................


.index:
        cmp     [.ptrWord], 0
        jne     .scan_index

        cmp     [.flagIndex], 0
        je      .notfound

        movx    [.ptrWord], '__index_main_file'

;        stdcall OpenFileByName, [.fullname], 0, TRUE
;        jmp     .found

.scan_index:
        stdcall FileOpenAccess, [.fullname], faReadOnly
        mov     ebx, eax
        jc      .notfound

.read_line:
        stdcall FileReadLine, ebx
        jc      .end_index
        test    eax, eax
        jz      .end_index

        mov     edx, eax

        stdcall StrClipSpacesR, edx
        stdcall StrClipSpacesL, edx
        stdcall StrPtr, edx
        cmp     byte [eax], ';' ; comment?
        jne     .not_comment

        stdcall StrDel, edx
        jmp     .read_line

.not_comment:
        stdcall StrSplitList, edx, '=', FALSE
        stdcall StrDel, edx

        mov     esi, eax
        cmp     [esi+TArray.count], 2
        jne     .next_line

        stdcall StrClipSpacesR, [esi+TArray.array]
        stdcall StrClipSpacesR, [esi+TArray.array+4]
        stdcall StrClipSpacesL, [esi+TArray.array]
        stdcall StrClipSpacesL, [esi+TArray.array+4]

        stdcall StrDupMem, [.ptrWord]
        stdcall StrCharCat, eax, '*'

        stdcall StrMatchPatternNoCase, eax, [esi+TArray.array]
        stdcall StrDel, eax
        jc      .line_found

.next_line:
        stdcall ListFree, esi, StrDel
        jmp     .read_line

.line_found:
        stdcall CheckAbsURL, [esi+TArray.array+4]
        jnc     .relative

        stdcall StrDup, [esi+TArray.array+4]
        mov     edi, eax
        jmp     .path_ok

.relative:
        stdcall StrDup, [cfgHelpURL]
        mov     edi, eax

        stdcall StrDup, [.hFileName]
        mov     ecx, eax
        stdcall StrSplitFilename, ecx
        stdcall StrDel, eax
        stdcall StrCat, ecx, [esi+TArray.array+4]
        stdcall StrCat, edi, ecx
        stdcall StrDel, ecx

.path_ok:
        stdcall OpenWebPage, edi
        stdcall StrDel, edi
        stdcall FileClose, ebx
        stdcall ListFree, esi, StrDel
        jmp     .found

.end_index:
        stdcall FileClose, ebx
        jmp     .notfound

;....................................................


.htmlhelp:
        cmp     [HtmlHelp], NULL
        je      .notfound

        xor     eax, eax
        mov     [.aklink.cbStruct], sizeof.HH_AKLINK
        mov     [.aklink.fReserved], eax
        mov     [.aklink.pszUrl], eax
        mov     [.aklink.pszMsgText], eax
        mov     [.aklink.pszMsgTitle], eax
        mov     [.aklink.pszWindow], eax

        push    [.ptrWord]
        pop     [.aklink.pszKeywords]

        push    [.flagIndex]
        pop     [.aklink.fIndexOnFail]

        stdcall StrPtr, [.fullname]
        mov     ebx,eax
        lea     eax, [.aklink]

        mov     ecx, HH_KEYWORD_LOOKUP
        cmp     [eax+HH_AKLINK.pszKeywords], 0
        jne     @f
        mov     ecx, HH_DISPLAY_TOC
@@:
        invoke  HtmlHelp, NULL, ebx, ecx, eax
        test    eax,eax   ; if eax==0 then given word doesn't exist in the help
        jz      .notfound   ;  file - so we search in user defined help file

.found:
        stc
        pop     edi esi ecx ebx
        return


.winhelp:
        stdcall utf8ToWideChar, [.fullname]
        mov     esi, eax

        cmp     [.ptrWord], 0
        jne     @f

        invoke  WinHelpW, [hEditorsHost], esi, HELP_FINDER, 0
        jmp     .found_winhlp

@@:
        stdcall utf8ToWideChar, [.ptrWord]
        push    eax
        invoke  WinHelpW, [hEditorsHost], esi, HELP_PARTIALKEY, eax
        stdcall FreeMem ; from the stack

        cmp     [.flagIndex], 0
        jne     .found_winhlp

        invoke  FindWindowW, cWinHelpName, 0
        mov     ebx, eax

        invoke  SendMessageW, ebx, WM_GETTEXTLENGTH, 0, 0
        invoke  IsWindowVisible, ebx
        test    eax, eax
        jnz     .found_winhlp

        stdcall utf8ToWideChar, [.fullname]
        push    eax

        invoke  WinHelpW, [hEditorsHost], esi, HELP_QUIT, 0
        stdcall FreeMem ; from the stack

.waitforclose:
        invoke  FindWindowW, cWinHelpName, 0
        test    eax, eax
        jnz     .waitforclose

        stdcall FreeMem, esi
        jmp     .notfound

.found_winhlp:
        stdcall FreeMem, esi
        jmp     .found

endp


iglobal
  cWinHelpName du 'MS_WINHELP', 0
endg



proc OpenWebPage, .hURL
begin
        pushad

        stdcall GetParamFromINI, cWebBrowserKey
        jc      .finish

        mov     ebx, eax

        stdcall StrCharCat, ebx, ' '
        stdcall StrCat, ebx, [.hURL]

        stdcall Exec, ebx
        jc      .finish2

        stdcall FreeProcessID, eax
        stdcall FreeThreadID, edx
        clc

.finish2:
        stdcall StrDel, ebx

.finish:
        popad
        return
endp


; Returns CF=1 if the URL is absolute
;         CF=0 if the URL is relative

proc CheckAbsURL, .hURL
begin
        pushad

        stdcall StrPtr, [.hURL]
        mov     esi, eax

.start_loop:
        lodsb
        test    al,al
        jz      .relative

        cmp     al, 'A'
        jb      .found
        cmp     al, 'Z'
        jbe     .start_loop

        cmp     al, 'a'
        jb      .found
        cmp     al, 'z'
        jb      .start_loop

.found:
        cmp     al, ':'
        jne     .relative
        cmp     word [esi], '//'
        jne     .relative

        stc
        popad
        return

.relative:
        clc
        popad
        return
endp



cID     text 'id'
cName   text 'name'


; Returns:
;    CF=1 the keyword has been found in this document. eax contains an anchor link.
;    CF=0 the keyword has not been found.

proc SearchKeywordHTML, .hHTML, .hKeyword
begin
        pushad

        stdcall StrPtr, [.hHTML]
        mov     esi, eax

.parse:
        lodsb

        test    al, al
        jz      .not_found

        cmp     al, '<'
        jne     .parse

; tag begin
        lodsb

        cmp     al, '/'
        je      .parse

        or      al, $20
        cmp     al, 'a'
        jne     .parse

;................................................

.anchor:
        lodsb

        test    al, al
        jz      .not_found

        cmp     al, ' '
        ja      .anchor

.anchor2:
        lodsb
        test    al, al
        jz      .not_found

        cmp     al, ' '
        jbe     .anchor2

.start_found:
        dec     esi
        mov     ebx, esi

.search_end:
        lodsb

        test    al, al
        jz      .not_found

        cmp     al, '>'
        jne     .search_end

        mov     ecx, esi
        sub     ecx, ebx
        dec     ecx
        jz      .parse                          ; there is no parameters

        stdcall StrExtract, ebx, 0, ecx
        push    eax
        stdcall SplitTagParams, eax
        stdcall StrDel ; from the stack

        mov     edi, eax        ; TArray with name, value pairs
        xor     ecx, ecx

.param_loop:
        cmp     ecx, [edi+TArray.count]
        je      .end_params

        stdcall StrCompNoCase, [edi+TArray.array+8*ecx], cID
        jc      .indexfound

        stdcall StrCompNoCase, [edi+TArray.array+8*ecx], cName
        jc      .indexfound

        inc     ecx
        jmp     .param_loop

.indexfound:
        stdcall StrDup, [.hKeyword]
        stdcall StrCharCat, eax, '*'
        stdcall StrMatchPatternNoCase, eax, [edi+TArray.array+8*ecx+4]
        stdcall StrDel, eax
        jc      .found

.end_params:
        stdcall FreeParamArray, edi
        jmp     .parse

.found:
        stdcall StrDup, [edi+TArray.array+8*ecx+4]
        stdcall FreeParamArray, edi

        mov     [esp+4*regEAX], eax

        stc
        popad
        return

.not_found:
        clc
        popad
        return
endp



; Returns:
;    CF=1 the keyword has been found in this document. eax contains an anchor link.
;    CF=0 the keyword has not been found.

proc SearchKeywordMarkdown, .hText, .hKeyword
begin
        pushad

        stdcall StrPtr, [.hText]
        mov     esi, eax

.parse:
        lodsb

        cmp     al, $0d
        je      .new_line

        cmp     al, $0a
        je      .new_line

        test    al, al
        jnz     .parse

.not_found:
        clc
        popad
        return

.new_line:
        xor     al, $0d xor $0a
        cmp     byte [esi], al
        jne     @f
        inc     esi
@@:
        cmp     word [esi], '[#' ; start of the markdown anchor.
        jne     .parse


        add     esi, 2
        mov     edi, esi

; search the end:

.end_loop:
        lodsb

        test    al, al
        jz      .not_found

        cmp     al, ']'
        jne     .end_loop


        sub     esi, edi
        dec     esi

        stdcall StrExtract, edi, 0, esi
        mov     edx, eax

        stdcall StrDup, [.hKeyword]
        stdcall StrCharCat, eax, '*'
        stdcall StrMatchPatternNoCase, eax, edx
        stdcall StrDel, eax
        jc      .found

        stdcall StrDel, edx
        add     esi, edi
        jmp     .parse

.found:
        mov     [esp+4*regEAX], edx
        stc
        popad
        return

endp



proc SplitTagParams, .hParamStr
.start dd ?
begin
        pushad
        stdcall CreateArray, 8
        mov     edx, eax

        stdcall StrPtr, [.hParamStr]
        mov     esi, eax
        mov     [.start], eax

.eq_loop:
        lodsb

        test    al, al
        jz      .finish

        cmp     al, '='
        jne     .eq_loop

        stdcall AddArrayItems, edx, 1
        mov     edi, eax

        dec     esi
        mov     ecx, esi

.left:
        dec     ecx
        mov     al, [ecx]
        cmp     al, ' '
        je      .start_found2

        cmp     ecx, [.start]
        je      .start_found
        jmp     .left

.start_found2:
        inc     ecx

.start_found:
        mov     eax, esi
        sub     eax, ecx
        stdcall StrExtract, ecx, 0, eax
        mov     [edi], eax

.search_val:
        lodsb
        test    al, al
        jz      .finish

        cmp     al, '"'
        jne     .search_val

        mov     ecx, esi

.end_loop:
        lodsb
        test    al, al
        jz      .end_found

        cmp     al, '"'
        jne     .end_loop

.end_found:
        push    eax

        mov     eax, esi
        sub     eax, ecx
        dec     eax
        stdcall StrExtract, ecx, 0, eax
        mov     [edi+4], eax

        pop     eax
        test    al, al
        jnz     .eq_loop

.finish:
        mov     [esp+4*regEAX], edx
        popad
        return
endp


proc FreeParamArray, .pArray
begin
        pushf
        pushad

        mov     esi, [.pArray]

        mov     ecx, [esi+TArray.count]
        lea     esi, [esi+TArray.array]
        jecxz   .freemem

.loop:
        stdcall StrDel, [esi]
        stdcall StrDel, [esi+4]
        add     esi, 8
        loop    .loop

.freemem:
        stdcall FreeMem, [.pArray]
        popad
        popf
        return
endp



