module Data.Apart.Shape (Shape (..)) where

import "base" Data.Bifoldable (Bifoldable (..))
import "base" Data.Bifunctor (Bifunctor (..))
import "base" Data.Bitraversable (Bitraversable (..))
import "base" Data.Semigroup (Semigroup (..))
import "semigroupoids" Data.Functor.Apply (Apply (..))
import "semigroupoids" Data.Functor.Alt (Alt (..))

-- | Type that can tell you about aggregate state of your structure.
data Shape t raw value
        = Ready (t value) -- ^ Segment of values in memory
        | Converted raw -- ^ Segment of values somewhere else

instance (Show (t value), Show value, Show raw) => Show (Shape t raw value) where
        show (Ready values)  = show values
        show (Converted raw) = "{" <> show raw <> "}"

instance Functor t => Functor (Shape t raw) where
        fmap f (Ready values)  = Ready $ f <$> values
        fmap f (Converted raw) = Converted raw

instance Apply t => Apply (Shape t raw) where
        Ready fs <.> Ready xs = Ready $ fs <.> xs
        Ready fs <.> Converted raw = Converted raw
        Converted raw <.> _ = Converted raw

instance Alt t => Alt (Shape t raw) where
        Converted raw <!> x = x
        Ready xs <!> _ = Ready xs

instance Foldable t => Foldable (Shape t raw) where
        foldr f acc (Ready values)  = foldr f acc values
        foldr f acc (Converted raw) = acc

instance Traversable t => Traversable (Shape t raw) where
        traverse f (Ready values)  = Ready <$> traverse f values
        traverse _ (Converted raw) = pure $ Converted raw

instance Functor t => Bifunctor (Shape t) where
        bimap _ f (Ready values)  = Ready $ f <$> values
        bimap g _ (Converted raw) = Converted $ g raw

instance Foldable t => Bifoldable (Shape t) where
        bifoldr _ f acc (Ready values)  = foldr f acc values
        bifoldr g _ acc (Converted raw) = g raw acc

instance Traversable t => Bitraversable (Shape t) where
        bitraverse _ f (Ready values)  = Ready <$> traverse f values
        bitraverse g _ (Converted raw) = Converted <$> g raw