module Gargantext.Utils.Selection where

import Prelude
import Data.Maybe (Maybe, maybe)
import Data.Nullable (Nullable, toMaybe)
import Data.Tuple (Tuple(..))
import DOM.Simple.Types (Element, DOMRect)
import DOM.Simple.Element as Element
import Effect (Effect)
import FFI.Simple ((.?), (..), (...))

-- | Represents a text selection
foreign import data Selection :: Type
-- | Represents a single selection range
foreign import data Range :: Type

-- Terminology:
  -- Anchor: point at which the selection was started
  -- Focus: point at which the selection ends

-- | The Node in which the anchor lies
anchorNode :: Selection -> Maybe Element
anchorNode s = s .? "anchorNode"

-- | The Node in which the focus lies
focusNode :: Selection -> Maybe Element
focusNode s = s .? "focusNode"

-- | Whether the anchor and focus are at the same point
isSelectionCollapsed :: Selection -> Boolean
isSelectionCollapsed s = s .. "isCollapsed"

rangeCount :: Selection -> Int
rangeCount s = s .. "rangeCount"

getRange :: Selection -> Int -> Effect Range
getRange s i = pure $ s ... "getRangeAt" $ [i]

-- | Renders a selection or range as a string
selectionToString :: Selection -> String
selectionToString s = s ... "toString" $ []

-- | Renders a range as a string
rangeToString :: Range -> String
rangeToString s = s ... "toString" $ []

--- | Convert range to an offset tuple
rangeToTuple :: Range -> Tuple Int Int
rangeToTuple r = Tuple (r .. "startOffset") (r .. "endOffset")

-- | Whether the anchor and focus are at the same point
isRangeCollapsed :: Range -> Boolean
isRangeCollapsed r = r .. "isCollapsed"

cloneRange :: Range -> Range
cloneRange r = r ... "cloneRange" $ []

collapseRange :: Range -> Boolean -> Effect Unit
collapseRange r toStart = pure $ r ... "collapse" $ [toStart]

commonAncestorContainer :: Range -> Element
commonAncestorContainer r = r .. "commonAncestorContainer"

insertNode :: Range -> Element -> Effect Unit
insertNode r e = pure $ r ... "insertNode" $ [e]

boundingRect :: Range -> DOMRect
boundingRect r = r ... "getBoundingClientRect" $ []

-- getSelection

-- | Fetches the current text selection, if any
getSelection :: Effect (Maybe Selection)
getSelection = toMaybe <$> _getSelection

foreign import _getSelection :: Effect (Nullable Selection)

-- | Are both the start and end of the selection contained within an Element
doesSelectionLieWithin :: Selection -> Element -> Boolean
doesSelectionLieWithin sel elem = test anchorNode && test focusNode
  where
    test :: (Selection -> Maybe Element) -> Boolean
    test f = maybe false (Element.contains elem) (f sel)