-----------------------------------------------------------------------------
-- Copyright 2014, Open Universiteit Nederland. This file is distributed
-- under the terms of the GNU General Public License. For more information,
-- see the file "LICENSE.txt", which is included in the distribution.
-----------------------------------------------------------------------------
-- |
-- Maintainer  :  bastiaan.heeren@ou.nl
-- Stability   :  provisional
-- Portability :  portable (depends on ghc)
--
-- A path encodes a location (sub-part) of a strategy by:
-- * maintaining a list of directions (go left/go right) for each choice;
-- * the depth (number of steps)
-- Use Show/Read type classes for serialization/deserialization
--
-----------------------------------------------------------------------------
--  $Id: Path.hs 6535 2014-05-14 11:05:06Z bastiaan $

module Ideas.Common.Strategy.Path
   ( -- * datatype and constructor
     Path, emptyPath
     -- * extending a path
   , toLeft, toRight, tick
     -- * following a path
   , leftOrRight, untick
   ) where

import Data.Foldable (toList)
import Data.Sequence (Seq, empty, (|>), viewl, ViewL(..), fromList)
import Ideas.Common.Classes

data Path = Path !Int (Seq Bool) -- depth, choices
   deriving Eq

instance Show Path where
   show = show . intList

instance Read Path where
   readsPrec _ = map (mapFirst fromIntList) . readList

emptyPath :: Path
emptyPath = Path 0 empty

toLeft, toRight, tick :: Path -> Path
toLeft  (Path n bs) = Path (n+1) (bs |> True)
toRight (Path n bs) = Path (n+1) (bs |> False)
tick    (Path n bs) = Path (n+1) bs

-- |Following a path without going left or right (counterpart of 'tick')
untick :: Monad m => Path -> m Path
untick (Path n bs)
   | n > 0     = return (Path (n-1) bs)
   | otherwise = fail "untick: invalid path"

leftOrRight :: Monad m => Path -> m (Either Path Path)
leftOrRight (Path n bs) =
   case viewl bs of
      b :< cs | n > 0 && b -> return (Left (Path (n-1) cs))
              | n > 0      -> return (Right (Path (n-1) cs))
      _ -> fail "untick: invalid path"

-- local helpers
intList :: Path -> [Int]
intList (Path n bs)
   | n == 0    = []
   | otherwise = n : map (\b -> if b then 0 else 1) (toList bs)

fromIntList :: [Int] -> Path
fromIntList []     = emptyPath
fromIntList (n:is) = Path n (fromList (map (==0) is))