/*
 * Xailer source code:
 *
 * XHtmlHelpView.prg
 * Visor de HTML Help System
 *
 * Copyright 2022 Jose F. Gimenez
 * Copyright 2022 Xailer.com
 * All rights reserved
 */

#include "Xailer.ch"
#include "CommCtrl.api"

#define NO_WEB "TWebView, nor TWebBrowser were linked with your App. " + ;
               "HtmlHelpView requires at least one of them. Be aware that:"+ CRLF + CRLF +;
               "TWebView is only available in Xailer Enterprise" + CRLF + ;
               "TWebBrowser is only available in Xailer Professional and Enterprise" + CRLF + ;
               CRLF + "Put one of this two lines at the beginning on any of your PRG files:" + CRLF +;
               "REQUEST TWebView" + CRLF + ;
               "REQUEST TWebBrowser" + CRLF + ;
               CRLF + "IMPORTANT" + CRLF + "HtmlHelpView is not available on Xailer Personal" + CRLF + ;
               "TWebView requires the WebView2Loader.dll"

STATIC oHelpView
STATIC cHelpFile
STATIC cWndPos

//------------------------------------------------------------------------------

FUNCTION XHtmlHelpView( cFile, cId, cTopic, lModal, lShowTopics )

   DEFAULT lModal TO .F.
   DEFAULT lShowTopics TO .T.

   IF Empty( oHelpView ) .OR. !( cFile == cHelpFile )
      IF !Empty( oHelpView )
         oHelpView:End()
      ENDIF
      cHelpFile := cFile
      oHelpView := TXHtmlHelpView():New( Application:oMainForm )
      oHelpView:Load( cFile )
   ENDIF

   WITH OBJECT oHelpView
      :ShowItem( cId, cTopic )

      :oTopics :lVisible := lShowTopics
      :oSplitter :lVisible := lShowTopics
      IF !Empty( cWndPos ) .AND. !:lVisible
         :RestoreState( cWndPos )
      ENDIF

      IF lModal
         :ShowModal()
      ELSE
         :Show()
      ENDIF
   END

RETURN oHelpView

//------------------------------------------------------------------------------

CLASS XXHtmlHelpView FROM TForm

   COMPONENT oTopics
   COMPONENT oSplitter
   COMPONENT oHeader
   COMPONENT oTitle
   COMPONENT oContent

   CLASSDATA lWebView      INIT .F.
   CLASSDATA nPanelWidth   INIT 260
   CLASSDATA nHelpWidth    INIT 740
   CLASSDATA nHelpHeight   INIT 700
   CLASSDATA cStyle        INIT "body {font-family: arial, sans-serif; font-size: 10pt;}" + CRLF + ;
                                '.note-tip {background: no-repeat url("res://[[exe]]/PNG/XA_PNG_TIP"); background-position: 8px 8px; background-color: #ddffdd; margin: 0.25em auto; padding: 12px 18px 12px 48px; max-width: 80%; min-height: 24px; vertical-align: middle;border-radius: 8px;color: black;overflow: hidden;}' + CRLF + ;
                                '.note-info {background: no-repeat url("res://[[exe]]/PNG/XA_PNG_INFO"); background-position: 8px 8px; background-color: #e0f0ff; margin: 0.25em auto; padding: 12px 18px 12px 48px; max-width: 80%; min-height: 24px; vertical-align: middle;border-radius: 8px;color: black;overflow: hidden;}' + CRLF + ;
                                '.note-warning {background: no-repeat url("res://[[exe]]/PNG/XA_PNG_WARNING"); background-position: 8px 8px; background-color: #ffd0d0; margin: 0.25em auto; padding: 12px 18px 12px 48px; max-width: 80%; min-height: 24px; vertical-align: middle;border-radius: 8px;color: black;overflow: hidden;}'

   PROPERTY cFile          INIT ""

   METHOD CreateForm()
   METHOD FormInitialize( oSender )
   METHOD TopicsSelChanged( oSender, nAction, hOldItem, hNewItem )
   METHOD WebResourceRequested( oSender, cUri, cData, cHeaders, cPostData, cMethod )
   METHOD NewWindow3( oSender, dispppDisp, lCancel, ndwFlags, cbstrUrlContext, cbstrUrl )
   METHOD BeforeNavigate2( oSender, disppDisp, vURL, vFlags, vTargetFrameName, vPostData, vHeaders, lCancel )
   METHOD Load( cFile, cId, cTopic )
   METHOD ShowItem( cId, cTopic )
   METHOD LocateIndex()

   METHOD WMHelp()   INLINE 0
   METHOD WMClose()  INLINE cWndPos := ::SaveState(), oHelpView := Nil
   METHOD Disable()  INLINE .T.

PROTECTED:
   DATA cContent

ENDCLASS

//------------------------------------------------------------------------------

METHOD CreateForm() CLASS XXHtmlHelpView

   ::cText := "Ayuda"
   ::SetClientSize( ::nPanelWidth + 5 + ::nHelpWidth, ::nHelpHeight )
   ::lCentered := .T.
   ::OnInitialize := "FormInitialize"
   ::Create()

   WITH OBJECT ::oTopics := TTreeView():New( Self )
      :SetBounds( 0, 0, ::nPanelWidth, ::nHelpHeight )
      :nAlign := alLEFT
      :nClrSelItem := 0xE0E0E0
      :Create()
   END

   WITH OBJECT ::oSplitter := TSplitter():New( Self )
      :SetBounds( ::nPanelWidth, 0, 5, ::nHelpHeight )
      :nAlign := alLEFT
      :Create()
   END

   WITH OBJECT ::oHeader := TBevel():New( Self )
      :nAlign := alTOP
      :nHeight := 44
      :nBorderStyle := bvNONE
      :Create()
   END

   WITH OBJECT ::oTitle := TLabel():New( ::oHeader )
      :SetBounds( 8, 8 )
      :oFont := TFont():Create( "Arial", 18,, 700 )
      :nClrText := clNavy
      :Create()
   END

   IF ::lWebView .AND. XA_FunctionExists( "TWebView" )
      WITH OBJECT ::oContent := XA_CallFunction( "TWebView" ):New( Self )
         :SetBounds( ::nPanelWidth + 5, 0, 100, 100 )
         :nAlign := alCLIENT
         :Create()
      END
      IF !::oContent:IsInstalled()
         ::oContent:End()
         ::oContent := Nil
         ::lWebView := .F.
         LogDebug( "TWebView requires the WebView2Loader.dll" )
      ENDIF
   ENDIF

   IF Empty( ::oContent ) .AND. XA_FunctionExists( "TWebBrowser" )
      WITH OBJECT ::oContent := XA_CallFunction( "TWebBrowser" ):New( Self )
         :SetBounds( ::nPanelWidth + 5, 0, 100, 100 )
         :nAlign := alCLIENT
         :Create()
      END
   ENDIF

   IF Empty( ::oContent )
      WITH OBJECT TLabel():New( SelF )
         :nAlign := alCLIENT
         :nVAlignment := vaCENTER
         :nAlignment := taCENTER
         :lMultiLine := .T.
         :nAlignMarginLeft := 10
         :nAlignMarginRight := 10
         :cText := NO_WEB
         :Create()
      END
   ENDIF

RETURN Self

//------------------------------------------------------------------------------

METHOD FormInitialize( oSender ) CLASS XXHtmlHelpView

   IF ::oContent == NIL
      RETURN NIL
   ENDIF

   ::oTopics:OnSelChanged := "TopicsSelChanged"
   WITH OBJECT ::oContent
      IF :IsKindOf( "TWebView" )
         :OnWebResourceRequested := "WebResourceRequested"
      ELSE
         :NewWindow2 := {|oSender, dispppDisp, lCancel| lCancel := .T. }
         :NewWindow3 := "NewWindow3"
         :BeforeNavigate2 := "BeforeNavigate2"
         :Navigate( "about:blank" )
      ENDIF
   END

RETURN Nil

//------------------------------------------------------------------------------

METHOD TopicsSelChanged( oSender, nAction, hOldItem, hNewItem ) CLASS XXHtmlHelpView

   LOCAL oItem

   IF !Empty( nAction ) // NOTA: Solo si lo provoca el usuario [JFG]
      IF !Empty( oItem := ::oTopics:GetSelectedItem() )
         ::ShowItem( oItem:Cargo )
      ENDIF
   ENDIF

RETURN Nil

//------------------------------------------------------------------------------

METHOD WebResourceRequested( oSender, cUri, cData, cHeaders, cPostData, cMethod ) CLASS XXHtmlHelpView

   LOCAL hRes, n

   IF ( n := At( ".exe/PNG/XA_PNG_", cUri ) ) > 0
      IF !Empty( hRes := FindResource( 0, SubStr( cUri, n + 9 ), "PNG" ) )
         cData := LoadResource( 0, hRes )
      ENDIF
   ELSE
      cData := ::cContent
      ::cContent := Nil
   ENDIF

RETURN Nil

//------------------------------------------------------------------------------

METHOD NewWindow3( oSender, dispppDisp, lCancel, ndwFlags, cbstrUrlContext, cbstrUrl ) CLASS XXHtmlHelpView

   ::BeforeNavigate2( oSender, dispppDisp, cbstrUrl )
   lCancel := .T.

RETURN Nil

//------------------------------------------------------------------------------

METHOD BeforeNavigate2( oSender, disppDisp, vURL, vFlags, vTargetFrameName, vPostData, vHeaders, lCancel ) CLASS XXHtmlHelpView

   IF Left( vURL, 7 ) == "help://"
      vURL := Substr( vURL, 8 )
      IF Right( vURL, 1 ) == "/"
         vURL := Left( vURL, Len( vURL ) - 1 )
      ENDIF
      lCancel := .T.
      ::ShowItem( vURL )
   ENDIF

RETURN Nil

//------------------------------------------------------------------------------

METHOD Load( cFile, cId, cTopic ) CLASS XXHtmlHelpView

   LOCAL oItem, aItem, oTopic, nLevel := 1, nNewLevel

   UPDATE ::cFile TO cFile

   // Cargar el treeview
   oItem := ::oTopics
   FOR EACH aItem IN XHtmlHelpTopics( ::cFile )
      IF Left( aItem[ 2 ], 1 ) == "*"
         aItem[ 2 ] := SubStr( aItem[ 2 ], 2 )
         IF Left( aItem[ 2 ], 1 ) != "*"
            LOOP
         ENDIF
      ENDIF
      IF ( nNewLevel := Len( aItem[ 1 ] ) - Len( LTrim( aItem[ 1 ] ) ) ) > 0
         IF nNewLevel > nLevel
            oItem := ATail( oItem:aItems )
         ELSEIF nNewLevel < nLevel
            oItem := oItem:oParent
         ENDIF
         nLevel := nNewLevel
         oItem:AddItem( aItem[ 2 ] ):Cargo := LTrim( aItem[ 1 ] )
         IF ( !Empty( cId ) .AND. cId == LTrim( aItem[ 1 ] ) ) .OR. ( !Empty( cTopic ) .AND. cTopic == aItem[ 2 ] )
            oTopic := oItem
         ENDIF
      ENDIF
   NEXT
   ::oTopics:ExpandAll()

   // Seleccionar el item inicial
   IF !Empty( oTopic )
      oTopic:Select()
   ELSEIF !Empty( ::oTopics:aItems )
      ::oTopics:aItems[ 1 ]:Select()
   ENDIF

RETURN Nil

//------------------------------------------------------------------------------

METHOD ShowItem( cId, cTopic ) CLASS XXHtmlHelpView

   LOCAL cText, cAnchor := "", n, oElement, cStyle

   IF Empty( cId ) .AND. Empty( cTopic )
      IF !Empty( ::oTopics:aItems )
         cId := ::oTopics:aItems[ 1 ]:Cargo
      ENDIF
   ENDIF
   IF ( n := At( "#", cId ) ) > 0
      cAnchor := Substr( cId, n + 1 )
      cId := Left( cId, n - 1 )
   ENDIF
   cText := XHtmlHelpTopic( ::cFile, cId, @cTopic )
   IF !Empty( cTopic ) .AND. Left( cTopic, 1 ) == "*"
      cTopic := SubStr( cTopic, 2 )
   ENDIF
   ::oTitle:cText := cTopic
   ::oHeader:lVisible := !Empty( cTopic )
   IF Empty( cText ) .AND. Empty( cTopic )
      cText := LT( XA_MSG_HELP_NOT_FOUND )
   ELSEIF !Empty( cAnchor )
      IF ( n := At( ">" + Lower( cAnchor ) + "<", Lower( cText ) ) ) > 0
         cText := Left( cText, n - 1 ) + " id='anchor'" + SubStr( cText, n )
      ENDIF
   ENDIF
   IF ::oContent != NIL
      cStyle := StrTran( ::cStyle, "[[exe]]", Lower( FileShortName( Application:cFileName ) ) + ".exe" )
      IF ::oContent:IsKindOf( "TWebBrowser" )
         WITH OBJECT ::oContent
            WHILE !Empty( :OCX ) .AND. :ReadyState != 4
               ProcessMessages( 50 )
            ENDDO
            :Document:CreateStyleSheet( "" ):cssText := cStyle
            :Document:body:innerHtml := cText
            IF !Empty( cAnchor )
               oElement := :Document:getElementById( "anchor" )
               IF !Empty( oElement )
                  oElement:scrollIntoView( .T. )
               ENDIF
            ENDIF
            IF Empty( oElement )
               :Document:parentWindow:scroll(0,0)
            ENDIF
         END
      ELSE
         ::oContent:SetContent( "<body>&nbsp;</body>" )
         cText := "<html><head><style>" + cStyle + "</style></head><body" + IIF( Empty( cAnchor ), "", e" onload=\"document.getElementById(\'anchor\').scrollIntoView();\">" ) + ">" + cText + "</body></html>"
         IF !Empty( ::oContent:SetContent( cText ) )
            // NOTA: Algunas paginas dan error al cargarlas con SetContent(), y entonces hay que usar Navigate() y cargarlas desde OnWebResourceRequested
            ::cContent := cText
            ::oContent:Navigate( "http://0.0.0.0/" + StringToHex( cId ) )
         ENDIF
      ENDIF
   ENDIF
   IF !::LocateIndex()
      ::oTopics:SendMsg( TVM_SELECTITEM, 0, 0 )
   ENDIF

RETURN Nil

//------------------------------------------------------------------------------

METHOD LocateIndex( oItem, cTopic ) CLASS XXHtmlHelpView

   LOCAL o

   DEFAULT oItem TO ::oTopics
   DEFAULT cTopic TO ::oTitle:cText

   FOR EACH o IN oItem:aItems
      IF o:cText == cTopic
         o:Select()
         o:EnsureVisible()
         RETURN .T.
      ELSEIF !Empty( o:aItems )
         IF ::LocateIndex( o, cTopic )
            RETURN .T.
         ENDIF
      ENDIF
   NEXT

RETURN .F.

//------------------------------------------------------------------------------
