{-# OPTIONS -Wall #-}

--------------------------------------------------------------------------------
-- |
-- Module      :  Wumpus.Drawing.Chains.Derived
-- Copyright   :  (c) Stephen Tetley 2010-2011
-- License     :  BSD3
--
-- Maintainer  :  stephen.tetley@gmail.com
-- Stability   :  unstable
-- Portability :  GHC 
--
-- Generate points in an iterated chain.
--
-- WARNING - very unstable.
--
--------------------------------------------------------------------------------

module Wumpus.Drawing.Chains.Derived
  (
    
    tableDown
  , tableRight

  , horizontalPoints
  , verticalPoints

  , horizontalSteps
  , verticalSteps

  , innerHorizontals
  , innerVerticals

  ) where

import Wumpus.Drawing.Chains.Base

import Wumpus.Basic.Kernel                      -- package: wumpus-basic
import Wumpus.Core                              -- package: wumpus-core


import Data.List 

--------------------------------------------------------------------------------
-- Tables

-- Note - for the minor runtime cost, pairing the row_width and 
-- row_height should make the API more /memorable/...


-- | 'tableDown' : @ num_rows * (row_width, row_height) -> LocChain @
--
-- The table grows down and right, the implicit initial point is 
-- @top-left@.
--
tableDown :: Num u => Int -> (u,u) -> LocChain u
tableDown n (rw,rh) = liftChainF $ \pt -> map (fn pt) ints
  where
    ints    = iterate (+1) 0
    fn pt i = let (x,y) = i `divMod` n 
              in displace (rw * fromIntegral x) (rh * fromIntegral (-y)) pt


-- | 'tableRight' : @ num_cols * row_width * row_height -> LocChain @
--
-- The table grows right and down, the implicit initial point is 
-- @top-left@.
--
-- This chain is infinite.
--
tableRight :: Num u => Int -> (u,u) -> LocChain u
tableRight n (rw,rh) = liftChainF $ \pt -> map (fn pt) ints
  where
    ints    = iterate (+1) 0
    fn pt i = let (y,x) = i `divMod` n 
              in displace (rw * fromIntegral x) (rh * fromIntegral (-y)) pt



-- | 'horizontalPoints' : @ horizontal_dist -> LocChain @
--
-- The chain grows right by the supplied increment.
--
-- This chain is infinite.
--
horizontalPoints :: Num u => u -> LocChain u
horizontalPoints dx = liftChainF $ iterate (displaceH dx)


-- | 'verticalPoints' : @ vertical_dist -> LocChain @
--
-- The chain grows up by the supplied increment.
--
-- This chain is infinite.
--
verticalPoints :: Num u => u -> LocChain u
verticalPoints dy = liftChainF $ iterate (displaceV dy)


-- | 'horizontalSteps' : @ [horizontal_dist] -> LocChain @
--
-- This is a @scanl@ successive displacing the start point.
--
-- This chain is finite (for finite input list).
--
horizontalSteps :: Num u => [u] -> LocChain u
horizontalSteps xs = liftChainF $ \pt -> scanl (flip displaceH) pt xs 


-- | 'verticalSteps' : @ [vertical_dist] -> LocChain @
--
-- This is a @scanl@ successive displacing the start point.
--
-- This chain is finite (for finite input list).
--
-- \*\* WARNING \*\* - name due to be changed. Current name is 
-- too general for this function. 
--
verticalSteps :: Num u => [u] -> LocChain u
verticalSteps ys = liftChainF $ \pt -> scanl (flip displaceV) pt ys


{-# INLINE [0] ceilingi #-}
ceilingi :: RealFrac a => a -> Int
ceilingi = ceiling


-- | Note - horizontals are projected from the start point. The 
-- horizontal component of the second point is ignored.
-- 
-- This chain is finite for well formed input.
--
innerHorizontals :: RealFrac u => u -> ConnectorChain u
innerHorizontals n = promoteR2 $ \a b -> return $ body a b
  where
    body (P2 x0 y0) (P2 x1 _) = unfoldr phi (n * fromIntegral z) 
      where z                 = ceilingi $ x0 / n
            phi x | x < x1    = Just (P2 x y0, x+n)
                  | otherwise = Nothing

      

-- | Note - verticals are projected from the start point. The 
-- vertical component of the second point is ignored.
-- 
-- This chain is finite for well formed input.
--
innerVerticals :: RealFrac u => u -> ConnectorChain u
innerVerticals n = promoteR2 $ \a b -> return $ body a b
  where
    body (P2 x0 y0) (P2 _ y1) = unfoldr phi (n * fromIntegral z)
      where z                 = ceilingi $ y0 / n
            phi y | y < y1    = Just (P2 x0 y, y+n)
                  | otherwise = Nothing