module Type.Check.CheckConst
(
--	checkConst
)

where

-----
import Util

import qualified Data.Map	as Map
import Data.Map			(Map)

import qualified Data.Set	as Set
import Data.Set			(Set)

import Data.Array.IO


import qualified Util.Data.Bag	as Bag
import Util.Data.Bag		(Bag)

-----
import qualified Shared.Var	as Var
import Shared.Error

import Type.Exp
import Type.Util
import Type.Error
import Type.Plate.Update

import Type.State
import Type.Class
import Type.Scheme
import Type.Induced

import Type.Trace.TraceFetters
import Type.Trace.TraceEffects

-----
stage	= "Type.Squid.CheckConst"
debug	= True
trace x	= when debug $ traceM x

{-
-----
-- checkConst
--	| For all regions in the graph, check that they aren't trying to
--	  be both Const and Mutable at the same time.
--
--	BUGS: we also have to do this for type nodes so we can catch
--	Const t0, Mutable t0 problems.
--
checkConst :: SquidM [Error]
checkConst	
 = do
	-- We only care about region classes.
	school		<- gets stateClass
	classes		<- liftIO (getElems $ schoolClass school)
	let csRegion	= [ c 	| c@Class{} <- classes
				, classKind c == KRegion]
				
	errs		<- liftM concat
			$  mapM checkConstC csRegion
			
	return errs
	

	
checkConstC :: Class -> SquidM [Error]
checkConstC    c
 = do	
 	let cid		= classId c
 
	-- Find the classes which contain effects that are acting on this one.
	cidsEs		<- traceEffectsZ
				Set.empty
				(Bag.singleton cid)
				[]
	
	cidClass	<- mapM (\cid -> do
					Just c	<- lookupClass cid
					return (cid, c))
			$ cidsEs
	

	-- Build a list containing:
	--	(which class an effect is in, the effect, the source of the effect).
	--

	let cidEffSource
		= (concat.concat)
		$ map (\(cid, c) -> 
			[zip3 (repeat cid) es (repeat (classTypeSource c))
				| TEffect (ESum es) <- classQueue c ])
		$ cidClass

	cidEffSourceU_
		<- mapM (\(cid, eff, tSource)
		 	-> do	effU	<- updateVC eff
				return (cid, effU, tSource))
		$ cidEffSource

	-- This sometimes includes more effects than it should.
	--	Crushing effs like EReadH can eliminate the effect, but not the backref from the
	--	type class that was being read.
	-- Restrict the effect list to only the effects that are actually on the region

	let cidEffSourceU	
		= [ ces 	| ces@(_, ECon _ [TRegion (RClass cidR)], _)
				<- cidEffSourceU_ 
				, cidR == cid ]

	---------------
	-- Do the ConstWrite check.
	--
	fetterCids	<- liftM (map fst)
			$  traceFettersZ
				Set.empty
				(Bag.singleton cid)
				[]

	fetterCs	<- liftM (map (\(Just c) -> c))
			$  mapM lookupClass fetterCids
			
	let fetterNodes	= catMap classNodes fetterCs


	-- Const fetters in this class.
	let constDirect
	 	= [(f, tSource)
			| (TFetter f@(FClass v _), tSource)	
				<- fetterNodes
			, Var.bind v == Var.FConst ]

	-- Write effects on this class.
	let effsWrite
	 	= [(e, tSource)
			| (_, e@(ECon v _), tSource)
				<- cidEffSourceU
			, elem (Var.bind v) [Var.EWrite, Var.EWriteT] ]

	errsConstWrite
		<- checkConstWrite 
			constDirect
			effsWrite


	---------------
	-- Do the PureReadWrite check.
	--

	-- Read effects on this class.
	let effsRead
		= [(cid, e, tSource)
			| (cid, e@(ECon v _), tSource)
				<- cidEffSourceU
			, elem (Var.bind v) [Var.ERead, Var.EReadT, Var.EReadH] ]

	errsPureReadWrite
		<- checkPureReadWrite 
			effsRead
			effsWrite


	---------------
	-- Debugging
	--

	trace	$ "*   CheckConst.checkConstC " % cid 		% "\n"
		% "    cidsEs          = " % cidsEs		% "\n"
		% "    cidEffSourceU   = " % cidEffSourceU	% "\n"
		% "    fetterCids      = " % fetterCids		% "\n"
		% "    constDirect     = " % constDirect	% "\n"
		% "    effsWrite       = " % effsWrite		% "\n"
		% "    effsRead        = " % effsRead		% "\n"
		% "\n"

	return	$  errsConstWrite 
		++ case errsPureReadWrite of
			(x:xs)	-> [x]
			[]	-> []
			

-- classTypeSource
--	Choose a TypeSource from one of the nodes in this class to represent it.
--	
classTypeSource :: Class -> TypeSource
classTypeSource c
 = let	ts	= map snd 
 		$ classNodes c
		
   in	selectTypeSource ts
	
	
-- selectTypeSource
-- 	Choose the first non TSNil from a list of TypeSources
--
selectTypeSource :: [TypeSource] -> TypeSource	
selectTypeSource xx
 = case xx of
 	(TSNil : xs)	-> selectTypeSource xs
	(x:xs)		-> x
	[]		-> TSNil

-----
-- checkConstWrite
--	Check that if this region contains Const fetters, 
--	then there are no writes to it.
--
--	We'll take the _last_ source in the list of type sources
--	to report the location of the error as this should correspond
--	to the source of the _first_ conflicting constraint in the source file.
--
checkConstWrite constDirect mutableWrite
	| (constF, constSource) : _	<- reverse constDirect
	, (writeE, writeSource) : _	<- reverse mutableWrite
	= do	
		constF'	<- updateVC constF
		writeE'	<- updateVC writeE
	
		return	$ [ErrorConstWrite
			{ eEffect	= writeE'
			, eEffectSource	= writeSource
			, eFetter	= constF'
			, eFetterSource	= constSource }]
	
	| otherwise
	= return []
	

-----
-- checkPureReadWrite
--	Check that if this region has both Read and Write
--	effects acting on it, then none of the Reads are
--	being purified.
--
checkPureReadWrite effsRead effsWrite

  | []				<- effsWrite
  = return []

  | (writeE, writeSource) : _	<- effsWrite 
  = do
 	pureReads	<- liftM catMaybes
			$  mapM checkPureRead effsRead
	
	case pureReads of
	 []		
	  -> return []

	 (cid, readE, readSource, (pureF, pureSource)) : _
	  -> return	$ [ErrorPureReadWrite
	  		{ eReadEff	= readE
			, eReadSource	= readSource
			, ePureFetter	= pureF
			, ePureSource	= pureSource
			, eWriteEff	= writeE
			, eWriteSource	= writeSource }]

			
----
-- checkPureRead
--	For this read effect, see if it is being purified.
--	If so, add this information to the tuple.
--
checkPureRead 
	(cid, eff, ts)
 = do
 	-- Find out what fetters are above this class.
	--
	fetters		<- traceFettersZ
				Set.empty
				(Bag.singleton cid)
				[]

	-- See if there were any Pure fetters above us.
	--
	let cFs		= [ cFs	| cFs@(cidF, FClass v _) <- fetters
				, Var.bind v == Var.FPure]

	case cFs of
	 []	-> return Nothing
	 (x:xs) -> checkPureRead2 cid eff ts x
	 

checkPureRead2 cid eff effTS (cidF, fetter)
 = do
	-- If there were Pure fetters, traceFettersZ doesn't return
	--	typeSources, so look them back up.
	--
 	Just c	<- lookupClass cidF

	let fsPure	= [ (f, ts)
				| (TFetter f@(FClass v _), ts)
					<- classNodes c
				, Var.bind v == Var.FPure ]
				
	let mPurifier	= takeHead fsPure

	trace	$ "*   checkPureRead\n"		
		% "    cid               = " % cid 		% "\n"
		% "    eff               = " % eff		% "\n"
		% "    fsPure            = " % fsPure 		% "\n"
		% "    mPurifier         = " % mPurifier 	% "\n\n"

	case mPurifier of
	 Nothing	-> return Nothing
	 Just purifier	-> return $ Just (cid, eff, effTS, purifier)
	 
-}

