{-# LANGUAGE CPP #-}
{-
	Copyright (C) 2021 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@]	Defines the values of a single unspecified piece, of occupying various coordinates.
-}

module BishBosh.Component.PieceSquareValueByCoordinates(
-- * Types
-- ** Type-synonyms
--	ByCoordinates,
-- ** Data-types
	PieceSquareValueByCoordinates(),
-- * Functions
-- ** Accessors
	dereference,
	getPieceSquareValue,
	toList,
-- ** Constructors
	fromList
) where

import			Data.Array.IArray((!))
import qualified	BishBosh.Cartesian.Coordinates	as Cartesian.Coordinates
import qualified	BishBosh.Colour.LogicalColour	as Colour.LogicalColour
import qualified	BishBosh.Property.Reflectable	as Property.Reflectable
import qualified	BishBosh.Type.Mass		as Type.Mass
import qualified	Control.DeepSeq
import qualified	Data.Array.IArray

type ByCoordinates	=
#ifdef UNBOX_TYPEMASS_ARRAYS
	Cartesian.Coordinates.UArrayByCoordinates
#else
	Cartesian.Coordinates.ArrayByCoordinates
#endif
		Type.Mass.PieceSquareValue

-- | The piece-square value varies with coordinates at which the unspecified piece is located.
newtype PieceSquareValueByCoordinates	= MkPieceSquareValueByCoordinates ByCoordinates deriving (PieceSquareValueByCoordinates
-> PieceSquareValueByCoordinates -> Bool
(PieceSquareValueByCoordinates
 -> PieceSquareValueByCoordinates -> Bool)
-> (PieceSquareValueByCoordinates
    -> PieceSquareValueByCoordinates -> Bool)
-> Eq PieceSquareValueByCoordinates
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: PieceSquareValueByCoordinates
-> PieceSquareValueByCoordinates -> Bool
$c/= :: PieceSquareValueByCoordinates
-> PieceSquareValueByCoordinates -> Bool
== :: PieceSquareValueByCoordinates
-> PieceSquareValueByCoordinates -> Bool
$c== :: PieceSquareValueByCoordinates
-> PieceSquareValueByCoordinates -> Bool
Eq, Int -> PieceSquareValueByCoordinates -> ShowS
[PieceSquareValueByCoordinates] -> ShowS
PieceSquareValueByCoordinates -> String
(Int -> PieceSquareValueByCoordinates -> ShowS)
-> (PieceSquareValueByCoordinates -> String)
-> ([PieceSquareValueByCoordinates] -> ShowS)
-> Show PieceSquareValueByCoordinates
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [PieceSquareValueByCoordinates] -> ShowS
$cshowList :: [PieceSquareValueByCoordinates] -> ShowS
show :: PieceSquareValueByCoordinates -> String
$cshow :: PieceSquareValueByCoordinates -> String
showsPrec :: Int -> PieceSquareValueByCoordinates -> ShowS
$cshowsPrec :: Int -> PieceSquareValueByCoordinates -> ShowS
Show)

instance Control.DeepSeq.NFData PieceSquareValueByCoordinates where
	rnf :: PieceSquareValueByCoordinates -> ()
rnf (MkPieceSquareValueByCoordinates ByCoordinates
pieceSquareValueByCoordinates)	=
#ifdef UNBOX_TYPEMASS_ARRAYS
		ByCoordinates -> ()
forall a. a -> ()
Control.DeepSeq.rwhnf
#else
		Control.DeepSeq.rnf
#endif
			ByCoordinates
pieceSquareValueByCoordinates

-- | Constructor from a list ordered by /coordinates/.
fromList :: [Type.Mass.PieceSquareValue] -> PieceSquareValueByCoordinates
fromList :: [PieceSquareValue] -> PieceSquareValueByCoordinates
fromList	= ByCoordinates -> PieceSquareValueByCoordinates
MkPieceSquareValueByCoordinates (ByCoordinates -> PieceSquareValueByCoordinates)
-> ([PieceSquareValue] -> ByCoordinates)
-> [PieceSquareValue]
-> PieceSquareValueByCoordinates
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [PieceSquareValue] -> ByCoordinates
forall (a :: * -> * -> *) e. IArray a e => [e] -> a Coordinates e
Cartesian.Coordinates.listArrayByCoordinates

-- | Deconstruct, returning the ordered list from which it was constructed.
toList :: PieceSquareValueByCoordinates -> [Type.Mass.PieceSquareValue]
toList :: PieceSquareValueByCoordinates -> [PieceSquareValue]
toList (MkPieceSquareValueByCoordinates ByCoordinates
pieceSquareValueByCoordinates)	= ByCoordinates -> [PieceSquareValue]
forall (a :: * -> * -> *) e i. (IArray a e, Ix i) => a i e -> [e]
Data.Array.IArray.elems ByCoordinates
pieceSquareValueByCoordinates

-- | Extract the value for the specified /coordinates/ (from @White@'s perspective).
dereference :: PieceSquareValueByCoordinates -> Cartesian.Coordinates.Coordinates -> Type.Mass.PieceSquareValue
dereference :: PieceSquareValueByCoordinates -> Coordinates -> PieceSquareValue
dereference (MkPieceSquareValueByCoordinates ByCoordinates
pieceSquareValueByCoordinates)	= (ByCoordinates
pieceSquareValueByCoordinates ByCoordinates -> Coordinates -> PieceSquareValue
forall (a :: * -> * -> *) e i.
(IArray a e, Ix i) =>
a i e -> i -> e
!)

{- |
	* Get the piece-square value, for the specified /coordinates/.

	* Utilises symmetry to infer values for @Black@, by reflecting the specified /coordinates/ into @White@'s domain.
-}
getPieceSquareValue
	:: PieceSquareValueByCoordinates
	-> Colour.LogicalColour.LogicalColour	-- ^ The /piece/'s /logical colour/.
	-> Cartesian.Coordinates.Coordinates	-- ^ The /piece/'s location.
	-> Type.Mass.PieceSquareValue
getPieceSquareValue :: PieceSquareValueByCoordinates
-> LogicalColour -> Coordinates -> PieceSquareValue
getPieceSquareValue (MkPieceSquareValueByCoordinates ByCoordinates
pieceSquareValueByCoordinates) LogicalColour
logicalColour	= (ByCoordinates
pieceSquareValueByCoordinates ByCoordinates -> Coordinates -> PieceSquareValue
forall (a :: * -> * -> *) e i.
(IArray a e, Ix i) =>
a i e -> i -> e
!) (Coordinates -> PieceSquareValue)
-> (Coordinates -> Coordinates) -> Coordinates -> PieceSquareValue
forall b c a. (b -> c) -> (a -> b) -> a -> c
. if LogicalColour -> Bool
Colour.LogicalColour.isBlack LogicalColour
logicalColour
	then Coordinates -> Coordinates
forall a. ReflectableOnX a => a -> a
Property.Reflectable.reflectOnX
	else Coordinates -> Coordinates
forall a. a -> a
id