-- | This is the main function that retrieves a stored graph
-- from the database and creates a new SVG file for it.
buildSVG :: GraphId              -- ^ The ID of the graph that is being built.
         -> M.Map String String  -- ^ A map of courses that holds the course
                                 --   ID as a key, and the data-active
                                 --   attribute as the course's value.
                                 --   The data-active attribute is used in the
                                 --   interactive graph to indicate which
                                 --   courses the user has selected.
         -> String               -- ^ The filename that this graph will be
                                 --   written to.
         -> Bool                 -- ^ Whether to include inline styles.
         -> IO ()
buildSVG gId courseMap filename styled =
    runSqlite databasePath $ do
        sqlRects    :: [Entity Shape] <- selectList
                                             [ShapeType_ <-. [Node, Hybrid],
                                              ShapeGraph ==. gId] []
        sqlTexts    :: [Entity Text]  <- selectList [TextGraph ==. gId] []
        sqlPaths    :: [Entity Path]  <- selectList [PathGraph ==. gId] []
        sqlEllipses :: [Entity Shape] <- selectList
                                             [ShapeType_ ==. BoolNode,
                                              ShapeGraph ==. gId] []
        sqlGraph    :: [Entity Graph] <- selectList [GraphId ==. gId] []
        let courseStyleMap = M.map convertSelectionToStyle courseMap
            texts          = map entityVal sqlTexts
            -- TODO: Ideally, we would do these "build" steps *before*
            -- inserting these values into the database.
            rects          = zipWith (buildRect texts)
                                     (map entityVal sqlRects)
                                     (map keyAsInt sqlRects)
            ellipses       = zipWith (buildEllipses texts)
                                     (map entityVal sqlEllipses)
                                     (map keyAsInt sqlEllipses)
            paths          = zipWith (buildPath rects ellipses)
                                     (map entityVal sqlPaths)
                                     (map keyAsInt sqlPaths)
            regions        = filter pathIsRegion paths
            edges          = filter (not . pathIsRegion) paths
            regionTexts    = filter (not .
                                     intersectsWithShape (rects ++ ellipses))
                                    texts
            width          = graphWidth $ entityVal (head sqlGraph)
            height         = graphHeight $ entityVal (head sqlGraph)
            stringSVG      = renderSvg $ makeSVGDoc courseStyleMap
                                                    rects
                                                    ellipses
                                                    edges
                                                    regions
                                                    regionTexts
                                                    styled
                                                    width
                                                    height
        liftIO $ writeFile filename stringSVG
    where
        keyAsInt :: PersistEntity a => Entity a -> Integer
        keyAsInt = fromIntegral . (\(PersistInt64 x) -> x) . head . keyToValues . entityKey

        convertSelectionToStyle :: String -> String
        convertSelectionToStyle courseStatus =
            if isSelected courseStatus
            then "stroke-width:4;"
            else "opacity:0.5;stroke-dasharray:8,5;"

        isSelected :: String -> Bool
        isSelected courseStatus =
            isPrefixOf "active" courseStatus ||
            isPrefixOf "overridden" courseStatus

-- * SVG Creation

-- | This function does the heavy lifting to actually create
-- a new SVG value given the graph components.
buildSVG :: GraphId              -- ^ The ID of the graph that is being built.
         -> M.Map String String  -- ^ A map of courses that holds the course
                                 --   ID as a key, and the data-active
                                 --   attribute as the course's value.
                                 --   The data-active attribute is used in the
                                 --   interactive graph to indicate which
                                 --   courses the user has selected.
         -> String               -- ^ The filename that this graph will be
                                 --   written to.
         -> Bool                 -- ^ Whether to include inline styles.
         -> IO ()
buildSVG gId courseMap filename styled =
    runSqlite databasePath $ do
        sqlRects    :: [Entity Shape] <- selectList
                                             [ShapeType_ <-. [Node, Hybrid],
                                              ShapeGraph ==. gId] []
        sqlTexts    :: [Entity Text]  <- selectList [TextGraph ==. gId] []
        sqlPaths    :: [Entity Path]  <- selectList [PathGraph ==. gId] []
        sqlEllipses :: [Entity Shape] <- selectList
                                             [ShapeType_ ==. BoolNode,
                                              ShapeGraph ==. gId] []
        sqlGraph    :: [Entity Graph] <- selectList [GraphId ==. gId] []
        let courseStyleMap = M.map convertSelectionToStyle courseMap
            texts          = map entityVal sqlTexts
            -- TODO: Ideally, we would do these "build" steps *before*
            -- inserting these values into the database.
            rects          = zipWith (buildRect texts)
                                     (map entityVal sqlRects)
                                     (map keyAsInt sqlRects)
            ellipses       = zipWith (buildEllipses texts)
                                     (map entityVal sqlEllipses)
                                     (map keyAsInt sqlEllipses)
            paths          = zipWith (buildPath rects ellipses)
                                     (map entityVal sqlPaths)
                                     (map keyAsInt sqlPaths)
            regions        = filter pathIsRegion paths
            edges          = filter (not . pathIsRegion) paths
            regionTexts    = filter (not .
                                     intersectsWithShape (rects ++ ellipses))
                                    texts
            width          = graphWidth $ entityVal (head sqlGraph)
            height         = graphHeight $ entityVal (head sqlGraph)
            stringSVG      = renderSvg $ makeSVGDoc courseStyleMap
                                                    rects
                                                    ellipses
                                                    edges
                                                    regions
                                                    regionTexts
                                                    styled
                                                    width
                                                    height
        liftIO $ writeFile filename stringSVG
    where
        keyAsInt :: PersistEntity a => Entity a -> Integer
        keyAsInt = fromIntegral . (\(PersistInt64 x) -> x) . head . keyToValues . entityKey

        convertSelectionToStyle :: String -> String
        convertSelectionToStyle courseStatus =
            if isSelected courseStatus
            then "stroke-width:4;"
            else "opacity:0.5;stroke-dasharray:8,5;"

        isSelected :: String -> Bool
        isSelected courseStatus =
            isPrefixOf "active" courseStatus ||
            isPrefixOf "overridden" courseStatus

-- * SVG Creation

-- | This function does the heavy lifting to actually create
-- a new SVG value given the graph components.
buildSVG gId courseMap filename styled =
    runSqlite databasePath $ do
        sqlRects    :: [Entity Shape] <- selectList
                                             [ShapeType_ <-. [Node, Hybrid],
                                              ShapeGraph ==. gId] []
        sqlTexts    :: [Entity Text]  <- selectList [TextGraph ==. gId] []
        sqlPaths    :: [Entity Path]  <- selectList [PathGraph ==. gId] []
        sqlEllipses :: [Entity Shape] <- selectList
                                             [ShapeType_ ==. BoolNode,
                                              ShapeGraph ==. gId] []
        sqlGraph    :: [Entity Graph] <- selectList [GraphId ==. gId] []
        let courseStyleMap = M.map convertSelectionToStyle courseMap
            texts          = map entityVal sqlTexts
            -- TODO: Ideally, we would do these "build" steps *before*
            -- inserting these values into the database.
            rects          = zipWith (buildRect texts)
                                     (map entityVal sqlRects)
                                     (map keyAsInt sqlRects)
            ellipses       = zipWith (buildEllipses texts)
                                     (map entityVal sqlEllipses)
                                     (map keyAsInt sqlEllipses)
            paths          = zipWith (buildPath rects ellipses)
                                     (map entityVal sqlPaths)
                                     (map keyAsInt sqlPaths)
            regions        = filter pathIsRegion paths
            edges          = filter (not . pathIsRegion) paths
            regionTexts    = filter (not .
                                     intersectsWithShape (rects ++ ellipses))
                                    texts
            width          = graphWidth $ entityVal (head sqlGraph)
            height         = graphHeight $ entityVal (head sqlGraph)
            stringSVG      = renderSvg $ makeSVGDoc courseStyleMap
                                                    rects
                                                    ellipses
                                                    edges
                                                    regions
                                                    regionTexts
                                                    styled
                                                    width
                                                    height
        liftIO $ writeFile filename stringSVG
    where
        keyAsInt :: PersistEntity a => Entity a -> Integer
        keyAsInt = fromIntegral . (\(PersistInt64 x) -> x) . head . keyToValues . entityKey

        convertSelectionToStyle :: String -> String
        convertSelectionToStyle courseStatus =
            if isSelected courseStatus
            then "stroke-width:4;"
            else "opacity:0.5;stroke-dasharray:8,5;"

        isSelected :: String -> Bool
        isSelected courseStatus =
            isPrefixOf "active" courseStatus ||
            isPrefixOf "overridden" courseStatus

-- * SVG Creation

-- | This function does the heavy lifting to actually create
-- a new SVG value given the graph components.