{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeApplications #-}
module Data.Geometry.QuadTree.Tree where
import           Control.Lens (makePrisms)
import           Data.Bifoldable
import           Data.Bifunctor
import           Data.Bitraversable
import           Data.Ext
import qualified Data.Foldable as F
import           Data.Functor.Apply
import           Data.Geometry.Point
import           Data.Geometry.QuadTree.Cell
import           Data.Geometry.QuadTree.Quadrants
import           Data.Geometry.QuadTree.Split
import           Data.List.NonEmpty (NonEmpty(..))
import qualified Data.List.NonEmpty as NonEmpty
import           Data.Semigroup.Foldable.Class
import           Data.Semigroup.Traversable.Class
import qualified Data.Tree as RoseTree
import           Data.Tree.Util (TreeNode(..))
data Tree v p = Leaf !p
              | Node !v (Quadrants (Tree v p)) 
              deriving (Show,Eq)
makePrisms ''Tree
instance Bifunctor Tree where
  bimap = bimapDefault
instance Bifoldable Tree where
  bifoldMap = bifoldMapDefault
instance Bitraversable Tree where
  bitraverse f g = \case
    Leaf p    -> Leaf <$> g p
    Node v qs -> Node <$> f v <*> traverse (bitraverse f g) qs
instance Bifoldable1 Tree
instance Bitraversable1 Tree where
  bitraverse1 f g = \case
    Leaf p    -> Leaf <$> g p
    Node v qs -> Node <$> f v <.> traverse1 (bitraverse1 f g) qs
foldTree     :: (p -> b) -> (v -> Quadrants b -> b) -> Tree v p -> b
foldTree f g = go
  where
    go = \case
      Leaf p    -> f p
      Node v qs -> g v (go <$> qs)
leaves :: Tree v p -> NonEmpty p
leaves = NonEmpty.fromList . bifoldMap (const []) (:[])
toRoseTree :: Tree v p -> RoseTree.Tree (TreeNode v p)
toRoseTree = foldTree (\p    -> RoseTree.Node (LeafNode p)     [])
                      (\v qs -> RoseTree.Node (InternalNode v) (F.toList qs))
height :: Tree v p -> Integer
height = foldTree (const 1) (\_ -> (1 +) . maximum)
build             :: Fractional r
                  => Splitter r pts v p -> Cell r -> pts -> Tree v p
build shouldSplit = build'
  where
    build' cc pts = case shouldSplit cc pts of
                      No p     -> Leaf p
                      Yes v qs -> Node v $ build' <$> splitCell cc <*> qs
withCells :: Fractional r => Cell r -> Tree v p -> Tree (v :+ Cell r) (p :+ Cell r)
withCells c0 = \case
  Leaf p    -> Leaf (p :+ c0)
  Node v qs -> Node (v :+ c0) (withCells <$> splitCell c0 <*> qs)
fromPoints :: (Fractional r, Ord r)
           => Cell r -> [Point 2 r :+ p]
           -> Tree () (Maybe (Point 2 r :+ p))
fromPoints = build fromPointsF
fromPointsF   :: (Fractional r, Ord r)
              => Splitter r [Point 2 r :+ p] () (Maybe (Point 2 r :+ p))
fromPointsF c = \case
      []   -> No Nothing
      [p]  -> No (Just p)
      pts  -> Yes () $ partitionPoints c pts