-- | This module contains a runner for a an Lseed garden. It can be passed an
-- observer that will receive the results.
module Lseed.Mainloop where

import Lseed.Data
import Lseed.Geometry
import Lseed.Data.Functions
import Lseed.Constants
import Lseed.Logic
import Lseed.StipeInfo
import System.Time
import System.Random
import Control.Concurrent
import Control.Monad

-- | Lets a garden grow for the given number of days, while keeping the
-- observer informed about any changes.
lseedMainLoop :: Bool -- ^ Run in real time, e.g. call 'threadDelay'
	-> Observer -- ^ Who to notify about the state of the game
	-> GardenSource -- ^ Where do get the plant code from
	-> Integer -- ^ Maximum days to run
	-> IO ()
lseedMainLoop rt obs gardenSource maxDays = do
	garden <- getGarden gardenSource
	obInit obs
	let nextDay (tick, garden) = 
		let (day, tickOfDay) = tick `divMod` ticksPerDay in
		if day > maxDays then
			obFinished obs garden
		else do

		tickStart <- getClockTime
		rgen <- newStdGen
		let sampleAngle = lightAngle $ (fromIntegral tickOfDay + 0.5) /
                                                fromIntegral ticksPerDay
		let newGardenWithSeeds = applyGenome sampleAngle rgen garden
		rgen <- newStdGen
		newGarden <- fmap concat $ forM newGardenWithSeeds $
			\(parent,seedPoss) -> fmap (parent:) $ forM seedPoss $ \seedPos -> do
				genome <- getUpdatedCode gardenSource (fmap (const ()) parent)
				return $ Planted (plantPosition parent + seedPos)
				                 (plantOwner parent)
				                 (plantOwnerName parent)
				         	 genome
				         	 (fmap (const NoGrowth) inititalPlant)

		let growingGarden = growGarden sampleAngle rgen newGarden


		obState obs tick sampleAngle garden
		when rt $ do
			text <- getScreenMessage gardenSource 
			obGrowingState obs $ \later -> 
				let tickDiff = timeSpanFraction tickLength tickStart later
				    dayDiff = (fromIntegral tickOfDay + tickDiff) /
					      fromIntegral ticksPerDay
				    timeInfo = formatTimeInfo day dayDiff
				    visualizeAngle = lightAngle dayDiff
				    gardenNow = annotateGarden visualizeAngle $ 
				                growingGarden tickDiff
				in ScreenContent gardenNow visualizeAngle timeInfo text

			threadDelay (round (tickLength * 1000 * 1000))
		nextDay (succ tick, growingGarden 1)
	nextDay (0::Integer, mapGarden (fmap (const NoGrowth)) garden)