content: {-# LANGUAGE DeriveTraversable #-}
  {-# LANGUAGE TypeFamilies      #-}
  
  -- | Chronological sequences.
  
  module Pos.Core.Chrono
         ( Chrono
         , NewestFirst(..)
         , _NewestFirst
         , OldestFirst(..)
         , _OldestFirst
         , toNewestFirst
         , toOldestFirst
         , NE
         , nonEmptyOldestFirst
         , nonEmptyNewestFirst
         , splitAtNewestFirst
         , splitAtOldestFirst
         ) where
  
  import           Universum
  
  import           Control.Lens (makePrisms, makeWrapped, _Wrapped)
  import qualified Control.Lens as Lens (Each (..))
  import           Data.Coerce (coerce)
  import qualified Data.List.NonEmpty as NE
  import           Data.Semigroup (Semigroup)
  import qualified GHC.Exts as IL
  
  import           Pos.Binary.Class (Bi)
  
  newtype NewestFirst f a = NewestFirst {getNewestFirst :: f a}
    deriving (Eq, Ord, Show,
              Functor, Foldable, Traversable,
              Container,
              Bi,
              NFData)
  newtype OldestFirst f a = OldestFirst {getOldestFirst :: f a}
    deriving (Eq, Ord, Show,
              Functor, Foldable, Traversable,
              Container,
              Bi,
              NFData)
  
  makePrisms  ''NewestFirst
  makeWrapped ''NewestFirst
  
  makePrisms  ''OldestFirst
  makeWrapped ''OldestFirst
  
  deriving instance Semigroup (f a) => Semigroup (NewestFirst f a)
  deriving instance Semigroup (f a) => Semigroup (OldestFirst f a)
  
  instance Lens.Each (f a) (f b) a b =>
           Lens.Each (NewestFirst f a) (NewestFirst f b) a b where
      each = _Wrapped . Lens.each
  instance Lens.Each (f a) (f b) a b =>
           Lens.Each (OldestFirst f a) (OldestFirst f b) a b where
      each = _Wrapped . Lens.each
  
  instance One (f a) => One (NewestFirst f a) where
      type OneItem (NewestFirst f a) = OneItem (f a)
      one = NewestFirst . one
  instance One (f a) => One (OldestFirst f a) where
      type OneItem (OldestFirst f a) = OneItem (f a)
      one = OldestFirst . one
  
  instance IL.IsList (f a) => IL.IsList (NewestFirst f a) where
      type Item (NewestFirst f a) = IL.Item (f a)
      toList = IL.toList . getNewestFirst
      fromList = NewestFirst . IL.fromList
  
  instance IL.IsList (f a) => IL.IsList (OldestFirst f a) where
      type Item (OldestFirst f a) = IL.Item (f a)
      toList = IL.toList . getOldestFirst
      fromList = OldestFirst . IL.fromList
  
  class Chrono f where
      toNewestFirst :: OldestFirst f a -> NewestFirst f a
      toOldestFirst :: NewestFirst f a -> OldestFirst f a
  
  instance Chrono [] where
      toNewestFirst = NewestFirst . reverse . getOldestFirst
      toOldestFirst = OldestFirst . reverse . getNewestFirst
  
  instance Chrono NonEmpty where
      toNewestFirst = NewestFirst . NE.reverse . getOldestFirst
      toOldestFirst = OldestFirst . NE.reverse . getNewestFirst
  
  type NE = NonEmpty
  
  nonEmptyOldestFirst ::
      forall a.
         OldestFirst [] a
      -> Maybe (OldestFirst NE a)
  nonEmptyOldestFirst = coerce (nonEmpty @a)
  
  nonEmptyNewestFirst ::
      forall a.
         NewestFirst [] a
      -> Maybe (NewestFirst NE a)
  nonEmptyNewestFirst = coerce (nonEmpty @a)
  
  splitAtOldestFirst ::
      forall a.
         Int
      -> OldestFirst NE a
      -> (OldestFirst [] a, OldestFirst [] a)
  splitAtOldestFirst = coerce (NE.splitAt @a)
  
  splitAtNewestFirst ::
      forall a.
         Int
      -> NewestFirst NE a
      -> (NewestFirst [] a, NewestFirst [] a)
  splitAtNewestFirst = coerce (NE.splitAt @a)
  