module Pandora.Paradigm.Primary.Functor.Conclusion (Conclusion (..), Failable, conclusion, fail, failure) where

import Pandora.Core.Functor (type (~>))
import Pandora.Pattern.Category ((.))
import Pandora.Paradigm.Controlflow.Joint.Interpreted (Interpreted (Primary, run))
import Pandora.Paradigm.Controlflow.Joint.Transformer.Monadic (Monadic (lay, wrap), (:>) (TM))
import Pandora.Paradigm.Controlflow.Joint.Schematic (Schematic)
import Pandora.Paradigm.Controlflow.Joint.Adaptable (Adaptable (adapt))
import Pandora.Paradigm.Controlflow.Joint.Schemes.UT (UT (UT))
import Pandora.Pattern.Functor.Covariant (Covariant ((<$>), (<$$>)))
import Pandora.Pattern.Functor.Pointable (Pointable (point))
import Pandora.Pattern.Functor.Alternative (Alternative ((<+>)))
import Pandora.Pattern.Functor.Applicative (Applicative ((<*>), apply))
import Pandora.Pattern.Functor.Traversable (Traversable ((->>)))
import Pandora.Pattern.Functor.Bindable (Bindable ((>>=)))
import Pandora.Pattern.Functor.Monad (Monad)
import Pandora.Pattern.Functor.Bivariant (Bivariant ((<->)))
import Pandora.Pattern.Functor.Divariant (($))
import Pandora.Pattern.Object.Setoid (Setoid ((==)), Boolean (False))
import Pandora.Pattern.Object.Chain (Chain ((<=>)), Ordering (Less, Greater))
import Pandora.Pattern.Object.Semigroup (Semigroup ((+)))

data Conclusion e a = Failure e | Success a

instance Covariant (Conclusion e) where
        f <$> Success x = Success $ f x
        _ <$> Failure y = Failure y

instance Pointable (Conclusion e) where
        point = Success

instance Applicative (Conclusion e) where
        Success f <*> x = f <$> x
        Failure y <*> _ = Failure y

instance Alternative (Conclusion e) where
        Failure _ <+> x = x
        Success x <+> _ = Success x

instance Traversable (Conclusion e) where
        Failure y ->> _ = point $ Failure y
        Success x ->> f = Success <$> f x

instance Bindable (Conclusion e) where
        Success x >>= f = f x
        Failure y >>= _ = Failure y

instance Monad (Conclusion e) where

instance Bivariant Conclusion where
    f <-> g = conclusion (Failure . f) (Success . g)

instance (Setoid e, Setoid a) => Setoid (Conclusion e a) where
        Success x == Success y = x == y
        Failure x == Failure y = x == y
        _ == _ = False

instance (Chain e, Chain a) => Chain (Conclusion e a) where
        Success x <=> Success y = x <=> y
        Failure x <=> Failure y = x <=> y
        Failure _ <=> Success _ = Less
        Success _ <=> Failure _ = Greater

instance (Semigroup e, Semigroup a) => Semigroup (Conclusion e a) where
        Success x + Success y = Success $ x + y
        Failure x + Failure y = Failure $ x + y
        Failure _ + Success y = Success y
        Success x + Failure _ = Success x

conclusion :: (e -> r) -> (a -> r) -> Conclusion e a -> r
conclusion f _ (Failure x) = f x
conclusion _ s (Success x) = s x

fail :: (e -> r) -> Conclusion e ~> Conclusion r
fail f (Failure x) = Failure $ f x
fail _ (Success y) = Success y

instance Interpreted (Conclusion e) where
        type Primary (Conclusion e) a = Conclusion e a
        run x = x

type instance Schematic Monad (Conclusion e) u = UT Covariant Covariant (Conclusion e) u

instance Monadic (Conclusion e) where
        lay x = TM . UT $ Success <$> x
        wrap x = TM . UT . point $ x

type Failable e = Adaptable (Conclusion e)

instance Covariant u => Covariant (UT Covariant Covariant (Conclusion e) u) where
        f <$> UT x = UT $ f <$$> x

instance Applicative u => Applicative (UT Covariant Covariant (Conclusion e) u) where
        UT f <*> UT x = UT $ apply <$> f <*> x

instance Pointable u => Pointable (UT Covariant Covariant (Conclusion e) u) where
        point = UT . point . point

instance (Pointable u, Bindable u) => Bindable (UT Covariant Covariant (Conclusion e) u) where
        UT x >>= f = UT $ x >>= conclusion (point . Failure) (run . f)

instance Monad u => Monad (UT Covariant Covariant (Conclusion e) u) where

failure :: Failable e t => e -> t a
failure = adapt . Failure