module Gargantext.Components.Forest.Tree.Node where

import Data.Array (reverse)
import Data.Maybe (Maybe(..))
import Data.Nullable (null)
import Data.Tuple (snd)
import Data.Tuple.Nested ((/\))
import Effect (Effect)
import Effect.Aff (Aff, launchAff)
import Effect.Class (liftEffect)
import React.SyntheticEvent as E
import Reactix as R
import Reactix.DOM.HTML as H

import Gargantext.Prelude

import Gargantext.AsyncTasks as GAT
import Gargantext.Components.Forest.Tree.Node.Settings (SettingsBox(..), settingsBox)
import Gargantext.Components.Forest.Tree.Node.Action (Action(..))
import Gargantext.Components.Forest.Tree.Node.Action.Upload.Types (FileType(..), UploadFileBlob(..))
import Gargantext.Components.Forest.Tree.Node.Action.Upload (DroppedFile(..), fileTypeView)
import Gargantext.Components.Forest.Tree.Node.Box (nodePopupView)
import Gargantext.Components.Forest.Tree.Node.Box.Types (CommonProps)
import Gargantext.Components.Forest.Tree.Node.Tools.ProgressBar (asyncProgressBar, BarType(..))
import Gargantext.Components.Forest.Tree.Node.Tools.Sync (nodeActionsGraph, nodeActionsNodeList)
import Gargantext.Components.Forest.Tree.Node.Tools (nodeLink)
import Gargantext.Components.GraphExplorer.API as GraphAPI
import Gargantext.Components.Lang (Lang(EN))
import Gargantext.Components.Nodes.Corpus (loadCorpusWithChild)
import Gargantext.Ends (Frontends)
import Gargantext.Hooks.Loader (useLoader)
import Gargantext.Routes as Routes
import Gargantext.Version as GV
import Gargantext.Sessions (Session, sessionId)
import Gargantext.Types (Name, ID)
import Gargantext.Types as GT
import Gargantext.Utils.Popover as Popover
import Gargantext.Utils.Reactix as R2

thisModule :: String
thisModule = "Gargantext.Components.Forest.Tree.Node"

-- Main Node
type NodeMainSpanProps = (
    appReload     :: GT.ReloadS
  , asyncTasks    :: GAT.Reductor
  , currentRoute  :: Routes.AppRoute
  , folderOpen    :: R.State Boolean
  , frontends     :: Frontends
  , id            :: ID
  , isLeaf        :: IsLeaf
  , name          :: Name
  , nodeType      :: GT.NodeType
  , setPopoverRef :: R.Ref (Maybe (Boolean -> Effect Unit))
  | CommonProps
  )

type IsLeaf = Boolean

nodeSpan :: R2.Component NodeMainSpanProps
nodeSpan = R.createElement nodeSpanCpt
  where
    nodeSpanCpt :: R.Component NodeMainSpanProps
    nodeSpanCpt = R.hooksComponentWithModule thisModule "nodeSpan" cpt

    cpt props children = do
      pure $ H.div {} ([ nodeMainSpan props [] ] <> children)

nodeMainSpan :: R2.Component NodeMainSpanProps
nodeMainSpan = R.createElement nodeMainSpanCpt
  where
    nodeMainSpanCpt :: R.Component NodeMainSpanProps
    nodeMainSpanCpt = R.hooksComponentWithModule thisModule "nodeMainSpan" cpt

    cpt props@{ appReload
              , asyncTasks: (asyncTasks /\ dispatchAsyncTasks)
              , currentRoute
              , dispatch
              , folderOpen
              , frontends
              , handed
              , id
              , isLeaf
              , name
              , nodeType
              , session
              , setPopoverRef
              } _ = do
      -- only 1 popup at a time is allowed to be opened
      droppedFile   <- R.useState' (Nothing :: Maybe DroppedFile)
      isDragOver    <- R.useState' false

      popoverRef    <- R.useRef null

      R.useEffect' $ do
        R.setRef setPopoverRef $ Just $ Popover.setOpen popoverRef

      let ordering =
            case handed of
              GT.LeftHanded  -> reverse
              GT.RightHanded -> identity

      let isSelected = Just currentRoute == Routes.nodeTypeAppRoute nodeType (sessionId session) id

      pure $ H.span (dropProps droppedFile isDragOver)
                $ ordering
                [ folderIcon  nodeType folderOpen
                , chevronIcon isLeaf handed nodeType folderOpen
                , nodeLink { frontends
                           , handed
                           , folderOpen
                           , id
                           , isSelected
                           , name: name' props
                           , nodeType
                           , session
                           }

                , fileTypeView { dispatch, droppedFile, id, isDragOver, nodeType }
                , H.div {} (map (\t -> asyncProgressBar { asyncTask: t
                                                       , barType: Pie
                                                       , nodeId: id
                                                       , onFinish: onTaskFinish id t
                                                       , session
                                                       }
                                ) $ GAT.getTasks asyncTasks id
                           )
                , if nodeType == GT.NodeUser
                        then GV.versionView {session}
                        else H.div {} []

                , if showBox then
                        Popover.popover { arrow: false
                                        , open: false
                                        , onClose: \_ -> pure unit
                                        , onOpen:  \_ -> pure unit
                                        , ref: popoverRef } [
                        popOverIcon
                        , mNodePopupView props (onPopoverClose popoverRef)
                        ]
                else H.div {} []

                , nodeActions { id
                              , nodeType
                              , session
                              , triggerRefresh: const $ dispatch RefreshTree
                              }


                ]
        where
          onTaskFinish id t _ = do
            dispatchAsyncTasks $ GAT.Finish id t
            snd appReload $ (_ + 1)

          SettingsBox {show: showBox} = settingsBox nodeType
          onPopoverClose popoverRef _ = Popover.setOpen popoverRef false

          name' {name, nodeType} = if nodeType == GT.NodeUser then show session else name

          mNodePopupView props@{id, nodeType} onPopoverClose =
                                nodePopupView { dispatch
                                              , handed : props.handed
                                              , id
                                              , name: name' props
                                              , nodeType
                                              , onPopoverClose
                                              , session
                                              }

    chevronIcon true handed' nodeType (open /\ setOpen) = H.div {} []
    chevronIcon false handed' nodeType (open /\ setOpen) =
      H.a { className: "chevron-icon"
          , on: { click: \_ -> setOpen $ not }
          }
        [ H.i { className: if open
                           then "fa fa-chevron-down"
                           else if handed' == GT.RightHanded
                                   then "fa fa-chevron-right"
                                   else "fa fa-chevron-left"
                } [] ]

    folderIcon nodeType (open /\ setOpen) =
      H.a { className: "folder-icon"
          , on: { click: \_ -> setOpen $ not }
          } [
              H.i {className: GT.fldr nodeType open} []
            ]

    popOverIcon = H.a { className: "settings fa fa-cog" 
                      , title : "Each node of the Tree can perform some actions.\n"
                             <> "Click here to execute one of them."
                      } []

    dropProps droppedFile isDragOver =
      { className: "leaf " <> (dropClass droppedFile isDragOver)
      , on: { drop: dropHandler droppedFile
            , dragOver: onDragOverHandler isDragOver
            , dragLeave: onDragLeave isDragOver }
      }
      where
        dropClass   (Just _  /\ _)        _          = "file-dropped"
        dropClass    _                   (true /\ _) = "file-dropped"
        dropClass   (Nothing /\ _)        _          = ""
        dropHandler (_ /\ setDroppedFile) e          = do
          -- prevent redirection when file is dropped
          E.preventDefault e
          E.stopPropagation e
          blob <- R2.dataTransferFileBlob e
          void $ launchAff do
            --contents <- readAsText blob
            liftEffect $ setDroppedFile
                       $ const
                       $ Just
                       $ DroppedFile { blob: (UploadFileBlob blob)
                                     , fileType: Just CSV
                                     , lang    : EN
                                     }
    onDragOverHandler (_ /\ setIsDragOver) e = do
      -- prevent redirection when file is dropped
      -- https://stackoverflow.com/a/6756680/941471
      E.preventDefault e
      E.stopPropagation e
      setIsDragOver $ const true
    onDragLeave (_ /\ setIsDragOver) _ = setIsDragOver $ const false

{-
fldr nt open = if open
               then "fa fa-globe" -- <> color nt
               else "fa fa-folder-globe" -- <> color nt
               --else "glyphicon glyphicon-folder-close" <> color nt
                 where
                   color GT.NodeUser     = ""
                   color FolderPublic = ""
                   color FolderShared = " text-warning"
                   color _            = " text-danger"
-}


-- START nodeActions

type NodeActionsProps =
  ( id          :: ID
  , nodeType    :: GT.NodeType
  , session     :: Session
  , triggerRefresh :: Unit -> Aff Unit
  )

nodeActions :: Record NodeActionsProps -> R.Element
nodeActions p = R.createElement nodeActionsCpt p []
  where
    nodeActionsCpt :: R.Component NodeActionsProps
    nodeActionsCpt = R.hooksComponentWithModule thisModule "nodeActions" cpt

    cpt { id
        , nodeType: GT.Graph
        , session
        , triggerRefresh
        } _ = do

      useLoader id (graphVersions session) $ \gv ->
        nodeActionsGraph { id
                         , graphVersions: gv
                         , session
                         , triggerRefresh
                         }

    cpt { id
        , nodeType: GT.NodeList
        , session
        , triggerRefresh
        } _ = do
      useLoader { nodeId: id, session } loadCorpusWithChild $
        \{ corpusId } ->
          nodeActionsNodeList { listId: id
                              , nodeId: corpusId
                              , nodeType: GT.TabNgramType GT.CTabTerms
                              , session
                              , triggerRefresh
                              }
    cpt _ _ = do
      pure $ H.div {} []

    graphVersions session graphId = GraphAPI.graphVersions { graphId, session }


-- END nodeActions