{-# OPTIONS_GHC -Wall #-}
-- | textual chart elements
module Chart.Text
  ( TextOptions(..)
  , text_
  , texts
  , textChart
  , textChart_
  , LabelOptions(..)
  , labelled
  ) where

import Chart.Core
import qualified Data.Text as Text
import Diagrams.Prelude hiding (Color, D, scale)
import qualified Diagrams.TwoD.Size as D
import Graphics.SVGFonts
import Graphics.SVGFonts.ReadFont
import NumHask.Pair
import NumHask.Prelude hiding (rotate)
import NumHask.Rect

-- | text options
data TextOptions = TextOptions
  { textSize :: Double
  , textAlignH :: AlignH
  , textColor :: AlphaColour Double
  , textFillRule :: FillRule
  , textRotation :: Double
  , textFont :: PreparedFont Double
  }

instance Default TextOptions where
  def = TextOptions 0.08 AlignCenter (withOpacity black 0.33) EvenOdd 0 lin2

-- | Create a textual chart element
--
-- > let text_Example = text_ def "Welcome to chart-unit!"
--
-- ![text_ example](other/text_Example.svg)
--
text_ :: TextOptions -> Text -> Chart b
text_ (TextOptions s a c fr rot f) t =
  moveTo (p_ (Pair (alignHTU a * D.width path) 0)) $
  path # fcA c # lw 0 # fillRule fr # rotate (rot @@ deg)
  where
    path = textSVG_ (TextOpts f INSIDE_H KERN False s s) (Text.unpack t)

-- | Creatye positioned text from a list
--
-- > let ts = map (Text.singleton) ['a'..'z']
-- > texts def ts [Pair (0.05*x) 0 |x <- [0..5]]
--
-- ![texts example](other/textsExample.svg)
--
texts :: (R2 r) => TextOptions -> [Text] -> [r Double] -> Chart b
texts opts ts ps = mconcat $ zipWith (\p t -> positioned p (text_ opts t)) ps ts

-- | A chart of text
textChart ::
     (Traversable f)
  => [TextOptions]
  -> Aspect
  -> Rect Double
  -> [f (Text, Pair Double)]
  -> Chart b
textChart optss (Aspect asp) r xyss =
  mconcat $
  getZipList $
  texts <$> ZipList optss <*> ZipList (map fst . toList <$> xyss) <*>
  ZipList (projectss r asp (fmap snd . toList <$> xyss))

-- | A chart of text scaled to its own range
--
-- > import qualified Data.Text as Text
-- > let ps = [Pair (sin (x*0.1)) x | x<-[0..25]]
-- > textChart_ (repeat $ def {textSize=0.33}) widescreen [zip ts ps]
--
-- ![textChart_ example](other/textChart_Example.svg)
--
textChart_ :: [TextOptions] -> Aspect -> [[(Text, Pair Double)]] -> Chart b
textChart_ optss asp xyss =
  textChart optss asp (range $ fmap snd . toList <$> xyss) xyss

-- | A label is a text element attached to a chart element
data LabelOptions = LabelOptions
  { labelText :: TextOptions
  , labelOrientation :: Pair Double -- ^ direction of label
  , labelGap :: Double -- ^ distance to label
  }

instance Default LabelOptions where
  def = LabelOptions def (Pair 0 1) 0.05

-- | Label a chart element with some text
--
-- > let lopts = def {textAlignH = AlignLeft, textRotation=45}
-- > labelled (LabelOptions lopts (Pair 1 1) 0.05) "a label" (glyph_ def)
--
-- ![labelled example](other/labelledExample.svg)
--
labelled :: LabelOptions -> Text -> Chart b -> Chart b
labelled (LabelOptions texto o g) t ch =
  beside (r_ o) (beside (r_ o) ch (strut (r_ o) # scale g)) (text_ texto t)