module Gargantext.Components.PhyloExplorer.Layout
  ( layout
  ) where

import Gargantext.Prelude

import DOM.Simple (document, querySelector, window)
import Data.Either (Either(..))
import Data.Foldable (for_, intercalate)
import Data.Int as Int
import Data.Maybe (Maybe(..))
import Data.String (null)
import Effect (Effect)
import FFI.Simple ((..), (.=))
import Gargantext.Components.Bootstrap as B
import Gargantext.Components.PhyloExplorer.Frame.DocFocus (docFocus)
import Gargantext.Components.PhyloExplorer.Resources (PubSubEvent(..))
import Gargantext.Components.PhyloExplorer.Resources as RS
import Gargantext.Components.PhyloExplorer.SideBar (sideBar)
import Gargantext.Components.PhyloExplorer.Store as PhyloStore
import Gargantext.Components.PhyloExplorer.ToolBar (toolBar)
import Gargantext.Components.PhyloExplorer.TopBar (topBar)
import Gargantext.Components.PhyloExplorer.Types (DisplayView, ExtractedCount, FrameDoc, PhyloData(..), TabView(..), Term, sortSources)
import Gargantext.Hooks.FirstEffect (useFirstEffect')
import Gargantext.Hooks.Session (useSession)
import Gargantext.Hooks.UpdateEffect (useUpdateEffect1', useUpdateEffect3')
import Gargantext.Types (SidePanelState(..))
import Gargantext.Utils (getter, (?))
import Gargantext.Utils.Reactix as R2
import Graphics.D3.Base (d3)
import Reactix as R
import Reactix.DOM.HTML as H
import Simple.JSON as JSON
import Toestand as T

here :: R2.Here
here = R2.here "Gargantext.Components.PhyloExplorer"

layout :: R2.Leaf ()
layout = R2.leaf layoutCpt

layoutCpt :: R.Component ()
layoutCpt = here.component "layout" cpt where
  cpt _ _ = do
    -- | States
    -- |
    { isBuilt
    , source
    , sources
    , terms
    , toolBarDisplayed
    , search
    , result
    , displayView
    , isIsolineDisplayed
    , sideBarDisplayed
    , sideBarTabView
    , extractedTerms
    , selectedTerm
    , selectedBranch
    , selectedSource
    , extractedCount
    , phyloId
    , phyloData
    , frameDoc
    } <- PhyloStore.use

    (PhyloData o)       <- R2.useLive' phyloData
    phyloId'            <- R2.useLive' phyloId
    sources'            <- R2.useLive' sources
    terms'              <- R2.useLive' terms
    displayView'        <- R2.useLive' displayView
    isIsolineDisplayed' <- R2.useLive' isIsolineDisplayed
    sideBarDisplayed'   <- R2.useLive' sideBarDisplayed
    toolBarDisplayed'   <- R2.useLive' toolBarDisplayed
    isBuilt'            <- R2.useLive' isBuilt
    selectedTerm'       <- R2.useLive' selectedTerm
    selectedBranch'     <- R2.useLive' selectedBranch
    selectedSource'     <- R2.useLive' selectedSource
    frameDoc'           <- R2.useLive' frameDoc

    session <- useSession

    -- | Hooks
    -- |
    let topBarPortalKey = "portal-topbar::" <> show phyloId'

    mTopBarHost <- R.unsafeHooksEffect $ R2.getElementById "portal-topbar"

    -- | Behaviors
    -- |

    -- (!) do not rely on the JavaScript `Resources.js:resetSelection`,
    --     as it will lead to potential circular computations
    --
    -- (!) same idea for Box content mutation: eg. due the reset notion
    --     triggered at each selection (cf. `resetSelection`), a call via
    --     `T.write` + `T.listen` will also create an infinite loop
    let

      resetSelection :: Unit -> Effect Unit
      resetSelection _ = do
        T.write_ Nothing selectedBranch
        T.write_ Nothing selectedTerm
        T.write_ ""      source
        T.write_ Nothing selectedSource
        T.write_ Nothing extractedCount
        T.write_ mempty  extractedTerms

        void $ pure $ (window .= "branchFocus") []

      changeViewCallback :: DisplayView -> Effect Unit
      changeViewCallback =
            flip T.write displayView
        >=> RS.changeDisplayView

      sourceCallback :: String -> Effect Unit
      sourceCallback id = do
        -- (!) upcoming value from a `B.formSelect`, so simple <String> format
        let mSource = RS.findSourceById sources' =<< Int.fromString id
        let mLabel  = pure <<< getter _.label =<< mSource

        resetSelection unit
        T.write_ id source
        T.write_ mLabel selectedSource
        RS.selectSource window mSource

      searchCallback :: String -> Effect Unit
      searchCallback =
            flip T.write search
        >=> RS.autocompleteSearch terms'
        >=> flip T.write_ result

      resultCallback :: Maybe Term -> Effect Unit
      resultCallback mTerm =
            resetSelection unit
        *> RS.autocompleteSubmit displayView mTerm

      unselectCallback :: Unit -> Effect Unit
      unselectCallback _ =
            resetSelection unit
        *> RS.doubleClick

      selectTermCallback :: String -> Effect Unit
      selectTermCallback s = do
        resetSelection unit
        mTerm <- RS.autocompleteSearch terms' s
        RS.autocompleteSubmit displayView mTerm

      closeDocCallback :: Unit -> Effect Unit
      closeDocCallback _ = T.write_ Nothing frameDoc

    -- | Effects
    -- |

    -- Drawing the phylo
    useFirstEffect' do
      (sortSources >>> flip T.write_ sources) o.sources
      RS.setGlobalD3Reference window d3
      RS.setGlobalDependencies window (PhyloData o)
      RS.drawPhylo
        o.branches
        o.periods
        o.groups
        o.links
        o.ancestorLinks
        o.branchLinks
        o.bb
      RS.changeDisplayView displayView'
      T.write_ true isBuilt
      -- @NOTE #219: handling global variables
      --             (see `Resources.js` how they are being used)
      T.write_ (window .. "terms") terms

    -- (see `Gargantext.Components.PhyloExplorer.Resources` > JavaScript >
    -- `pubsub` for detailed explanations)
    useFirstEffect' do
      RS.subscribe (show ExtractedTermsEvent) $ flip T.write_ extractedTerms

      RS.subscribe (show SelectedTermEvent) $ case _ of
        res
          | true == null res -> T.write_ Nothing selectedTerm
          | otherwise        -> T.write_ (Just res) selectedTerm

      RS.subscribe (show SelectedBranchEvent) $ case _ of
        res
          | true == null res -> T.write_ Nothing selectedBranch
          | otherwise        -> T.write_ (Just res) selectedBranch

      RS.subscribe (show SelectedSourceEvent) $ sourceCallback

      RS.subscribe (show DisplayViewEvent) $ read >>> case _ of
        Nothing  -> R.nothing
        Just res -> T.write_ res displayView

      RS.subscribe (show ExtractedCountEvent) $ JSON.readJSON >>> case _ of
        Left _ ->
          T.write_ Nothing extractedCount
        Right (res :: ExtractedCount) ->
          T.write_ (Just res) extractedCount

    R.useEffect1' isIsolineDisplayed' do
      mEl <- querySelector document ".phylo-isoline"
      for_ mEl \el -> do
        let style = el .. "style"
        pure $ (style .= "display") $ isIsolineDisplayed' ?
          "flex" $
          "none"

    -- @NOTE #219: handling global variables (eg. via `window`)
    --             (see `Resources.js` how they are being used)
    useUpdateEffect1' displayView' do
      pure $ (window .= "displayView") (show displayView')

    -- Educational behavior: automatically opening sidebar on first selection
    useUpdateEffect3'
      selectedTerm'
      selectedBranch'
      selectedSource' $
        when (sideBarDisplayed' == InitialClosed) do
          T.write_ Opened sideBarDisplayed
          T.write_ SelectionTab sideBarTabView


    -- | Render
    -- |

    pure $

      H.div
      { className: intercalate " "
          [ "phylo"
          , not isBuilt' ? "phylo--preloading" $ ""
          ]
      }
      [
        -- Preloading spinner
        R2.when (not isBuilt') $

          H.div
          { className: "phylo__spinner-wrapper" }
          [
            B.spinner
            { className: "phylo__spinner" }
          ]
      ,
        -- Topbar
        R2.createPortal' mTopBarHost
        [
          R2.fragmentWithKey topBarPortalKey
          [
            R2.when (isBuilt') $
              topBar
              { sourceCallback
              , searchCallback
              , resultCallback
              }
          ]
        ]
      ,
        -- Sidebar + Focus frame
        H.div
        { className: "phylo__frame" }
        [
          -- Doc focus
          R2.fromMaybe frameDoc' \(frameDoc_ :: FrameDoc) ->

            H.div
            { className: "phylo__focus" }
            [
              H.div
              { className: "phylo__focus__inner" }
              [
                docFocus
                { session
                , frameDoc: frameDoc_
                , closeCallback: closeDocCallback
                , key: show $ getter _.docId frameDoc_
                }
              ]
            ]
        ,
          -- Sidebar
          H.div
          { className: "phylo__sidebar"
          -- @XXX: ReactJS lack of "keep-alive" feature workaround solution
          -- @link https://github.com/facebook/react/issues/12039
          , style: { display: sideBarDisplayed' == Opened ? "block" $ "none" }
          }
          [
            H.div
            { className: "phylo__sidebar__inner" }
            [
              sideBar
              { selectTermCallback }
            ]
          ]
        ]
      ,
        -- Toolbar
        R2.when (toolBarDisplayed') $
          toolBar
          { resetViewCallback : const RS.resetView
          , exportCallback    : const RS.exportViz
          , unselectCallback  : unselectCallback
          , changeViewCallback
          }
      ,
        -- Iso Line
        H.div
        { className: "phylo-isoline"}
        []
      ,
        -- (?) prefer div "margin" instead of CSS margin, it will ease
        --     some computation made on the scape and peak
        H.div
        { className: "phylo-margin" }
        []
      ,
        -- Phylo Grid
        H.div
        { className: "phylo-grid" }
        [
          H.div
          { className: "phylo-grid__blueprint" }
          [
            H.div
            { className: "phylo-grid__blueprint__left" }
            []
          ,
            H.div
            { className: "phylo-grid__blueprint__center" }
            []
          ,
            H.div
            { className: "phylo-grid__blueprint__right"}
            []
          ]
        ,
          H.div
          { className: "phylo-grid__content" }
          [
            H.div
            { className: "phylo-grid__content__scape" }
            []
          ]
        ]
      ,
        -- (?) prefer div "margin" instead of CSS margin, it will ease
        --     some computation made on the scape and peak
        H.div
        { className: "phylo-margin" }
        []
      ]