{-# LANGUAGE DeriveGeneric, DeriveAnyClass #-}
module Eventloop.Utility.Trees.GeneralTree where

import GHC.Generics (Generic)
import Control.DeepSeq

import Eventloop.Module.BasicShapes.Types
import Eventloop.Utility.Vectors
import Eventloop.Utility.Trees.LayoutTree


data GeneralTree = GeneralTreeBox [GeneralNodeContent] [(GeneralLine, GeneralTree)]
                deriving (Show, Eq, Generic, NFData)

data GeneralNodeContent = GeneralNodeText FillColor String
                        | GeneralNode FillColor Radius
                        deriving (Show, Eq, Generic, NFData)

data GeneralLine = GeneralLine StrokeColor
                    deriving (Show, Eq, Generic, NFData)


type LeftOffset   = X
type TopOffset    = Y
type RightOffset  = X
type BottomOffset = Y
type Middle = GraphicalNumeric

type Pos = (X, Y)

class GeneralizeTree a where
    generalizeTree :: a -> GeneralTree

instance GeneralizeTree GeneralTree where
    generalizeTree a = a



-- Courier 16px
textFont = "Courier"
textHeight = 16 :: Float -- px
charWidth  = 10 :: Float

marginBetweenTrees = 10 :: Float
marginBetweenNodeContent = 2 :: Float
marginBetweenNodeRows = 20 :: Float
marginBetweenNodeColumns = 20 :: Float




generalNodeDimension :: GeneralTree -> Dimensions
generalNodeDimension (GeneralTreeBox content _) = flattenDimensions contentDimensions
                                                where
                                                    contentDimensions = map generalNodeContentDimension content


flattenDimensions :: [Dimensions] -> Dimensions
flattenDimensions [] = (0.0,0.0)
flattenDimensions [d] = d
flattenDimensions ((w,h):ds) = (max w wTotal, h + marginBetweenNodeContent + hTotal)
                            where
                                (wTotal, hTotal) = flattenDimensions ds


generalNodeContentDimension :: GeneralNodeContent -> Dimensions
generalNodeContentDimension (GeneralNodeText _ str) = textSize str
generalNodeContentDimension (GeneralNode _ r      ) = (2*r, 2*r)


layoutGeneralTree :: LeftOffset -> TopOffset -> GeneralTree -> (LayoutTree, RightOffset, BottomOffset)
layoutGeneralTree leftOffset topOffset box@(GeneralTreeBox content children) = (LBox (Point (x, y)) topConnect bottomConnect lcs lchildrenWithLines, rightOffset, bottomOffsetChildren)
                                                                        where
                                                                            x               = leftBorder
                                                                            y               = topOffset
                                                                            middle          = (rightOffset - leftOffset) / 2 + leftOffset
                                                                            rightOffset     = max rightOffsetChildren (leftOffset + width)
                                                                            leftBorder      = middle - (width / 2)
                                                                            topConnect      = Point (width / 2, 0)
                                                                            bottomConnect   = Point (width / 2, height)
                                                                            (width, height) = generalNodeDimension box
                                                                            lcs             = layoutGeneralNodeContentList (width / 2) 0 content
                                                                            (lchildrenWithLines, rightOffsetChildren, bottomOffsetChildren) = layoutGeneralTreeChildren leftOffset (topOffset + marginBetweenNodeRows + height) children

layoutGeneralTreeChildren :: LeftOffset -> TopOffset -> [(GeneralLine, GeneralTree)] -> ([(LayoutLine, LayoutTree)], RightOffset, BottomOffset)
layoutGeneralTreeChildren left top treesWithLines =(zip lLines lTrees, right, bottom)
                                    where
                                        lines        = map fst treesWithLines
                                        generalTrees = map snd treesWithLines
                                        lLines       = map layoutLine lines
                                        (lTrees, right, bottom) = layoutGeneralTrees left top generalTrees

layoutLine :: GeneralLine -> LayoutLine
layoutLine (GeneralLine color) = LayoutLine color

layoutGeneralTrees :: LeftOffset -> TopOffset -> [GeneralTree] -> ([LayoutTree], RightOffset, BottomOffset)
layoutGeneralTrees left top [] = ([], left, top)
layoutGeneralTrees left top [box] = (\(a,b,c) -> ([a],b,c)) $ layoutGeneralTree left top box
layoutGeneralTrees left top (box:bs) = (lbox:lrest, rightRest, max bottom bottomRest)
                                            where
                                                (lbox, right , bottom) = layoutGeneralTree left top box
                                                (lrest, rightRest, bottomRest) = (layoutGeneralTrees (right + marginBetweenNodeColumns) top bs)

layoutGeneralNodeContentList :: Middle -> Height -> [GeneralNodeContent] -> [LayoutNodeContent]
layoutGeneralNodeContentList _ _ [] = []
layoutGeneralNodeContentList middle height [nc] = [layoutGeneralNodeContent (middle, height) nc]
layoutGeneralNodeContentList middle height (nc:ncs) = lnc:(layoutGeneralNodeContentList middle height' ncs)
                                                    where
                                                        height' = height + ncHeight + marginBetweenNodeContent
                                                        (_, ncHeight) = generalNodeContentDimension nc
                                                        lnc = layoutGeneralNodeContent (middle, height) nc

layoutGeneralNodeContent :: Pos -> GeneralNodeContent -> LayoutNodeContent
layoutGeneralNodeContent (middle, top) gnc@(GeneralNodeText color str) = LayoutNodeText color (Point (x,y)) str (textSize str)
                                                                    where
                                                                        x = middle
                                                                        y = top
                                                                        (w, h) = generalNodeContentDimension gnc

layoutGeneralNodeContent (middle, top) gnc@(GeneralNode color rad)     = LayoutNode color (Point (x,y)) rad
                                                                where
                                                                    x = middle
                                                                    y = top + h/2
                                                                    (w, h) = generalNodeContentDimension gnc

-- (width, height)
textSize :: [Char] -> (Float, Float)
textSize str = (textWidth, textHeight)
             where
                textWidth = charWidth * (fromIntegral (length str))


treeIndex :: Int -> Offset -> (Shape, LeftOffset)
treeIndex i (x, y) = (text, x + wText)
                where
                    iStr = show i
                    (wText, hText) = textSize iStr
                    p = Point (x, y)
                    text = Text iStr "Courier" 20 p AlignLeft (255,75,75, 255) 1 (0,0,0,0) Nothing