module Curry.RunTimeSystem.BaseCurry (
  module Curry.RunTimeSystem.BaseCurry, 
  module Curry.RunTimeSystem.Store) where

import Curry.RunTimeSystem.Store
import Data.IORef
import System.IO.Unsafe

--trace' s x = unsafePerformIO (putStrLn s >> return x) 

-- On the top level io monad of each program we manage a store.
-- Because there is unsafe io and because some operations on
-- stores start out without one, a state might be without store.
type State = Store

-- curry data terms are classified into ConsKinds
data ConsKind = Val | Branching | Failed deriving (Show,Eq)

-- computations of (head) normal forms might residuate or not.
type HNFMode = Bool

type Branches a = [a]

data Exception 
  = ErrorCall String
  | PatternMatchFail String
  | AssertionFailed String
  | PreludeFailed
  | IOException String deriving Eq

type C_Exceptions = Exception

type Result  a = State -> a
type Result' a = Store -> a

----------------------------------------------------------------
-- the BaseCurry class
----------------------------------------------------------------

class BaseCurry a where
  -- computations of normal forms
  nf   :: BaseCurry b => (a -> Result b) -> a -> Result b
  gnf  :: BaseCurry b => (a -> Result b) -> a -> Result b

  -- constructors
  generator :: Int -> a
  failed    :: C_Exceptions          -> a
  branching :: OrRef   -> Branches a -> a

  -- category of given constructor
  consKind :: a -> ConsKind

  -- selectors
  exceptions :: a -> C_Exceptions
  orRef      :: a -> OrRef
  branches   :: a -> Branches a

------------------------------------------------------------------
-- implementation of call-time choice
------------------------------------------------------------------

-- This function controls all kinds of evaluations to (head) normal forms
-- IMPORTANT: if you change anything here, also update ExternalPrelude.prim_do
ctcStore :: (BaseCurry a,BaseCurry b) => HNFMode -> (a -> Result b) -> a -> Result b
ctcStore mode cont x state = 
  case consKind x of
   Val       -> cont x state
   Failed    -> addException err x
   Branching -> let ref = orRef x 
                    bs  = branches x 
                 in manipulateStore
                      (failed (curryError "ctcStore"))
                      contCTC
                      (\ ref' contSt -> if   mode || not (isGenerator ref)
                                        then lift contCTC (narrowOrRef ref) bs contSt
                                        else cont (branching ref' bs) state)
                      
                      ( \ ref' x' state' -> branching ref' [contCTC x' state'])
                      ref bs state                      
  where
    contCTC = ctcStore mode cont
    err = curryError ("Prelude."++if mode then "$#" else "$!")

mapOr :: BaseCurry b => (a -> Result b) -> OrRef -> Branches a -> Result b
mapOr cont ref bs = manipulateStore
    (failed (curryError "mapOr"))
    cont
    (\ _ -> lift cont (narrowOrRef ref) bs)
    (\ ref x st -> branching ref [cont x st])
    ref bs

lift :: BaseCurry b => (a -> Result b) -> OrRef -> Branches a 
                    -> (Int -> State)  -> b
lift cont ref bs contSt = 
  branching ref (zipWith (\ x i -> cont x (contSt i)) bs [0..])

addException :: (BaseCurry a,BaseCurry b) => Exception -> a -> b
addException _ x = failed (exceptions x)

curryError :: String -> Exception
curryError = ErrorCall