-- |
-- Module      : Lens.Explicit.Core
-- Copyright   : (c) Justus Sagemüller 2017
-- License     : GPL v3
-- 
-- Maintainer  : (@) sagemueller $ geo.uni-koeln.de
-- Stability   : experimental
-- Portability : portable
-- 

{-# LANGUAGE GADTs                        #-}
{-# LANGUAGE UnicodeSyntax                #-}
{-# LANGUAGE Rank2Types                   #-}
{-# LANGUAGE KindSignatures               #-}
{-# LANGUAGE TypeFamilies                 #-}
{-# LANGUAGE FlexibleInstances            #-}
{-# LANGUAGE FlexibleContexts             #-}

module Lens.Explicit.Core where
    
import Prelude hiding (id, (.))
import Control.Category
import Control.Monad ((>=>))
import Control.Applicative (Const(..))
import Control.Arrow ((+++))
import Data.Functor.Identity

import GHC.Exts (Constraint)


type Optic c s t a b = OpticC c (a,b) (s,t)
data OpticC c x y where
  Equality :: OpticC c q q
  OpticC :: c s t a b -> Optic c s t a b

class (Category (OpticC c)) => Optical c where
  type OptDens c (ζ :: * -> * -> * -> * -> *) :: Constraint
  cloneOptic :: OptDens c ζ => c s t a b -> ζ s t a b
  () :: c x y s t -> c s t a b -> c x y a b

instance (Optical c) => Category (OpticC c) where
  id = Equality
  Equality . f = f
  f . Equality = f
  OpticC f . OpticC g = OpticC $ f  g

  

-- ⣿⢠⡤⠤⢀⣤⢤⣄⢠⡤⠤
-- ⣿⢈⣛⡷⠸⣧⣠⡿⢈⣛⡷

data IsoTrait s t a b = Iso (s -> a) (b -> t)
instance Category (OpticC IsoTrait) where
  id = Equality
  Equality . f = f
  f . Equality = f
  OpticC (Iso g γ) . OpticC (Iso f φ) = OpticC $ Iso (f . g) (γ . φ)

type AnIso s t a b = Optic IsoTrait s t a b
type Iso s t a b =  c . FromIso c => Optic c s t a b

class FromIso c where
  iso :: (s -> a) -> (b -> t) -> c s t a b
instance FromIso IsoTrait where
  iso = Iso
instance FromIso GetterTrait where
  iso f _ = Getter f
instance FromIso ReviewTrait where
  iso _ φ = Review φ
instance FromIso LensTrait where
  iso f φ = Lens f (\_ b -> φ b)
instance FromIso PrismTrait where
  iso f φ = Prism φ (Right . f)
instance FromIso SetterTrait where
  iso f φ = Setter $ \u -> φ . u . f
instance FromIso FoldTrait where
  iso f _ = Fold (\t -> t . f)
instance FromIso TraversalTrait where
  iso f φ = Traversal (\t -> fmap φ . t . f)


-- ⣿⠀⠀⣠⠤⣄⢠⡤⢤⡀⣤⠤⠄⣠⠤⣄⢠⡤⠤
-- ⣿⣀⣀⢿⣒⣛⢸⡇⢸⡇⣙⣻⠆⢿⣒⣛⢈⣛⡷

data LensTrait s t a b = Lens (s -> a) (s -> b -> t)

instance Optical LensTrait where
  type OptDens LensTrait ζ = FromLens ζ
  cloneOptic (Lens f φ) = lens f φ
  Lens g γ  Lens f φ = Lens (f . g) (\s b -> γ s $ φ (g s) b)

type ALens s t a b = Optic LensTrait s t a b
type Lens s t a b =  c . FromLens c => Optic c s t a b

class FromIso c => FromLens c where
  lens :: (s -> a) -> (s -> b -> t) -> c s t a b
instance FromLens GetterTrait where
  lens f _ = Getter f
instance FromLens LensTrait where
  lens = Lens
instance FromLens TraversalTrait where
  lens f φ = Traversal (\τ s -> fmap (φ s) . τ $ f s)
instance FromLens FoldTrait where
  lens f _ = Fold (\τ -> τ . f)
instance FromLens SetterTrait where
  lens f φ = Setter (\τ s -> φ s . τ $ f s)


-- ⣿⢉⣿⢠⣄⡄⣭⢠⡤⠤⢠⡤⢤⡤⢤⡄⣤⠤⠄
-- ⣿⠉⠁⢸⡇⠀⣿⢈⣛⡷⢸⡇⢸⡇⢸⡇⣙⣻⠆

data PrismTrait s t a b = Prism (b -> t) (s -> Either t a)

instance Optical PrismTrait where
  type OptDens PrismTrait ζ = FromPrism ζ
  cloneOptic (Prism f φ) = prism f φ
  Prism γ g  Prism φ f = Prism (γ . φ) (g >=> (γ+++id) . f)

type APrism s t a b = Optic PrismTrait s t a b
type Prism s t a b =  c . FromPrism c => Optic c s t a b

class FromIso c => FromPrism c where
  prism :: (b -> t) -> (s -> Either t a) -> c s t a b
instance FromPrism PrismTrait where
  prism = Prism
instance FromPrism TraversalTrait where
  prism φ f = Traversal (\τ -> either pure (fmap φ . τ) . f)
instance FromPrism FoldTrait where
  prism φ f = Fold (\τ -> either (const mempty) τ . f)
instance FromPrism SetterTrait where
  prism φ f = Setter (\τ -> either id (φ . τ) . f)
instance FromPrism ReviewTrait where
  prism φ _ = Review φ


-- ⣴⠋⠉⠁⣠⠤⣄⢼⡧⠠⣿⢄⡤⢤⡀⣤⣠⢠⡤⠤
-- ⠻⣄⣸⡇⢿⣒⣛⢸⣇⠀⣿⡸⣗⣚⡃⣿⠀⢈⣛⡷

data GetterTrait s t a b = Getter (s -> a)

instance Optical GetterTrait where
  type OptDens GetterTrait ζ = FromGetter ζ
  cloneOptic (Getter f) = to f
  Getter g  Getter f = Getter (f . g)

type AGetter s a = Optic GetterTrait s s a a
type Getter s t a b =  c . FromGetter c => Optic c s t a b

class FromLens c => FromGetter c where
  to :: (s -> a) -> c s t a b
instance FromGetter GetterTrait where
  to = Getter
instance FromGetter FoldTrait where
  to f = Fold (\t -> t . f)


-- ⣿⢉⡷⢀⡤⢤⡠⣄⠀⡤⢨⡅⣠⠤⣄⢠⡄⢠⡄⡠⢠⡤⠤
-- ⣿⠙⣧⡸⣗⣚⡃⢹⣶⠁⢸⡇⢿⣒⣛⠀⣿⠇⣧⠇⢈⣛⡷

data ReviewTrait s t a b = Review (b -> t)
instance Category (OpticC ReviewTrait) where
  id = Equality
  Equality . f = f
  f . Equality = f
  OpticC (Review η) . OpticC (Review θ) = OpticC $ Review (η . θ)

type AReview b t = Optic ReviewTrait t t b b
type Review s t a b =  c . FromReview c => Optic c s t a b

class FromPrism c => FromReview c where
  unto :: (b -> t) -> c s t a b
instance FromReview ReviewTrait where
  unto = Review


-- ⠉⢹⡏⠉⣤⣠⢠⢤⡀⢤⡀⢠⢄⡤⢤⡀⣤⣠⢠⡤⠤⠀⡤⣄⠀⣿⢠⡤⠤
-- ⠀⢸⡇⠀⣿⠀⢿⡹⣇⠈⣷⡎⠸⣗⣚⡃⣿⠀⢈⣛⡷⠸⣏⢿⡀⣿⢈⣛⡷

data TraversalTrait s t a b = Traversal ( f . Applicative f => (a -> f b) -> s -> f t)

instance Optical TraversalTrait where
  type OptDens TraversalTrait ζ = FromTraversal ζ
  cloneOptic (Traversal η) = traversed η
  Traversal η  Traversal θ = Traversal (η . θ)

type ATraversal s t a b = Optic TraversalTrait s t a b
type Traversal s t a b =  c . FromTraversal c => Optic c s t a b

class (FromLens c, FromPrism c) => FromTraversal c where
  traversed :: ( f . Applicative f => (a -> f b) -> s -> f t) -> c s t a b
instance FromTraversal TraversalTrait where
  traversed = Traversal
instance FromTraversal SetterTrait where
  traversed θ = Setter (\f -> runIdentity . θ (Identity . f))
instance FromTraversal FoldTrait where
  traversed θ = Fold (\t -> getConst . θ (Const . t))


-- ⣾⣍⠁⢀⡤⢤⡠⣿⠄⢼⡧⣠⠤⣄⢠⣄⡄⣤⠤⠄
-- ⣀⣉⡿⠸⣗⣚⡃⣿⡀⢸⣇⢿⣒⣛⢸⡇⠀⣙⣻⠆

data SetterTrait s t a b = Setter ((a -> b) -> s -> t)

instance Optical SetterTrait where
  type OptDens SetterTrait ζ = FromSetter ζ
  cloneOptic (Setter η) = sets η
  Setter s  Setter σ = Setter $ s . σ

type ASetter s t a b = Optic SetterTrait s t a b
type Setter s t a b =  c . FromSetter c => Optic c s t a b

class FromTraversal c => FromSetter c where
  sets :: ((a -> b) -> s -> t) -> c s t a b
instance FromSetter SetterTrait where
  sets = Setter


-- ⣿⣉⢁⣤⢤⣄⢸⡇⣠⠤⣿⢠⡤⠤
-- ⣿⠀⠸⣧⣠⡿⢸⡇⢿⣀⣿⢈⣛⡷

data FoldTrait s t a b = Fold ( r . Monoid r => (a -> r) -> s -> r)

instance Optical FoldTrait where
  type OptDens FoldTrait ζ = FromFold ζ
  cloneOptic (Fold η) = folds η
  Fold η  Fold θ = Fold (η . θ)

type AFold s t a b = Optic FoldTrait s t a b
type Fold s t a b =  c . FromFold c => Optic c s t a b

class FromTraversal c => FromFold c where
  folds :: ( r . Monoid r => (a -> r) -> s -> r) -> c s t a b
  folded :: Foldable f => c (f a) t a b
  folded = folds foldMap
instance FromFold FoldTrait where
  folds = Fold