{-# LANGUAGE CPP #-}
{-# LANGUAGE Rank2Types,GADTs, DataKinds, TypeOperators #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE DeriveTraversable #-}
#if __GLASGOW_HASKELL__ < 710
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE DeriveFoldable #-}
#endif

-----------------------------------------------------------------------------
-- |
-- Module      :  Data.Sequence.Queue
-- Copyright   :  (c) Atze van der Ploeg 2014
-- License     :  BSD-style
-- Maintainer  :  atzeus@gmail.org
-- Stability   :  provisional
-- Portability :  portable
--
-- A sequence, a queue, with amortized constant time: '|>', and 'viewl'.
-- It also supports '|>' and 'viewr' in amortized logarithmic time.
--
-- A simplified version of Okasaki's implicit recursive
-- slowdown queues. 
-- See purely functional data structures by Chris Okasaki 
-- section 8.4: Queues based on implicit recursive slowdown
--
-----------------------------------------------------------------------------
module Data.Sequence.Queue.Internal
  ( Queue (..)
  , P (..)
  , B (..)
  )  where
import Control.Applicative (pure, (<*>), (<$>))
import Data.Foldable
import Data.Monoid (Monoid (..), (<>))
import Data.Traversable
import qualified Text.Read as TR
import Data.Function (on)
#if MIN_VERSION_base(4,9,0)
import qualified Data.Semigroup as Semigroup
import Data.Functor.Classes (Show1 (..))
#endif

import Prelude hiding (foldr,foldl)
import Data.SequenceClass

data P a = a :* a 
  deriving (Functor, Foldable, Traversable)

data B a where
  B1 :: a    -> B a
  B2 :: !(P a)  -> B a

deriving instance Functor B
deriving instance Foldable B
deriving instance Traversable B

-- | A queue.
data Queue a  where
  Q0 :: Queue a 
  Q1 :: a  -> Queue a
  QN :: !(B a) -> Queue (P a) -> !(B a) -> Queue a

deriving instance Functor Queue
-- The derived Foldable instance has an optimal null
-- with a good unfolding. No need to fuss around with it.
deriving instance Foldable Queue
deriving instance Traversable Queue

#if MIN_VERSION_base(4,9,0)
instance Semigroup.Semigroup (Queue a) where
  (<>) = (><)
#endif
instance Monoid (Queue a) where
  mempty = empty
#if MIN_VERSION_base(4,9,0)
  mappend = (Semigroup.<>)
#else
  mappend = (><)
#endif

instance Sequence Queue where
  empty = Q0
  singleton = Q1 
  q |> b = case q of
    Q0             -> Q1 b
    Q1 a           -> QN (B1 a) Q0 (B1 b)
    QN l m (B1 a)  -> QN l m (B2 (a :* b))
    QN l m (B2 r)  -> QN l (m |> r) (B1 b)

  a <| q = case q of
    Q0 -> Q1 a
    Q1 b -> QN (B1 a) Q0 (B1 b)
    QN (B1 b) m r -> QN (B2 (a :* b)) m r
    QN (B2 l) m r -> QN (B1 a) (l <| m) r

  (><) = foldl' (|>)

  viewl q0 = case q0 of
    Q0                    -> EmptyL
    Q1 a                  -> a :< Q0
    QN (B2 (a :* b)) m r  -> a :< QN (B1 b) m r
    QN (B1 a) m r         -> a :< shiftLeft m r
    where  
           shiftLeft q r = case viewl q of
               EmptyL -> buf2queue r
               l :< m -> QN (B2 l) m r

  viewr q0 = case q0 of
    Q0 -> EmptyR
    Q1 a -> Q0 :> a
    QN l m (B2 (a :* b)) -> QN l m (B1 a) :> b
    QN l m (B1 a) -> shiftRight l m :> a
    where
      shiftRight l q = case viewr q of
        EmptyR -> buf2queue l
        m :> r -> QN l m (B2 r)

buf2queue :: B a -> Queue a
buf2queue (B1 a)        = Q1 a
buf2queue (B2 (a :* b))  = QN (B1 a) Q0 (B1 b)
{-# INLINE buf2queue #-}

instance Show a => Show (Queue a) where
    showsPrec p xs = showParen (p > 10) $
        showString "fromList " . shows (toList xs)

#if MIN_VERSION_base(4,9,0)
instance Show1 Queue where
  liftShowsPrec _shwsPrc shwList p xs = showParen (p > 10) $
        showString "fromList " . shwList (toList xs)
#endif

instance Read a => Read (Queue a) where
    readPrec = TR.parens $ TR.prec 10 $ do
        TR.Ident "fromList" <- TR.lexP
        xs <- TR.readPrec
        return (fromList xs)

    readListPrec = TR.readListPrecDefault

instance Eq a => Eq (Queue a) where
  (==) = (==) `on` toList

instance Ord a => Ord (Queue a) where
  compare = compare `on` toList