module Data.AccNotZeroOr(
  AccNotZeroOr(..)
, _IsAccNotZero
, _OrAccNotZero
, isoAccNotZeroOr
, OneNotZeroOr
, isoOneNotZeroOr
, isoOneNotZeroOrNumber
, isoOneNotZeroOrT
) where

import Control.Applicative(Applicative(pure, (<*>)))
import Control.Category(Category((.)))
import Control.Lens(Prism, prism, Iso, iso)
import Data.Either(Either(Left, Right))
import Data.Eq(Eq)
import Data.Functor.Alt
import Data.Functor.Identity(Identity(Identity))
import Data.Monoid(Monoid(mappend, mempty))
import Data.NotZero(NotZero)
import Data.NotZeroOr(NotZeroOr(IsNotZero, OrNotZero), NotZeroOrT, isoNumber, isoNotZeroOrT)
import Data.Semigroup(Semigroup((<>)))
import Prelude(Num)

data AccNotZeroOr f a x =
  IsAccNotZero (f (NotZero a))
  | OrAccNotZero x

_IsAccNotZero ::
  Prism (AccNotZeroOr f a x) (AccNotZeroOr f b x) (f (NotZero a)) (f (NotZero b))
_IsAccNotZero =
  prism
    IsAccNotZero
    (\z -> case z of
              IsAccNotZero o ->
                Right o
              OrAccNotZero x ->
                Left (OrAccNotZero x))

_OrAccNotZero ::
  Prism (AccNotZeroOr f a x) (AccNotZeroOr f a y) x y
_OrAccNotZero =
  prism
    OrAccNotZero
    (\z -> case z of
              IsAccNotZero o ->
                Left (IsAccNotZero o)
              OrAccNotZero x ->
                Right x)

isoAccNotZeroOr ::
  Iso (AccNotZeroOr f a x) (AccNotZeroOr g a x) (Either (f (NotZero a)) x) (Either (g (NotZero a)) x)
isoAccNotZeroOr =
  iso
    (\z -> case z of
              IsAccNotZero o ->
                Left o
              OrAccNotZero x ->
                Right x)
    (\e -> case e of
              Left o ->
                IsAccNotZero o
              Right x ->
                OrAccNotZero x)

type OneNotZeroOr a x =
  AccNotZeroOr Identity a x

isoOneNotZeroOr ::
  Iso (OneNotZeroOr a x) (OneNotZeroOr b y) (NotZeroOr a x) (NotZeroOr b y) 
isoOneNotZeroOr =
  iso
    (\z -> case z of
              IsAccNotZero (Identity o) ->
                IsNotZero o
              OrAccNotZero x ->
                OrNotZero x)
    (\z -> case z of
              IsNotZero o ->
                IsAccNotZero (Identity o)
              OrNotZero x ->
                OrAccNotZero x)

isoOneNotZeroOrNumber ::
  (Eq a, Num a) =>
  Iso (OneNotZeroOr a ()) (OneNotZeroOr a ()) a a
isoOneNotZeroOrNumber =
  isoOneNotZeroOr . isoNumber

isoOneNotZeroOrT ::
  Iso (OneNotZeroOr a x) (OneNotZeroOr b y) (NotZeroOrT a Identity x) (NotZeroOrT b Identity y)
isoOneNotZeroOrT =
  isoOneNotZeroOr . isoNotZeroOrT

instance Semigroup (AccNotZeroOr f a x) where
  OrAccNotZero x <> _ =
    OrAccNotZero x
  IsAccNotZero _ <> y =
    y
    
instance Monoid x => Monoid (AccNotZeroOr f a x) where
  mappend =
    (<>)
  mempty =
    OrAccNotZero mempty

instance Functor f => Functor (AccNotZeroOr f a) where
  fmap _ (IsAccNotZero z) =
    IsAccNotZero z
  fmap f (OrAccNotZero x) =
    OrAccNotZero (f x)

instance Alt f => Apply (AccNotZeroOr f a) where
  IsAccNotZero z1 <.> IsAccNotZero z2 =
    IsAccNotZero (z1 <!> z2)
  IsAccNotZero z1 <.> OrAccNotZero _ =
    IsAccNotZero z1
  OrAccNotZero _ <.> IsAccNotZero z2 =
    IsAccNotZero z2
  OrAccNotZero f <.> OrAccNotZero a =
    OrAccNotZero (f a)
                  
instance Alt f => Applicative (AccNotZeroOr f a) where
  pure =
    OrAccNotZero
  (<*>) =
    (<.>)