{-# LANGUAGE CPP #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FunctionalDependencies #-}
#ifdef TRUSTWORTHY
{-# LANGUAGE Trustworthy #-}
#endif
-----------------------------------------------------------------------------
-- |
-- Module      :  Control.Lens.Internal.Review
-- Copyright   :  (C) 2012-2013 Edward Kmett
-- License     :  BSD-style (see the file LICENSE)
-- Maintainer  :  Edward Kmett <ekmett@gmail.com>
-- Stability   :  provisional
-- Portability :  non-portable
--
----------------------------------------------------------------------------
module Control.Lens.Internal.Review
  (
  -- ** Reviewing
    Reviewable(..)
  , Reviewed(..)
  ) where

import Control.Applicative
import Control.Comonad
import Control.Monad.Fix
import Data.Bifoldable
import Data.Bifunctor
import Data.Bitraversable
import Data.Distributive
import Data.Foldable
import Data.Functor.Bind
import Data.Profunctor
import Data.Profunctor.Rep
import Data.Profunctor.Unsafe
import Data.Proxy
import Data.Tagged
import Data.Traversable
#ifndef SAFE
import Unsafe.Coerce
#endif

-----------------------------------------------------------------------------
-- Reviewable
----------------------------------------------------------------------------

-- | This provides a dual notion to that of 'Control.Lens.Getter.Gettable'.
class Profunctor p => Reviewable p where
  retagged :: p a b -> p s b
  -- retaggedDot, dotRetagged?

instance Reviewable Tagged where
  retagged = retag
  {-# INLINE retagged #-}

------------------------------------------------------------------------------
-- Review: Reviewed
------------------------------------------------------------------------------

-- | This is a profunctor used internally to implement "Review"
--
-- It plays a role similar to that of 'Control.Lens.Internal.Getter.Accessor'
-- or 'Const' do for "Control.Lens.Getter"
newtype Reviewed a b = Reviewed { runReviewed :: b }

instance Reviewable Reviewed where
  retagged (Reviewed b) = Reviewed b
  {-# INLINE retagged #-}

instance Functor (Reviewed a) where
  fmap bc (Reviewed b) = Reviewed (bc b)
  {-# INLINE fmap #-}

instance Apply (Reviewed a) where
  (<.>) a = Reviewed #. runReviewed a .# runReviewed
  {-# INLINE (<.>) #-}
  a <. _ = a
  {-# INLINE (<.) #-}
  _ .> b = b
  {-# INLINE (.>) #-}

instance Applicative (Reviewed a) where
  pure = Reviewed
  (<*>) a = Reviewed #. runReviewed a .# runReviewed
  {-# INLINE (<*>) #-}
  a <* _ = a
  {-# INLINE (<*) #-}
  _ *> b = b
  {-# INLINE (*>) #-}

instance Comonad (Reviewed a) where
  extract = runReviewed
  {-# INLINE extract #-}
  duplicate = Reviewed
  {-# INLINE duplicate #-}
  extend = ( #. ) Reviewed
  {-# INLINE extend #-}

instance ComonadApply (Reviewed a) where
  (<@>) a = Reviewed #. runReviewed a .# runReviewed
  {-# INLINE (<@>) #-}
  a <@ _ = a
  {-# INLINE (<@) #-}
  _ @> b = b
  {-# INLINE (@>) #-}

instance Bind (Reviewed a) where
  Reviewed a >>- f = f a
  {-# INLINE (>>-) #-}

instance Monad (Reviewed a) where
  return = Reviewed
  {-# INLINE return #-}
  Reviewed a >>= f = f a
  {-# INLINE (>>=) #-}
  _ >> a = a
  {-# INLINE (>>) #-}

instance MonadFix (Reviewed a) where
  mfix f = a where a = f (runReviewed a)
  {-# INLINE mfix #-}

instance Foldable (Reviewed a) where
  foldMap f (Reviewed b) = f b
  {-# INLINE foldMap #-}

instance Traversable (Reviewed a) where
  traverse f (Reviewed b) = Reviewed <$> f b
  {-# INLINE traverse #-}

instance Bifunctor Reviewed where
  bimap _ g (Reviewed b) = Reviewed (g b)
  {-# INLINE bimap #-}

instance Bifoldable Reviewed where
  bifoldMap _ g (Reviewed b) = g b
  {-# INLINE bifoldMap #-}

instance Bitraversable Reviewed where
  bitraverse _ g (Reviewed b) = Reviewed <$> g b
  {-# INLINE bitraverse #-}

instance Distributive (Reviewed a) where
  distribute = Reviewed . fmap runReviewed
  {-# INLINE distribute #-}

instance Profunctor Reviewed where
  dimap _ f (Reviewed c) = Reviewed (f c)
  {-# INLINE dimap #-}
  lmap _ (Reviewed c) = Reviewed c
  {-# INLINE lmap #-}
  rmap = fmap
  {-# INLINE rmap #-}
  Reviewed b .# _ = Reviewed b
  {-# INLINE ( .# ) #-}
#ifndef SAFE
  ( #. ) _ = unsafeCoerce
  {-# INLINE ( #. ) #-}
#endif

instance Choice Reviewed where
  left' (Reviewed b) = Reviewed (Left b)
  {-# INLINE left' #-}
  right' (Reviewed b) = Reviewed (Right b)
  {-# INLINE right' #-}

instance Corepresentable Reviewed where
  type Corep Reviewed = Proxy
  cotabulate f = Reviewed (f Proxy)
  {-# INLINE cotabulate #-}
  corep (Reviewed b) Proxy = b
  {-# INLINE corep #-}