{- 
    Copyright 2009-2010 Mario Blazevic

    This file is part of the Streaming Component Combinators (SCC) project.

    The SCC project is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
    License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later
    version.

    SCC is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
    of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more details.

    You should have received a copy of the GNU General Public License along with SCC.  If not, see
    <http://www.gnu.org/licenses/>.
-}

-- | This module defines class 'Coercible' and its instances.

{-# LANGUAGE Rank2Types, ScopedTypeVariables, MultiParamTypeClasses, 
             FlexibleContexts, FlexibleInstances, IncoherentInstances #-}
{-# OPTIONS_HADDOCK hide #-}

module Control.Concurrent.SCC.Coercions
   (
   -- * Coercible class
      Coercible(..),
   -- * Splitter isomorphism
      adaptSplitter
   )
where

import Prelude hiding ((.))
import Control.Category ((.))
import Control.Monad (liftM)
import Data.Text (Text, pack, unpack)
   
import Control.Monad.Coroutine
import Control.Monad.Parallel (MonadParallel(..))

import Control.Concurrent.SCC.Streams
import Control.Concurrent.SCC.Types


-- | Two streams of 'Coercible' types can be unambigously converted one to another.
class Coercible x y where
   -- | A 'Transducer' that converts a stream of one type to another.
   coerce :: Monad m => Transducer m x y
   adaptConsumer :: Monad m => Consumer m y r -> Consumer m x r
   adaptConsumer consumer = isolateConsumer $ \source-> liftM snd $ pipe (transduce coerce source) (consume consumer)
   adaptProducer :: Monad m => Producer m x r -> Producer m y r
   adaptProducer producer = isolateProducer $ \sink-> liftM fst $ pipe (produce producer) (flip (transduce coerce) sink)

instance Coercible x x where
   coerce = Transducer pour
   adaptConsumer = id
   adaptProducer = id

instance Coercible Char Text where
   coerce = Transducer (mapStreamChunks ((:[]) . pack))

instance Coercible Text Char where
   coerce = statelessTransducer unpack

instance Coercible x y => Coercible [x] y where
   coerce = coerce . statelessTransducer id

instance Coercible x y => Coercible (Markup b x) y where
   coerce = coerce . statelessTransducer unmark
      where unmark (Content x) = [x]
            unmark (Markup _) = []

-- | Adjusts the argument splitter to split the stream of a data type 'Isomorphic' to the type it was meant to split.
adaptSplitter :: forall m x y b. (Monad m, Coercible x y, Coercible y x) => Splitter m x b -> Splitter m y b
adaptSplitter sx = 
   isolateSplitter $ \source true false edge->
   pipe 
      (transduce coerce source) 
      (\source'-> 
        pipe 
           (\true'-> 
             pipe
                (\false'-> split sx source' true' false' edge) 
                (flip (transduce coerce) false))
           (flip (transduce coerce) true))
      >> return ()