module Data.Functor.Composition.TT (TT (..)) where

import "morphisms" Control.Morphism ((.), ($))

import Control.Functor.Covariant (Covariant ((<$>), comap))
import Control.Functor.Covariant.Extractable (Extractable (extract))
import Control.Functor.Covariant.Exclusive (Exclusive (exclusive))
import Control.Functor.Covariant.Pointable (Pointable (point))
import Control.Functor.Covariant.Alternative (Alternative ((<+>)))
import Control.Functor.Covariant.Applicative (Applicative ((<*>), apply))
import Control.Functor.Covariant.Composition.Adjoint (Adjoint (phi, psi))
import Control.Functor.Contravariant (Contravariant ((>$<), contramap))
import Control.Variance (Variant (Co, Contra), (:.:))

newtype TT ct cu cv t u v a = TT { tt :: (t :.: u :.: v) a }

instance (Covariant t, Covariant u, Covariant v) => Covariant (TT Co Co Co t u v) where
        f <$> TT x = TT $ (comap . comap . comap) f x

instance (Covariant t, Covariant u, Contravariant v) => Contravariant (TT Co Co Contra t u v) where
        f >$< TT x = TT $ (comap . comap) (contramap f) x

instance (Covariant t, Contravariant u, Covariant v) => Contravariant (TT Co Contra Co t u v) where
        f >$< TT x = TT $ contramap (comap f) <$> x

instance (Contravariant t, Covariant u, Covariant v) => Contravariant (TT Contra Co Co t u v) where
        f >$< TT x = TT $ comap (comap f) >$< x

instance (Contravariant t, Contravariant u, Covariant v) => Covariant (TT Contra Contra Co t u v) where
        f <$> TT x = TT $ contramap (comap f) >$< x

instance (Covariant t, Contravariant u, Contravariant v) => Covariant (TT Co Contra Contra t u v) where
        f <$> TT x = TT $ contramap (contramap f) <$> x

instance (Contravariant t, Covariant u, Contravariant v) => Covariant (TT Contra Co Contra t u v) where
        f <$> TT x = TT $ comap (contramap f) >$< x

instance (Contravariant t, Contravariant u, Contravariant v) => Contravariant (TT Contra Contra Contra t u v) where
        f >$< TT x = TT $ (contramap . contramap . contramap) f x

instance (Applicative t, Applicative u, Applicative v) => Applicative (TT Co Co Co t u v) where
        TT f <*> TT x = TT $ (comap apply . (comap . comap) apply $ f) <*> x

instance (Alternative t, Covariant u, Covariant v) => Alternative (TT Co Co Co t u v) where
        TT x <+> TT y = TT $ x <+> y

instance (Exclusive t, Covariant u, Covariant v) => Exclusive (TT Co Co Co t u v) where
        exclusive = TT exclusive

instance (Pointable t, Pointable u, Pointable v) => Pointable (TT Co Co Co t u v) where
        point = TT . point . point . point

instance (Extractable t, Extractable u, Extractable v) => Extractable (TT Co Co Co t u v) where
        extract = extract . extract . extract . tt

type Adjunctive t u = (Extractable t, Pointable t, Extractable u, Pointable u, Adjoint t u)

instance (Adjunctive t w, Adjunctive v x, Adjunctive u y) => Adjoint (TT Co Co Co t v u) (TT Co Co Co w x y) where
        phi f = point . f . point
        psi f = extract . extract . comap f