{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE Strict #-}

{- |
Module      : Granite.Internal.LegacyChart
Copyright   : (c) 2025
License     : MIT
Maintainer  : mschavinda@gmail.com

Chart builders shared between "Granite" and "Granite.Svg" for the
plotly-express-style helpers (area, ribbon, density, errorBars,
funnel, polarLine, waterfall, distPlot).
-}
module Granite.Internal.LegacyChart (
    Chart,
    plotChart,
    areaChart,
    ribbonChart,
    densityChart,
    errorBarsChart,
    funnelChart,
    polarLineChart,
    waterfallChart,
    distPlotChart,
) where

import Data.Text (Text)
import Data.Text qualified as Text

import Granite.Spec

plotChart :: Int -> Int -> Text -> Chart -> Chart
plotChart :: Int -> Int -> Text -> Chart -> Chart
plotChart Int
w Int
h Text
title Chart
chart =
    Chart
chart
        { chartSize = SizeChars w h
        , chartTitle = if Text.null title then Nothing else Just title
        }

areaChart :: [(Text, [(Double, Double)])] -> Int -> Int -> Text -> Chart
areaChart :: [(Text, [(Double, Double)])] -> Int -> Int -> Text -> Chart
areaChart [(Text, [(Double, Double)])]
series Int
w Int
h Text
title =
    let layers :: [Layer]
layers = ((Text, [(Double, Double)]) -> [Layer])
-> [(Text, [(Double, Double)])] -> [Layer]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap (Text, [(Double, Double)]) -> [Layer]
mkSeries [(Text, [(Double, Double)])]
series
     in Int -> Int -> Text -> Chart -> Chart
plotChart Int
w Int
h Text
title (Chart -> Chart) -> Chart -> Chart
forall a b. (a -> b) -> a -> b
$ Chart
emptyChart{chartLayers = layers}
  where
    mkSeries :: (Text, [(Double, Double)]) -> [Layer]
mkSeries (Text
name, [(Double, Double)]
pts) =
        let xs :: [Double]
xs = ((Double, Double) -> Double) -> [(Double, Double)] -> [Double]
forall a b. (a -> b) -> [a] -> [b]
map (Double, Double) -> Double
forall a b. (a, b) -> a
fst [(Double, Double)]
pts
            ys :: [Double]
ys = ((Double, Double) -> Double) -> [(Double, Double)] -> [Double]
forall a b. (a -> b) -> [a] -> [b]
map (Double, Double) -> Double
forall a b. (a, b) -> b
snd [(Double, Double)]
pts
            zeros :: [Double]
zeros = Int -> Double -> [Double]
forall a. Int -> a -> [a]
replicate ([(Double, Double)] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [(Double, Double)]
pts) Double
0 :: [Double]
            df :: DataFrame
df =
                [(Text, Column)] -> DataFrame
fromColumns
                    [ (Text
"x", [Double] -> Column
ColNum [Double]
xs)
                    , (Text
"y", [Double] -> Column
ColNum [Double]
ys)
                    , (Text
"ymin", [Double] -> Column
ColNum [Double]
zeros)
                    ]
            ribbonL :: Layer
ribbonL =
                (Geom -> Layer
defLayer Geom
GeomRibbon)
                    { layerData = Just df
                    , layerMapping =
                        emptyMapping
                            { aesX = Just (ColumnRef "x")
                            , aesYmin = Just (ColumnRef "ymin")
                            , aesYmax = Just (ColumnRef "y")
                            , aesGroup = Just (ColumnRef name)
                            }
                    , layerAesDef = emptyAesDefaults{defAlpha = Just 0.35}
                    }
            lineL :: Layer
lineL =
                (Geom -> Layer
defLayer Geom
GeomLine)
                    { layerData = Just df
                    , layerMapping =
                        emptyMapping
                            { aesX = Just (ColumnRef "x")
                            , aesY = Just (ColumnRef "y")
                            , aesGroup = Just (ColumnRef name)
                            }
                    }
         in [Layer
ribbonL, Layer
lineL]

ribbonChart ::
    [(Text, [(Double, Double, Double)])] ->
    Int ->
    Int ->
    Text ->
    Chart
ribbonChart :: [(Text, [(Double, Double, Double)])] -> Int -> Int -> Text -> Chart
ribbonChart [(Text, [(Double, Double, Double)])]
series Int
w Int
h Text
title =
    let layers :: [Layer]
layers = ((Text, [(Double, Double, Double)]) -> Layer)
-> [(Text, [(Double, Double, Double)])] -> [Layer]
forall a b. (a -> b) -> [a] -> [b]
map (Text, [(Double, Double, Double)]) -> Layer
mkLayer [(Text, [(Double, Double, Double)])]
series
     in Int -> Int -> Text -> Chart -> Chart
plotChart Int
w Int
h Text
title (Chart -> Chart) -> Chart -> Chart
forall a b. (a -> b) -> a -> b
$ Chart
emptyChart{chartLayers = layers}
  where
    mkLayer :: (Text, [(Double, Double, Double)]) -> Layer
mkLayer (Text
name, [(Double, Double, Double)]
pts) =
        let xs :: [Double]
xs = [Double
x | (Double
x, Double
_, Double
_) <- [(Double, Double, Double)]
pts]
            los :: [Double]
los = [Double
l | (Double
_, Double
l, Double
_) <- [(Double, Double, Double)]
pts]
            his :: [Double]
his = [Double
hi | (Double
_, Double
_, Double
hi) <- [(Double, Double, Double)]
pts]
            df :: DataFrame
df =
                [(Text, Column)] -> DataFrame
fromColumns
                    [ (Text
"x", [Double] -> Column
ColNum [Double]
xs)
                    , (Text
"ymin", [Double] -> Column
ColNum [Double]
los)
                    , (Text
"ymax", [Double] -> Column
ColNum [Double]
his)
                    ]
         in (Geom -> Layer
defLayer Geom
GeomRibbon)
                { layerData = Just df
                , layerMapping =
                    emptyMapping
                        { aesX = Just (ColumnRef "x")
                        , aesYmin = Just (ColumnRef "ymin")
                        , aesYmax = Just (ColumnRef "ymax")
                        , aesGroup = Just (ColumnRef name)
                        }
                , layerAesDef = emptyAesDefaults{defAlpha = Just 0.4}
                }

densityChart :: [(Text, [Double])] -> Int -> Int -> Text -> Chart
densityChart :: [(Text, [Double])] -> Int -> Int -> Text -> Chart
densityChart [(Text, [Double])]
series Int
w Int
h Text
title =
    let layers :: [Layer]
layers = ((Text, [Double]) -> Layer) -> [(Text, [Double])] -> [Layer]
forall a b. (a -> b) -> [a] -> [b]
map (Text, [Double]) -> Layer
mkLayer [(Text, [Double])]
series
     in Int -> Int -> Text -> Chart -> Chart
plotChart Int
w Int
h Text
title (Chart -> Chart) -> Chart -> Chart
forall a b. (a -> b) -> a -> b
$ Chart
emptyChart{chartLayers = layers}
  where
    mkLayer :: (Text, [Double]) -> Layer
mkLayer (Text
name, [Double]
sample) =
        let df :: DataFrame
df = [(Text, Column)] -> DataFrame
fromColumns [(Text
"x", [Double] -> Column
ColNum [Double]
sample)]
         in (Geom -> Layer
defLayer Geom
GeomDensity)
                { layerData = Just df
                , layerMapping =
                    emptyMapping
                        { aesX = Just (ColumnRef "x")
                        , aesY = Just (ColumnRef "density")
                        , aesGroup = Just (ColumnRef name)
                        }
                , layerStat = StatDensity
                }

errorBarsChart ::
    [(Text, [(Double, Double, Double, Double)])] ->
    Int ->
    Int ->
    Text ->
    Chart
errorBarsChart :: [(Text, [(Double, Double, Double, Double)])]
-> Int -> Int -> Text -> Chart
errorBarsChart [(Text, [(Double, Double, Double, Double)])]
series Int
w Int
h Text
title =
    let layers :: [Layer]
layers = ((Text, [(Double, Double, Double, Double)]) -> [Layer])
-> [(Text, [(Double, Double, Double, Double)])] -> [Layer]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap (Text, [(Double, Double, Double, Double)]) -> [Layer]
mkSeries [(Text, [(Double, Double, Double, Double)])]
series
     in Int -> Int -> Text -> Chart -> Chart
plotChart Int
w Int
h Text
title (Chart -> Chart) -> Chart -> Chart
forall a b. (a -> b) -> a -> b
$ Chart
emptyChart{chartLayers = layers}
  where
    mkSeries :: (Text, [(Double, Double, Double, Double)]) -> [Layer]
mkSeries (Text
name, [(Double, Double, Double, Double)]
rows) =
        let xs :: [Double]
xs = [Double
x | (Double
x, Double
_, Double
_, Double
_) <- [(Double, Double, Double, Double)]
rows]
            ys :: [Double]
ys = [Double
y | (Double
_, Double
y, Double
_, Double
_) <- [(Double, Double, Double, Double)]
rows]
            los :: [Double]
los = [Double
l | (Double
_, Double
_, Double
l, Double
_) <- [(Double, Double, Double, Double)]
rows]
            his :: [Double]
his = [Double
hi | (Double
_, Double
_, Double
_, Double
hi) <- [(Double, Double, Double, Double)]
rows]
            df :: DataFrame
df =
                [(Text, Column)] -> DataFrame
fromColumns
                    [ (Text
"x", [Double] -> Column
ColNum [Double]
xs)
                    , (Text
"y", [Double] -> Column
ColNum [Double]
ys)
                    , (Text
"lo", [Double] -> Column
ColNum [Double]
los)
                    , (Text
"hi", [Double] -> Column
ColNum [Double]
his)
                    ]
            m :: Mapping
m =
                Mapping
emptyMapping
                    { aesX = Just (ColumnRef "x")
                    , aesY = Just (ColumnRef "y")
                    , aesYmin = Just (ColumnRef "lo")
                    , aesYmax = Just (ColumnRef "hi")
                    , aesGroup = Just (ColumnRef name)
                    }
            bars :: Layer
bars =
                (Geom -> Layer
defLayer Geom
GeomErrorbar)
                    { layerData = Just df
                    , layerMapping = m
                    , layerStat = StatIdentity
                    }
            pts :: Layer
pts =
                (Geom -> Layer
defLayer Geom
GeomPoint)
                    { layerData = Just df
                    , layerMapping = m
                    , layerStat = StatIdentity
                    }
         in [Layer
bars, Layer
pts]

funnelChart :: [(Text, Double)] -> Int -> Int -> Text -> Chart
funnelChart :: [(Text, Double)] -> Int -> Int -> Text -> Chart
funnelChart [(Text, Double)]
stages Int
w Int
h Text
title =
    let names :: [Text]
names = [Text
n | (Text
n, Double
_) <- [(Text, Double)]
stages]
        values :: [Double]
values = [Double
v | (Text
_, Double
v) <- [(Text, Double)]
stages]
        df :: DataFrame
df =
            [(Text, Column)] -> DataFrame
fromColumns
                [ (Text
"stage", [Text] -> Column
ColCat [Text]
names)
                , (Text
"value", [Double] -> Column
ColNum [Double]
values)
                ]
        layer :: Layer
layer =
            (Geom -> Layer
defLayer Geom
GeomBar)
                { layerMapping =
                    emptyMapping
                        { aesX = Just (ColumnRef "stage")
                        , aesY = Just (ColumnRef "value")
                        }
                , layerStat = StatIdentity
                }
     in Int -> Int -> Text -> Chart -> Chart
plotChart Int
w Int
h Text
title (Chart -> Chart) -> Chart -> Chart
forall a b. (a -> b) -> a -> b
$
            Chart
emptyChart
                { chartData = df
                , chartLayers = [layer]
                , chartCoord = CoordFlip
                }

polarLineChart :: [(Text, [(Double, Double)])] -> Int -> Int -> Text -> Chart
polarLineChart :: [(Text, [(Double, Double)])] -> Int -> Int -> Text -> Chart
polarLineChart [(Text, [(Double, Double)])]
series Int
w Int
h Text
title =
    let layers :: [Layer]
layers = ((Text, [(Double, Double)]) -> Layer)
-> [(Text, [(Double, Double)])] -> [Layer]
forall a b. (a -> b) -> [a] -> [b]
map (Text, [(Double, Double)]) -> Layer
mkLayer [(Text, [(Double, Double)])]
series
     in Int -> Int -> Text -> Chart -> Chart
plotChart Int
w Int
h Text
title (Chart -> Chart) -> Chart -> Chart
forall a b. (a -> b) -> a -> b
$
            Chart
emptyChart
                { chartLayers = layers
                , chartCoord = CoordPolar ThetaX 0 PolarCCW
                }
  where
    mkLayer :: (Text, [(Double, Double)]) -> Layer
mkLayer (Text
name, [(Double, Double)]
pts) =
        let thetas :: [Double]
thetas = [Double
t | (Double
t, Double
_) <- [(Double, Double)]
pts]
            rs :: [Double]
rs = [Double
r | (Double
_, Double
r) <- [(Double, Double)]
pts]
            df :: DataFrame
df = [(Text, Column)] -> DataFrame
fromColumns [(Text
"theta", [Double] -> Column
ColNum [Double]
thetas), (Text
"r", [Double] -> Column
ColNum [Double]
rs)]
         in (Geom -> Layer
defLayer Geom
GeomLine)
                { layerData = Just df
                , layerMapping =
                    emptyMapping
                        { aesX = Just (ColumnRef "theta")
                        , aesY = Just (ColumnRef "r")
                        , aesGroup = Just (ColumnRef name)
                        }
                }

waterfallChart :: [(Text, Double, Double)] -> Int -> Int -> Text -> Chart
waterfallChart :: [(Text, Double, Double)] -> Int -> Int -> Text -> Chart
waterfallChart [(Text, Double, Double)]
rows Int
w Int
h Text
title =
    let labels :: [Text]
labels = [Text
n | (Text
n, Double
_, Double
_) <- [(Text, Double, Double)]
rows]
        starts :: [Double]
starts = [Double
s | (Text
_, Double
s, Double
_) <- [(Text, Double, Double)]
rows]
        ends :: [Double]
ends = [Double
e | (Text
_, Double
_, Double
e) <- [(Text, Double, Double)]
rows]
        df :: DataFrame
df =
            [(Text, Column)] -> DataFrame
fromColumns
                [ (Text
"x", [Text] -> Column
ColCat [Text]
labels)
                , (Text
"y", [Double] -> Column
ColNum [Double]
ends)
                , (Text
"__ybase", [Double] -> Column
ColNum [Double]
starts)
                ]
        layer :: Layer
layer =
            (Geom -> Layer
defLayer Geom
GeomBar)
                { layerMapping =
                    emptyMapping
                        { aesX = Just (ColumnRef "x")
                        , aesY = Just (ColumnRef "y")
                        }
                , layerStat = StatIdentity
                }
     in Int -> Int -> Text -> Chart -> Chart
plotChart Int
w Int
h Text
title (Chart -> Chart) -> Chart -> Chart
forall a b. (a -> b) -> a -> b
$
            Chart
emptyChart
                { chartData = df
                , chartLayers = [layer]
                }

distPlotChart :: [(Text, [Double])] -> Int -> Int -> Text -> Chart
distPlotChart :: [(Text, [Double])] -> Int -> Int -> Text -> Chart
distPlotChart [(Text, [Double])]
series Int
w Int
h Text
title =
    let layers :: [Layer]
layers = ((Text, [Double]) -> [Layer]) -> [(Text, [Double])] -> [Layer]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap (Text, [Double]) -> [Layer]
mkSeries [(Text, [Double])]
series
     in Int -> Int -> Text -> Chart -> Chart
plotChart Int
w Int
h Text
title (Chart -> Chart) -> Chart -> Chart
forall a b. (a -> b) -> a -> b
$ Chart
emptyChart{chartLayers = layers}
  where
    mkSeries :: (Text, [Double]) -> [Layer]
mkSeries (Text
name, [Double]
sample) =
        let df :: DataFrame
df = [(Text, Column)] -> DataFrame
fromColumns [(Text
"x", [Double] -> Column
ColNum [Double]
sample)]
            hist :: Layer
hist =
                (Geom -> Layer
defLayer Geom
GeomHistogram)
                    { layerData = Just df
                    , layerMapping =
                        emptyMapping
                            { aesX = Just (ColumnRef "x")
                            , aesY = Just (ColumnRef "count")
                            , aesGroup = Just (ColumnRef name)
                            }
                    , layerStat = StatBin (BinByCount 20)
                    , layerAesDef = emptyAesDefaults{defAlpha = Just 0.6}
                    }
            kde :: Layer
kde =
                (Geom -> Layer
defLayer Geom
GeomDensity)
                    { layerData = Just df
                    , layerMapping =
                        emptyMapping
                            { aesX = Just (ColumnRef "x")
                            , aesY = Just (ColumnRef "density")
                            , aesGroup = Just (ColumnRef name)
                            }
                    , layerStat = StatDensity
                    }
         in [Layer
hist, Layer
kde]