{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE DoRec                #-}

module Data.Layer.Cover where

import Prelude
import Control.Lens
import Control.Monad
import Control.Monad.Fix
import Data.Construction
import Data.Convert
import Data.Layer

-- === Cover ===

newtype Cover a = Cover a deriving (Show, Eq, Ord, Functor, Foldable, Traversable)
type instance Unlayered (Cover a) = a

type family Uncovered a where Uncovered (Cover a) = a
                              Uncovered a         = Uncovered (Unlayered a)

-- Instances

instance Layered   (Cover a)
instance Rewrapped (Cover a) (Cover a')
instance Wrapped   (Cover a) where
    type Unwrapped (Cover a) = a
    _Wrapped' = iso (\(Cover a) -> a) Cover

instance Castable    a a' => Castable    (Cover a) (Cover a') where cast    = _Wrapped %~ cast
instance Convertible a a' => Convertible (Cover a) (Cover a') where convert = _Wrapped %~ convert


-- === Covered ===

-- pure interface

class Covered a where covered :: Lens' a (Uncovered a)
instance {-# OVERLAPPABLE #-} ( Layered a
                              , Covered (Unlayered a)
                              , Uncovered a ~ Uncovered (Unlayered a)
                              ) => Covered a        where covered = layered . covered
instance {-# OVERLAPPABLE #-}      Covered (Cover a) where covered = _Wrapped'

uncover :: Covered a => a -> Uncovered a
uncover = view covered


-- monadic interface

class CoveredM m a where viewCoveredM :: a -> m (Uncovered a)
                         setCoveredM  :: Uncovered a -> a -> m a

instance {-# OVERLAPPABLE #-} ( Monad m
                              , LayeredM m a
                              , CoveredM m (Unlayered a)
                              , Uncovered a ~ Uncovered (Unlayered a)
                              )       => CoveredM m a         where viewCoveredM   = viewLayeredM >=> viewCoveredM
                                                                    setCoveredM    = withLayeredM . setCoveredM
instance {-# OVERLAPPABLE #-} Monad m => CoveredM m (Cover a) where viewCoveredM   = return . uncover
                                                                    setCoveredM  v = return . set covered v


-- === Cover generator ===

class CoverConstructor m a where constructCover :: Uncovered a -> m a
instance {-# OVERLAPPABLE #-} ( CoverConstructor m (Unlayered a)
                              , Uncovered a ~ Uncovered (Unlayered a)
                              , LayerConstructor m a
                              , Monad m ) => CoverConstructor m a         where constructCover = constructCover >=> constructLayer
instance {-# OVERLAPPABLE #-}   Monad m   => CoverConstructor m (Cover a) where constructCover = return . Cover

class CoverConstructorFix m a where constructCoverFix :: Uncovered a -> m a
instance {-# OVERLAPPABLE #-} ( CoverConstructorFix m (Unlayered a)
                              , Uncovered a ~ Uncovered (Unlayered a)
                              , LayerConstructor m a
                              , MonadFix m ) => CoverConstructorFix m a         where
    constructCoverFix base = mdo
        out <- constructLayer l
        l   <- constructCoverFix base 
        return out
instance {-# OVERLAPPABLE #-}   Monad m   => CoverConstructorFix m (Cover a) where constructCoverFix = return . Cover

--class CoverConstructor m a where constructCover :: Uncovered a -> m a
--instance {-# OVERLAPPABLE #-} ( CoverConstructor m (Destructed a)
--                              , Uncovered a ~ Uncovered (Destructed a)
--                              , Constructor m a
--                              , Monad m ) => CoverConstructor m a         where constructCover = constructCover >=> construct
--instance {-# OVERLAPPABLE #-}   Monad m   => CoverConstructor m (Cover a) where constructCover = return . Cover

--class CoverDestructor m a where destructCover :: a -> m (Uncovered a)
--instance {-# OVERLAPPABLE #-} ( Uncovered a ~ Uncovered (Destructed a)
--                              , CoverDestructor m (Destructed a)
--                              , Destructor m a
--                              , Monad m ) => CoverDestructor m a         where destructCover = destruct >=> destructCover
--instance {-# OVERLAPPABLE #-}   Monad m   => CoverDestructor m (Cover a) where destructCover = return . view _Wrapped'

--class Destruction m a where destroy :: a -> m (Uncovered a)
--instance {-# OVERLAPPABLE #-} Monad m => Destruction m (Cover a) where destroy = return . unlayer
--instance {-# OVERLAPPABLE #-} Monad m => Destruction m (Cover a) where destroy = return . unlayer

--type family Deconstructed a
--class Construction   m a where construct   :: Deconstructed a -> m a
--class Deconstruction m a where deconstruct :: a               -> m (Deconstructed a)
-- >//>
-- >\\>
-- >><>