{-
	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.

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

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

import qualified	BishBosh.Attribute.LogicalColour	as Attribute.LogicalColour
import qualified	BishBosh.Cartesian.Abscissa		as Cartesian.Abscissa
import qualified	BishBosh.Property.Opposable		as Property.Opposable
import qualified	BishBosh.Types				as T
import qualified	Control.Exception

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

-- | The constant length of the /y/-axis.
yLength :: T.Distance
yLength :: Int
yLength	= Int
Cartesian.Abscissa.xLength	-- Because the board's square.

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

-- | The constant upper bound of the ordinate.
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 the ordinate.
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 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

{- |
	* The constant centre of the span.

	* CAVEAT: no square actually exists at this fractional value.
-}
centre :: Fractional centre => centre
centre :: centre
centre	= Int -> centre
forall a b. (Integral a, Num b) => a -> b
fromIntegral ((Int -> Int -> Int) -> (Int, Int) -> Int
forall a b c. (a -> b -> c) -> (a, b) -> c
uncurry Int -> Int -> Int
forall a. Num a => a -> a -> a
(+) (Int, Int)
forall y. Enum y => (y, y)
yBounds :: T.Y) centre -> centre -> centre
forall a. Fractional a => a -> a -> a
/ centre
2

-- | 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 -> y
forall a. Enum a => Int -> a
toEnum (Int -> y) -> (y -> Int) -> y -> y
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (
	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) -> (y -> Int) -> y -> Int
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> Int
forall a. Num a => a -> a
negate (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

-- | 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