module Gargantext.Components.Charts.Options.Series where

import Data.Array (foldl)
import Data.Generic.Rep (class Generic)
import Data.Maybe (Maybe(..), maybe)
import Data.Newtype (class Newtype)
import Data.Symbol (SProxy(..))
import Gargantext.Components.Charts.Options.Data (DataD1, DataD2)
import Gargantext.Components.Charts.Options.Font (ItemStyle, Tooltip)
import Gargantext.Components.Charts.Options.Legend (SelectedMode)
import Gargantext.Types (class Optional)
import Prelude (class Eq, class Show, bind, map, pure, show, ($), (+), (<<<), (<>))
import Record      as Record
import Record.Unsafe (unsafeSet)
import Simple.JSON as JSON
import Unsafe.Coerce (unsafeCoerce)


newtype SeriesType = SeriesType String

type SeriesName = String


data Chart = Line
           | Bar | PictorialBar
           | Pie
           | Scatter | EffectScatter
           | Radar
           | Trees
           | Sunburst
           | Boxplot
           | Candlestick
           | Heatmap
           | Map
           | Parallel
           | Lines
           | Graph
           | Sankey
           | Funnel
           | Gauge
           | ThemeRiver
-- Trees

instance Show Chart where
  show Bar      = "bar"
  show EffectScatter = "effectScatter" -- ^ https://ecomfe.github.io/echarts-examples/public/editor.html?c=scatter-effect
  show Funnel   = "funnel"
  show Heatmap  = "heatmap"
  show Line     = "line"
  show Pie      = "pie"
  show Sankey   = "sankey"
  show Scatter  = "scatter"            -- ^ https://ecomfe.github.io/echarts-examples/public/editor.html?c=scatter-simple
  show Sunburst = "sunburst"
  show _        = "not implemented yet: should throw error here"

seriesType :: Chart -> SeriesType
seriesType = SeriesType <<< show


-- | Scatter Dimension 2 data
type OptionalSeries =
  ( name          :: String
  , symbolSize    :: Number
  , itemStyle     :: ItemStyle
    -- ^ Graphic style of, *emphasis* is the style when it is highlighted, like being hovered by mouse, or highlighted via legend connect.
    --   https://ecomfe.github.io/echarts-doc/public/en/option.html#series-scatter.itemStyle
  , tooltip       :: Tooltip
  , emphasis      :: { itemStyle :: ItemStyle }
  , selectedMode  :: SelectedMode
  , select        :: { itemStyle :: ItemStyle }
  -- ^ need "selectedMode" to be defined
  -- many more...
  )

data Series

unsafeSeries :: forall o. Record o -> Series
unsafeSeries = unsafeCoerce

type RequiredSeriesD1 o =
  { "type" :: SeriesType
  , "data" :: Array DataD1
  | o
  }

seriesD1 :: forall o. Optional o OptionalSeries => RequiredSeriesD1 o -> Series
seriesD1 = unsafeSeries

seriesFunnelD1 :: forall o. Optional o OptionalSeries => Record o -> Array DataD1 -> Series
seriesFunnelD1 o ds = unsafeSeries (unsafeSet "data" ds (unsafeSet "type" (seriesType Funnel) o))

seriesBarD1 :: forall o. Optional o OptionalSeries => Record o -> Array DataD1 -> Series
seriesBarD1 o ds = unsafeSeries (unsafeSet "data" ds (unsafeSet "type" (seriesType Bar) o))

seriesPieD1 :: forall o. Optional o OptionalSeries => Record o -> Array DataD1 -> Series
seriesPieD1 o ds = unsafeSeries (unsafeSet "data" ds (unsafeSet "type" (seriesType Pie) o))

type RequiredSeriesD2 o =
  { "data" :: Array DataD2
  , "type" :: SeriesType
  | o
  }

seriesD2 :: forall o. Optional o OptionalSeries => RequiredSeriesD2 o -> Series
seriesD2 = unsafeSeries

seriesScatterD2 :: forall o. Optional o OptionalSeries => Record o -> Array DataD2 -> Series
seriesScatterD2 o ds =
  unsafeCoerce (unsafeSet "data" ds (unsafeSet "type" (seriesType Scatter) o))

type Node = { name :: String}
type Link = { source :: String
            , target :: String
            , value  :: Number
            }

-- | Sankey Chart
-- https://ecomfe.github.io/echarts-examples/public/editor.html?c=sankey-simple
type RequiredSankey o =
  { "data" :: Array Node
  , links  :: Array Link
  | o
  }

type OptionalSankey =
  ( layout :: String
  )

seriesSankey :: forall o. Optional o OptionalSankey => RequiredSankey o -> Series
seriesSankey o = unsafeSeries ((unsafeCoerce o) { "type" = seriesType Sankey })

-- | * Trees Chart
-- All these Trees are hierarchical Trees structure (or diagram)
-- https://en.wikipedia.org/wiki/Tree_structure

-- Tree types
data Trees = TreeLine | TreeRadial | TreeMap

instance Show Trees where
  show TreeLine    = "tree"           -- ^ https://ecomfe.github.io/echarts-examples/public/editor.html?c=tree-radial
  show TreeRadial  = "tree"           -- ^ https://ecomfe.github.io/echarts-examples/public/editor.html?c=scatter-simple
  show TreeMap     = "treemap"        -- ^ https://ecomfe.github.io/echarts-examples/public/editor.html?c=treemap-simple


-- TreeLine is a 1-Dimension horizontal hierchical Tree

-- TreeRadial is 1-Dimension radial (as circle) Tree with no surface meaning
-- https://en.wikipedia.org/wiki/Radial_tree
-- https://ecomfe.github.io/echarts-examples/public/editor.html?c=tree-radial

-- TreeMap is a is 2-Dimension Tree with surface meaning
-- TreeMap example implementation:
-- https://ecomfe.github.io/echarts-examples/public/editor.html?c=treemap-simple

type RequiredTree o =
  { "type" :: SeriesType
  , "data" :: Array TreeNode
  | o
  }

type OptionalTree =
  ( layout :: String
  )

seriesTree :: forall o. Optional o OptionalTree => RequiredTree o -> Series
seriesTree = unsafeSeries

mkTree :: Trees -> Array TreeNode -> Series
mkTree t ts = seriesTree { "type" : SeriesType (show t)
                         , "data" : map (toJsTree Nothing) ts
                         , layout : layout
                         }
              where
                layout = case t of
                           TreeRadial -> "radial"
                           _          -> "none"

-- ** Data Structure of the Trees
data TreeData = Array TreeNode


treeValue :: TreeNode -> Int
treeValue (TreeNode x) = foldl (+) 0 $ [x.value] <> map treeValue x.children

toJsTree :: Maybe String -> TreeNode -> TreeNode
toJsTree maybeSurname (TreeNode x) =
  unsafeCoerce { name : name
               , value : foldl (+) 0 $ [x.value] <> map treeValue x.children
               , children : (map (toJsTree (Just name)) x.children)
               }
    where
      name = maybe "" (\x' -> x' <> ">") maybeSurname  <> x.name

newtype TreeNode = TreeNode {
    children :: Array TreeNode
  , name     :: String
  , value    :: Int
  }
derive instance Generic TreeNode _
derive instance Newtype TreeNode _
derive instance Eq TreeNode
instance JSON.ReadForeign TreeNode where
  readImpl f = do
    inst <- JSON.readImpl f
    pure $ TreeNode $ Record.rename labelP nameP inst
instance JSON.WriteForeign TreeNode where
  writeImpl (TreeNode t) = JSON.writeImpl $ Record.rename nameP labelP t

treeNode :: String -> Int -> Array TreeNode -> TreeNode
treeNode n v ts = TreeNode {name : n, value:v, children:ts}

treeLeaf :: String -> Int -> TreeNode
treeLeaf n v = TreeNode { name : n, value : v, children : []}


nameP = SProxy :: SProxy "name"
labelP = SProxy :: SProxy "label"


-- | TODO
-- https://ecomfe.github.io/echarts-examples/public/data/asset/data/life-expectancy-table.json
-- https://ecomfe.github.io/echarts-examples/public/editor.html?c=scatter3D-dataset&gl=1