{-
	Copyright (C) 2018 Dr. Alistair Ward

	This file is part of BishBosh.

	BishBosh is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	BishBosh is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with BishBosh.  If not, see <http://www.gnu.org/licenses/>.
-}
{- |
 [@AUTHOR@]	Dr. Alistair Ward

 [@DESCRIPTION@]

	* Describes the /y/-axis by which the /board/ is indexed.

	* AKA the /rank/ of a piece.

	* N.B. this coordinate-system is for internal use only, and doesn't attempt to replicate any standard chess-notation.
-}

module BishBosh.Cartesian.Ordinate(
-- * Types
--	ArrayByOrdinate,
-- * Constants
	yOrigin,
	yLength,
	yMin,
	yMax,
	yBounds,
	yRange,
-- * Functions
	toIx,
	fromIx,
	firstRank,
	lastRank,
	pawnsFirstRank,
	enPassantRank,
	reflect,
	translate,
	maybeTranslate,
-- ** Constructors
--	listArrayByOrdinate,
-- ** Predicates
	inBounds
) where

import qualified	BishBosh.Attribute.LogicalColour	as Attribute.LogicalColour
import qualified	BishBosh.Cartesian.Abscissa		as Cartesian.Abscissa
import qualified	BishBosh.Data.Enum			as Data.Enum
import qualified	BishBosh.Property.Opposable		as Property.Opposable
import qualified	BishBosh.Type.Length			as Type.Length
import qualified	Control.Exception
import qualified	Data.Array.IArray

-- | The position of the origin on the /y/-axis.
yOrigin :: Int
yOrigin :: Int
yOrigin	= Int
Cartesian.Abscissa.xOrigin	-- N.B. it doesn't need to be the same.

-- | The constant length of the /y/-axis.
yLength :: Type.Length.Distance
yLength :: Int
yLength	= Int
Cartesian.Abscissa.xLength	-- Because the board is square.

-- | The constant lower bound of ordinates.
yMin :: Enum y => y
yMin :: y
yMin	= Int -> y
forall a. Enum a => Int -> a
toEnum Int
yOrigin

-- | The constant upper bound of ordinates.
yMax :: Enum y => y
yMax :: y
yMax	= Int -> y
forall a. Enum a => Int -> a
toEnum (Int -> y) -> Int -> y
forall a b. (a -> b) -> a -> b
$ Int
yOrigin Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Int -> Int
forall a. Enum a => a -> a
pred {-fence-post-} Int
yLength)

-- | The constant bounds of ordinates.
yBounds :: Enum y => (y, y)
yBounds :: (y, y)
yBounds	= (y
forall y. Enum y => y
yMin, y
forall y. Enum y => y
yMax)

-- | The constant list of all ordinates.
yRange :: Enum y => [y]
yRange :: [y]
yRange	= (y -> y -> [y]) -> (y, y) -> [y]
forall a b c. (a -> b -> c) -> (a, b) -> c
uncurry y -> y -> [y]
forall a. Enum a => a -> a -> [a]
enumFromTo (y, y)
forall y. Enum y => (y, y)
yBounds

-- | Convert to an array-index.
toIx :: Enum y => y -> Int
{-# INLINE toIx #-}
toIx :: y -> Int
toIx	= Int -> Int -> Int
forall a. Num a => a -> a -> a
subtract Int
yOrigin (Int -> Int) -> (y -> Int) -> y -> Int
forall b c a. (b -> c) -> (a -> b) -> a -> c
. y -> Int
forall a. Enum a => a -> Int
fromEnum

-- | Convert from an array-index.
fromIx :: Enum y => Int -> y
{-# INLINE fromIx #-}
fromIx :: Int -> y
fromIx	= Int -> y
forall a. Enum a => Int -> a
toEnum (Int -> y) -> (Int -> Int) -> Int -> y
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
yOrigin)

-- | The /rank/ from which /piece/s conventionally start.
firstRank :: Enum y => Attribute.LogicalColour.LogicalColour -> y
firstRank :: LogicalColour -> y
firstRank LogicalColour
Attribute.LogicalColour.Black	= y
forall y. Enum y => y
yMax
firstRank LogicalColour
_				= y
forall y. Enum y => y
yMin

-- | The final /rank/; i.e. the one on which a @Pawn@ is promoted.
lastRank :: Enum y => Attribute.LogicalColour.LogicalColour -> y
lastRank :: LogicalColour -> y
lastRank	= LogicalColour -> y
forall y. Enum y => LogicalColour -> y
firstRank (LogicalColour -> y)
-> (LogicalColour -> LogicalColour) -> LogicalColour -> y
forall b c a. (b -> c) -> (a -> b) -> a -> c
. LogicalColour -> LogicalColour
forall a. Opposable a => a -> a
Property.Opposable.getOpposite

-- | The /rank/ from which @Pawn@s conventionally start.
pawnsFirstRank :: Enum y => Attribute.LogicalColour.LogicalColour -> y
{-# INLINE pawnsFirstRank #-}
pawnsFirstRank :: LogicalColour -> y
pawnsFirstRank LogicalColour
Attribute.LogicalColour.Black	= y -> y
forall a. Enum a => a -> a
pred y
forall y. Enum y => y
yMax
pawnsFirstRank LogicalColour
_				= y -> y
forall a. Enum a => a -> a
succ y
forall y. Enum y => y
yMin

-- | The /rank/ from which a @Pawn@ may capture /en-passant/.
enPassantRank :: Enum y => Attribute.LogicalColour.LogicalColour -> y
{-# INLINE enPassantRank #-}
enPassantRank :: LogicalColour -> y
enPassantRank LogicalColour
Attribute.LogicalColour.Black	= Int -> y
forall a. Enum a => Int -> a
toEnum (Int -> y) -> Int -> y
forall a b. (a -> b) -> a -> b
$ Int
yOrigin Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
3
enPassantRank LogicalColour
_					= Int -> y
forall a. Enum a => Int -> a
toEnum (Int -> y) -> Int -> y
forall a b. (a -> b) -> a -> b
$ Int
yOrigin Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
4

-- | Reflects about the mid-point of the axis.
reflect :: Enum y => y -> y
reflect :: y -> y
reflect	= (Int -> Int) -> y -> y
forall a b. (Enum a, Enum b) => (Int -> Int) -> a -> b
Data.Enum.translate ((Int -> Int) -> y -> y) -> (Int -> Int) -> y -> y
forall a b. (a -> b) -> a -> b
$ (
	Int -> Int -> Int
forall a. Num a => a -> a -> a
+ (Int
2 Int -> Int -> Int
forall a. Num a => a -> a -> a
* Int
yOrigin Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Int -> Int
forall a. Enum a => a -> a
pred Int
yLength))
 ) (Int -> Int) -> (Int -> Int) -> Int -> Int
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> Int
forall a. Num a => a -> a
negate

-- | Predicate.
inBounds :: (Enum y, Ord y) => y -> Bool
{-# INLINE inBounds #-}
inBounds :: y -> Bool
inBounds y
y	= y
y y -> y -> Bool
forall a. Ord a => a -> a -> Bool
>= y
forall y. Enum y => y
yMin Bool -> Bool -> Bool
&& y
y y -> y -> Bool
forall a. Ord a => a -> a -> Bool
<= y
forall y. Enum y => y
yMax

-- | Translate the specified ordinate.
translate :: (Enum y, Ord y) => (y -> y) -> y -> y
translate :: (y -> y) -> y -> y
translate y -> y
transformation	= (\y
y -> Bool -> y -> y
forall a. (?callStack::CallStack) => Bool -> a -> a
Control.Exception.assert (y -> Bool
forall y. (Enum y, Ord y) => y -> Bool
inBounds y
y) y
y) (y -> y) -> (y -> y) -> y -> y
forall b c a. (b -> c) -> (a -> b) -> a -> c
. y -> y
transformation

-- | Where legal, translate the specified ordinate.
maybeTranslate :: (Enum y, Ord y) => (y -> y) -> y -> Maybe y
maybeTranslate :: (y -> y) -> y -> Maybe y
maybeTranslate y -> y
transformation	= (
	\y
y -> if y -> Bool
forall y. (Enum y, Ord y) => y -> Bool
inBounds y
y
		then y -> Maybe y
forall a. a -> Maybe a
Just y
y
		else Maybe y
forall a. Maybe a
Nothing
 ) (y -> Maybe y) -> (y -> y) -> y -> Maybe y
forall b c a. (b -> c) -> (a -> b) -> a -> c
. y -> y
transformation


-- | A boxed array indexed by /coordinates/, of arbitrary elements.
type ArrayByOrdinate y	= Data.Array.IArray.Array {-Boxed-} y

-- | Array-constructor.
listArrayByOrdinate :: (
	Data.Array.IArray.IArray	a e,
	Data.Array.IArray.Ix		y,
	Enum				y
 ) => [e] -> a y e
listArrayByOrdinate :: [e] -> a y e
listArrayByOrdinate	= (y, y) -> [e] -> a y e
forall (a :: * -> * -> *) e i.
(IArray a e, Ix i) =>
(i, i) -> [e] -> a i e
Data.Array.IArray.listArray (y, y)
forall y. Enum y => (y, y)
yBounds