module Gargantext.Components.GraphExplorer.Utils
  ( stEdgeToGET, stNodeToGET
  , normalizeNodes
  , normalizeNodeSize
  , takeGreatestNodeByCluster, countNodeByCluster
  ) where

import Gargantext.Prelude

import Data.Array as A
import Data.Foldable (maximum, minimum)
import Data.Lens (Lens', lens, over, traversed, (^.))
import Data.Int (floor, toNumber)
import Data.Maybe (Maybe(..), fromMaybe, maybe)
import Data.Newtype (wrap)
import Data.Sequence as Seq
import Data.Traversable (class Traversable)
import Gargantext.Components.GraphExplorer.GraphTypes as GEGT
import Gargantext.Components.GraphExplorer.Types as GET
import Gargantext.Hooks.Sigmax.Types as ST
import Gargantext.Utils (getter)
import Gargantext.Utils.Lens as GUL
import Gargantext.Utils.Seq as GUS

stEdgeToGET :: Record ST.Edge -> GEGT.Edge
stEdgeToGET { _original } = _original

stNodeToGET :: Record ST.Node -> GEGT.Node
stNodeToGET { id, label, x, y, _original: GEGT.Node { attributes, size, type_ } } = GEGT.Node {
    attributes
  , children: []
  , id_: id
  , label
  , size
  , type_
  , x
  , y
  }

-----------------------------------------------------------------------

-- | Normalize nodes, i.e. set their {x, y} values so that they are in
-- | range [0, 1].
normalizeNodes :: forall t. Traversable t => t GEGT.Node -> t GEGT.Node
normalizeNodes ns = GUL.normalizeLens xLens $ GUL.normalizeLens yLens ns
  where
    xLens :: Lens' GEGT.Node Number
    xLens = lens (\(GEGT.Node { x }) -> x) $ (\(GEGT.Node n) val -> GEGT.Node (n { x = val }))
    yLens :: Lens' GEGT.Node Number
    yLens = lens (\(GEGT.Node { y }) -> y) $ (\(GEGT.Node n) val -> GEGT.Node (n { y = val }))

normalizeNodeSize :: forall t. Traversable t => Int -> Int -> t GEGT.Node -> t GEGT.Node
normalizeNodeSize minSize maxSize ns = over traversed (over sizeLens (\s -> toNumber minSize + (s - sizeMin') * quotient)) ns
  where
    sizes = over traversed (_ ^. sizeLens) ns
    sizeMin = minimum sizes
    sizeMax = maximum sizes
    range = do
      sMin <- sizeMin
      sMax <- sizeMax
      pure $ sMax - sMin
    sizeMin' = fromMaybe 0.0 sizeMin
    divisor = maybe 1.0 (\r -> 1.0 / r) range
    quotient :: Number
    quotient = (toNumber $ maxSize - minSize) * divisor
    sizeLens :: Lens' GEGT.Node Number
    sizeLens = lens (\(GEGT.Node { size }) -> toNumber size) $ (\(GEGT.Node n) val -> GEGT.Node (n { size = floor val }))

------------------------------------------------------------------------

takeGreatestNodeByCluster :: GET.HyperdataGraph -> Int -> Int -> Array GEGT.Node
takeGreatestNodeByCluster graphData take clusterId
  =   graphData
  #   getter _.graph
  >>> getter _.nodes
  >>> A.filter
      (   getter _.attributes
      >>> getter _.clustDefault
      >>> eq clusterId
      )
  >>> A.sortWith
      ( getter _.size
      )
  >>> A.takeEnd take
  >>> A.reverse

countNodeByCluster :: GET.HyperdataGraph -> Int -> GEGT.ClusterCount
countNodeByCluster graphData clusterId
  =   graphData
  #   getter _.graph
  >>> getter _.nodes
  >>> A.filter
      (   getter _.attributes
      >>> getter _.clustDefault
      >>> eq clusterId
      )
  >>> A.length
  >>> { id:  clusterId
      , count: _
      }
  >>> wrap