content: {-# LANGUAGE NoMonomorphismRestriction #-}
  {-# LANGUAGE FlexibleContexts          #-}
  {-# LANGUAGE TypeFamilies              #-}
  
  import Diagrams.Prelude
  import Diagrams.Backend.SVG.CmdLine
  import Data.List (isPrefixOf)
  
  splitOn :: Eq a => [a] -> [a] -> [[a]]
  splitOn delim str = go str
    where
      go [] = [[]]
      go s
        | delim `isPrefixOf` s = [] : go (drop (length delim) s)
        | otherwise =
            let (c:cs) = s
                (x:xs) = go cs
            in (c:x) : xs
  
  -------------------------------------------------------------------------------
  -- Utilities
  -------------------------------------------------------------------------------
  
  title1 t =
    vsep 0.3 [ text line # fontSizeL 0.3 # bold | line <- splitOn "\n" t ]
  title2 t = text t # fontSizeL 0.2 # bold
  
  textBox x y t = t <> strutX x <> strutY y
  
  item1 t = text t # fontSizeL 0.25
  enumerate items = vsep 0.3 [ textBox 2.0 0.4 (item1 i) | i <- items ]
  
  boxedXY :: Double -> Double -> Diagram B -> Diagram B
  boxedXY w h content = content # centerXY  <> rect w h # lw thin
  
  boxedEnum x y items =
      let list = enumerate items
      in boxedXY x y (list # centerXY)
  
  titleW = 2.0
  titleH = 0.5
  
  boxedTitledEnum1 x y title items =
      let ttext = textBox titleW titleH (title1 title)
          list = enumerate items
      in boxedXY x y (vsep 0 [ttext, boxedXY x (y - titleH) (list # centerXY)])
  
  connect1 = connectOutside' (with & arrowHead .~ tri & headLength .~ global 0.2)
  
  coreOval :: String -> Diagram B
  coreOval tag =
      textBox 2.0 0.5 (title1 tag # centerXY)
      <> ellipseXY 1.5 0.75 # fc lightblue # lw medium
  
  -------------------------------------------------------------------------------
  -- streamly-core diagram
  -------------------------------------------------------------------------------
  
  bigBoxW = 12
  bigBoxH = 4
  centerBoxW = bigBoxH
  centerBoxH = bigBoxH
  
  box1 = boxedXY bigBoxW bigBoxH
  box2 = boxedXY centerBoxW centerBoxH
  
  bigBoxSubContent = boxedTitledEnum1 (bigBoxW / 3) (bigBoxH - titleH)
  
  streamsBox :: Diagram B
  streamsBox =
      let prod    = bigBoxSubContent "Generation" ["Stream", "StreamK", "Unfold"]
          transf  = bigBoxSubContent "Transformation" ["Scanl"]
          cons    = bigBoxSubContent "Consumption" ["Fold", "Parser", "ParserK"]
          row     = hsep 0 [prod, transf, cons]
          title   = textBox titleW titleH (title1 "Streams")
          content = vsep 0 [title, row # centerXY]
      in box1 (content # centerXY)
  
  arraysBox :: Diagram B
  arraysBox =
      let immut  = bigBoxSubContent "Immutable" ["Array", "Array.Generic"]
          mut    = bigBoxSubContent "Mutable" ["MutArray", "MutArray.Generic", "RingArray"]
          ser    = bigBoxSubContent "Serialization" ["MutByteArray", "Unbox", "Serialize"]
          row    = hsep 0 [immut, mut, ser]
          title  = textBox titleW titleH (title1 "Arrays")
          content= vsep 0 [title, row # centerXY]
      in box1 (content # centerXY)
  
  fileSystemBox :: Diagram B
  fileSystemBox =
      let items = ["Console.Stdio", "FileIO", "DirIO", "Handle", "Path"]
          content = boxedTitledEnum1 centerBoxW centerBoxH "File System" items
      in box2 (content # centerXY)
  
  otherModulesBox :: Diagram B
  otherModulesBox =
      let unicode = boxedTitledEnum1 centerBoxW (centerBoxH * 2/3) "Unicode" ["Parser", "Stream", "String"]
          resmgmt = boxedTitledEnum1 centerBoxW (centerBoxH * 1/3) "Resource Management" ["Control.Exception"]
          content = vsep 0 [unicode, resmgmt]
      in box2 (content # centerXY)
  
  streamlyCore :: Diagram B
  streamlyCore =
      let center    = coreOval "streamly-core\n(types and modules)" # named "core"
          topBox    = streamsBox # named "streams"
          bottomBox = arraysBox # named "arrays"
          leftBox   = fileSystemBox # named "fs"
          rightBox  = otherModulesBox # named "others"
  
          placed = position
            [ (p2 (0,0), center)
            , (p2 (0,4), topBox)
            , (p2 (0,-4), bottomBox)
            , (p2 (-4,0), leftBox)
            , (p2 (4,0), rightBox)
            ]
  
          arrows = applyAll
            [ connect1 "core" "streams"
            , connect1 "core" "arrays"
            , connect1 "core" "fs"
            , connect1 "core" "others"
            ]
      in arrows placed
  
  -------------------------------------------------------------------------------
  -- streamly diagram
  -------------------------------------------------------------------------------
  
  concurrentStreamsBox :: Diagram B
  concurrentStreamsBox =
      let prod    = bigBoxSubContent "Generation" ["Stream.Prelude"]
          transf  = bigBoxSubContent "Transformation" ["Scanl.Prelude"]
          cons    = bigBoxSubContent "Consumption" ["Fold.Prelude"]
          row     = hsep 0 [prod, transf, cons]
          title   = textBox titleW titleH (title1 "Concurrent Streams")
          content = vsep 0 [title, row # centerXY]
      in box1 (content # centerXY)
  
  networkBox :: Diagram B
  networkBox =
      let items = ["Network.Socket", "Network.Inet.TCP"]
          content = boxedTitledEnum1 centerBoxW centerBoxH "Network" items
      in box2 (content # centerXY)
  
  streamly :: Diagram B
  streamly =
      let center    = coreOval "streamly" # named "streamly"
          topBox    = concurrentStreamsBox # named "concurrent-streams"
          bottomBox = networkBox # named "network"
  
          placed = position
            [ (p2 (0,0), center)
            , (p2 (0,4), topBox)
            , (p2 (0,-4), bottomBox)
            ]
  
          arrows = applyAll
            [ connect1 "streamly" "concurrent-streams"
            , connect1 "streamly" "network"
            ]
      in arrows placed
  
  main :: IO ()
  main =
      mainWith
          [ ("streamly-core", (streamlyCore # centerXY # pad 1.1))
          , ("streamly", (streamly # centerXY # pad 1.1))
          ]
  