module Dovin.Matchers where

import Dovin.Prelude
import Dovin.Attributes (creature)
import Dovin.Types

import qualified Data.Set as S
import Control.Lens (_1)

-- CARD MATCHERS
--
-- Matchers are used for both filtering sets of cards, and also for verifying
-- attributes of cards.
matchDamage :: Int -> CardMatcher
matchDamage :: Int -> CardMatcher
matchDamage Int
n = String -> (Card -> Bool) -> CardMatcher
CardMatcher (Int -> String
forall a. Show a => a -> String
show Int
n String -> String -> String
forall a. Semigroup a => a -> a -> a
<> String
" damage") ((Card -> Bool) -> CardMatcher) -> (Card -> Bool) -> CardMatcher
forall a b. (a -> b) -> a -> b
$
  Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
(==) Int
n (Int -> Bool) -> (Card -> Int) -> Card -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Getting Int Card Int -> Card -> Int
forall s (m :: * -> *) a. MonadReader s m => Getting a s a -> m a
view Getting Int Card Int
Lens' Card Int
cardDamage

matchLoyalty :: Int -> CardMatcher
matchLoyalty :: Int -> CardMatcher
matchLoyalty Int
n = String -> (Card -> Bool) -> CardMatcher
CardMatcher (Int -> String
forall a. Show a => a -> String
show Int
n String -> String -> String
forall a. Semigroup a => a -> a -> a
<> String
" loyalty") ((Card -> Bool) -> CardMatcher) -> (Card -> Bool) -> CardMatcher
forall a b. (a -> b) -> a -> b
$
  Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
(==) Int
n (Int -> Bool) -> (Card -> Int) -> Card -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Getting Int Card Int -> Card -> Int
forall s (m :: * -> *) a. MonadReader s m => Getting a s a -> m a
view Getting Int Card Int
Lens' Card Int
cardLoyalty

matchPlusOneCounters :: Int -> CardMatcher
matchPlusOneCounters :: Int -> CardMatcher
matchPlusOneCounters Int
n = String -> (Card -> Bool) -> CardMatcher
CardMatcher (Int -> String
forall a. Show a => a -> String
show Int
n String -> String -> String
forall a. Semigroup a => a -> a -> a
<> String
" +1/+1 counters") ((Card -> Bool) -> CardMatcher) -> (Card -> Bool) -> CardMatcher
forall a b. (a -> b) -> a -> b
$
  Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
(==) Int
n (Int -> Bool) -> (Card -> Int) -> Card -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Getting Int Card Int -> Card -> Int
forall s (m :: * -> *) a. MonadReader s m => Getting a s a -> m a
view Getting Int Card Int
Lens' Card Int
cardPlusOneCounters

matchMinusOneCounters :: Int -> CardMatcher
matchMinusOneCounters :: Int -> CardMatcher
matchMinusOneCounters Int
n = String -> (Card -> Bool) -> CardMatcher
CardMatcher (Int -> String
forall a. Show a => a -> String
show Int
n String -> String -> String
forall a. Semigroup a => a -> a -> a
<> String
" -1/-1 counters") ((Card -> Bool) -> CardMatcher) -> (Card -> Bool) -> CardMatcher
forall a b. (a -> b) -> a -> b
$
  Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
(==) Int
n (Int -> Bool) -> (Card -> Int) -> Card -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Getting Int Card Int -> Card -> Int
forall s (m :: * -> *) a. MonadReader s m => Getting a s a -> m a
view Getting Int Card Int
Lens' Card Int
cardMinusOneCounters

matchLocation :: CardLocation -> CardMatcher
matchLocation :: CardLocation -> CardMatcher
matchLocation CardLocation
loc = String -> (Card -> Bool) -> CardMatcher
CardMatcher (String
"in location " String -> String -> String
forall a. Semigroup a => a -> a -> a
<> CardLocation -> String
forall a. Show a => a -> String
show CardLocation
loc) ((Card -> Bool) -> CardMatcher) -> (Card -> Bool) -> CardMatcher
forall a b. (a -> b) -> a -> b
$
  CardLocation -> CardLocation -> Bool
forall a. Eq a => a -> a -> Bool
(==) CardLocation
loc (CardLocation -> Bool) -> (Card -> CardLocation) -> Card -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Getting CardLocation Card CardLocation -> Card -> CardLocation
forall s (m :: * -> *) a. MonadReader s m => Getting a s a -> m a
view Getting CardLocation Card CardLocation
Lens' Card CardLocation
cardLocation

matchInPlay :: CardMatcher
matchInPlay = String -> (Card -> Bool) -> CardMatcher
CardMatcher String
"in play" ((Card -> Bool) -> CardMatcher) -> (Card -> Bool) -> CardMatcher
forall a b. (a -> b) -> a -> b
$ \Card
c -> CardLocation -> Location
forall a b. (a, b) -> b
snd (Getting CardLocation Card CardLocation -> Card -> CardLocation
forall s (m :: * -> *) a. MonadReader s m => Getting a s a -> m a
view Getting CardLocation Card CardLocation
Lens' Card CardLocation
location Card
c) Location -> Location -> Bool
forall a. Eq a => a -> a -> Bool
== Location
Play

matchAttribute :: CardAttribute -> CardMatcher
matchAttribute :: String -> CardMatcher
matchAttribute String
attr = String -> (Card -> Bool) -> CardMatcher
CardMatcher (String
"has attribute " String -> String -> String
forall a. Semigroup a => a -> a -> a
<> String
attr) ((Card -> Bool) -> CardMatcher) -> (Card -> Bool) -> CardMatcher
forall a b. (a -> b) -> a -> b
$
  String -> Set String -> Bool
forall a. Ord a => a -> Set a -> Bool
S.member String
attr (Set String -> Bool) -> (Card -> Set String) -> Card -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Getting (Set String) Card (Set String) -> Card -> Set String
forall s (m :: * -> *) a. MonadReader s m => Getting a s a -> m a
view Getting (Set String) Card (Set String)
Lens' Card (Set String)
cardAttributes

matchAttributes :: [CardAttribute] -> CardMatcher
matchAttributes :: [String] -> CardMatcher
matchAttributes = (String -> CardMatcher -> CardMatcher)
-> CardMatcher -> [String] -> CardMatcher
forall (t :: * -> *) a b.
Foldable t =>
(a -> b -> b) -> b -> t a -> b
foldr (CardMatcher -> CardMatcher -> CardMatcher
forall a. Semigroup a => a -> a -> a
(<>) (CardMatcher -> CardMatcher -> CardMatcher)
-> (String -> CardMatcher) -> String -> CardMatcher -> CardMatcher
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> CardMatcher
matchAttribute) CardMatcher
forall a. Monoid a => a
mempty

matchName :: CardName -> CardMatcher
matchName :: String -> CardMatcher
matchName String
n = String -> (Card -> Bool) -> CardMatcher
CardMatcher (String
"has name " String -> String -> String
forall a. Semigroup a => a -> a -> a
<> String
n) ((Card -> Bool) -> CardMatcher) -> (Card -> Bool) -> CardMatcher
forall a b. (a -> b) -> a -> b
$ String -> String -> Bool
forall a. Eq a => a -> a -> Bool
(==) String
n (String -> Bool) -> (Card -> String) -> Card -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Getting String Card String -> Card -> String
forall s (m :: * -> *) a. MonadReader s m => Getting a s a -> m a
view Getting String Card String
Lens' Card String
cardName

matchOtherCreatures :: Card -> CardMatcher
matchOtherCreatures :: Card -> CardMatcher
matchOtherCreatures = String -> Card -> CardMatcher
matchOther String
creature

matchOther :: CardAttribute -> Card -> CardMatcher
matchOther :: String -> Card -> CardMatcher
matchOther String
attribute Card
card =
     CardLocation -> CardMatcher
matchLocation (Getting CardLocation Card CardLocation -> Card -> CardLocation
forall s (m :: * -> *) a. MonadReader s m => Getting a s a -> m a
view Getting CardLocation Card CardLocation
Lens' Card CardLocation
cardLocation Card
card)
  CardMatcher -> CardMatcher -> CardMatcher
forall a. Semigroup a => a -> a -> a
<> String -> CardMatcher
matchAttribute String
attribute
  CardMatcher -> CardMatcher -> CardMatcher
forall a. Semigroup a => a -> a -> a
<> CardMatcher -> CardMatcher
invert (String -> CardMatcher
matchName (Getting String Card String -> Card -> String
forall s (m :: * -> *) a. MonadReader s m => Getting a s a -> m a
view Getting String Card String
Lens' Card String
cardName Card
card))

matchController :: Player -> CardMatcher
matchController Player
player = String -> (Card -> Bool) -> CardMatcher
CardMatcher (String
"has controller " String -> String -> String
forall a. Semigroup a => a -> a -> a
<> Player -> String
forall a. Show a => a -> String
show Player
player) ((Card -> Bool) -> CardMatcher) -> (Card -> Bool) -> CardMatcher
forall a b. (a -> b) -> a -> b
$
  Player -> Player -> Bool
forall a. Eq a => a -> a -> Bool
(==) Player
player (Player -> Bool) -> (Card -> Player) -> Card -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Getting Player Card Player -> Card -> Player
forall s (m :: * -> *) a. MonadReader s m => Getting a s a -> m a
view ((CardLocation -> Const Player CardLocation)
-> Card -> Const Player Card
Lens' Card CardLocation
location ((CardLocation -> Const Player CardLocation)
 -> Card -> Const Player Card)
-> ((Player -> Const Player Player)
    -> CardLocation -> Const Player CardLocation)
-> Getting Player Card Player
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Player -> Const Player Player)
-> CardLocation -> Const Player CardLocation
forall s t a b. Field1 s t a b => Lens s t a b
_1)

matchLesserPower :: Int -> CardMatcher
matchLesserPower Int
n = String -> (Card -> Bool) -> CardMatcher
CardMatcher (String
"power < " String -> String -> String
forall a. Semigroup a => a -> a -> a
<> Int -> String
forall a. Show a => a -> String
show Int
n) ((Card -> Bool) -> CardMatcher) -> (Card -> Bool) -> CardMatcher
forall a b. (a -> b) -> a -> b
$
  (Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
n) (Int -> Bool) -> (Card -> Int) -> Card -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Getting Int Card Int -> Card -> Int
forall s (m :: * -> *) a. MonadReader s m => Getting a s a -> m a
view Getting Int Card Int
Lens' Card Int
cardPower

matchNone :: CardMatcher
matchNone = String -> (Card -> Bool) -> CardMatcher
CardMatcher String
"never match" (Bool -> Card -> Bool
forall a b. a -> b -> a
const Bool
False)

matchCard :: Card -> CardMatcher
matchCard :: Card -> CardMatcher
matchCard = String -> CardMatcher
matchName (String -> CardMatcher) -> (Card -> String) -> Card -> CardMatcher
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Getting String Card String -> Card -> String
forall s (m :: * -> *) a. MonadReader s m => Getting a s a -> m a
view Getting String Card String
Lens' Card String
cardName

matchToughness :: Int -> CardMatcher
matchToughness :: Int -> CardMatcher
matchToughness Int
n = String -> CardMatcher -> CardMatcher
labelMatch (String
"toughness = " String -> String -> String
forall a. Semigroup a => a -> a -> a
<> Int -> String
forall a. Show a => a -> String
show Int
n) (CardMatcher -> CardMatcher) -> CardMatcher -> CardMatcher
forall a b. (a -> b) -> a -> b
$ String -> (Card -> Bool) -> CardMatcher
CardMatcher String
""
  ((Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
n) (Int -> Bool) -> (Card -> Int) -> Card -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Getting Int Card Int -> Card -> Int
forall s (m :: * -> *) a. MonadReader s m => Getting a s a -> m a
view Getting Int Card Int
Lens' Card Int
cardToughness) CardMatcher -> CardMatcher -> CardMatcher
forall a. Semigroup a => a -> a -> a
<> String -> CardMatcher
matchAttribute String
creature

matchPower :: Int -> CardMatcher
matchPower :: Int -> CardMatcher
matchPower Int
n = String -> CardMatcher -> CardMatcher
labelMatch (String
"power = " String -> String -> String
forall a. Semigroup a => a -> a -> a
<> Int -> String
forall a. Show a => a -> String
show Int
n) (CardMatcher -> CardMatcher) -> CardMatcher -> CardMatcher
forall a b. (a -> b) -> a -> b
$ String -> (Card -> Bool) -> CardMatcher
CardMatcher String
""
  ((Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
n) (Int -> Bool) -> (Card -> Int) -> Card -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Getting Int Card Int -> Card -> Int
forall s (m :: * -> *) a. MonadReader s m => Getting a s a -> m a
view Getting Int Card Int
Lens' Card Int
cardPower) CardMatcher -> CardMatcher -> CardMatcher
forall a. Semigroup a => a -> a -> a
<> String -> CardMatcher
matchAttribute String
creature

matchStrength :: (Int, Int) -> CardMatcher
matchStrength :: (Int, Int) -> CardMatcher
matchStrength (Int
p, Int
t) = String -> CardMatcher -> CardMatcher
labelMatch (String
"P/T = " String -> String -> String
forall a. Semigroup a => a -> a -> a
<> Int -> String
forall a. Show a => a -> String
show Int
p String -> String -> String
forall a. Semigroup a => a -> a -> a
<> String
"/" String -> String -> String
forall a. Semigroup a => a -> a -> a
<> Int -> String
forall a. Show a => a -> String
show Int
t) (CardMatcher -> CardMatcher) -> CardMatcher -> CardMatcher
forall a b. (a -> b) -> a -> b
$
  Int -> CardMatcher
matchPower Int
p CardMatcher -> CardMatcher -> CardMatcher
forall a. Semigroup a => a -> a -> a
<> Int -> CardMatcher
matchToughness Int
t

matchTarget :: Target -> CardMatcher
matchTarget :: Target -> CardMatcher
matchTarget Target
t = String -> CardMatcher -> CardMatcher
labelMatch (String
"target = " String -> String -> String
forall a. Semigroup a => a -> a -> a
<> Target -> String
forall a. Show a => a -> String
show Target
t) (CardMatcher -> CardMatcher) -> CardMatcher -> CardMatcher
forall a b. (a -> b) -> a -> b
$ String -> (Card -> Bool) -> CardMatcher
CardMatcher String
""
  (Target -> Target -> Bool
forall a. Eq a => a -> a -> Bool
(==) Target
t (Target -> Bool) -> (Card -> Target) -> Card -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> Target
TargetCard (String -> Target) -> (Card -> String) -> Card -> Target
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Getting String Card String -> Card -> String
forall s (m :: * -> *) a. MonadReader s m => Getting a s a -> m a
view Getting String Card String
Lens' Card String
cardName)

missingAttribute :: String -> CardMatcher
missingAttribute = CardMatcher -> CardMatcher
invert (CardMatcher -> CardMatcher)
-> (String -> CardMatcher) -> String -> CardMatcher
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> CardMatcher
matchAttribute

(CardMatcher String
d1 Card -> Bool
f) matchOr :: CardMatcher -> CardMatcher -> CardMatcher
`matchOr` (CardMatcher String
d2 Card -> Bool
g) =
    String -> (Card -> Bool) -> CardMatcher
CardMatcher (String
d1 String -> String -> String
forall a. Semigroup a => a -> a -> a
<> String
" or " String -> String -> String
forall a. Semigroup a => a -> a -> a
<> String
d2) ((Card -> Bool) -> CardMatcher) -> (Card -> Bool) -> CardMatcher
forall a b. (a -> b) -> a -> b
$ \Card
c -> Card -> Bool
f Card
c Bool -> Bool -> Bool
|| Card -> Bool
g Card
c

invert :: CardMatcher -> CardMatcher
invert :: CardMatcher -> CardMatcher
invert (CardMatcher String
d Card -> Bool
f) = String -> (Card -> Bool) -> CardMatcher
CardMatcher (String
"not " String -> String -> String
forall a. Semigroup a => a -> a -> a
<> String
d) ((Card -> Bool) -> CardMatcher) -> (Card -> Bool) -> CardMatcher
forall a b. (a -> b) -> a -> b
$ Bool -> Bool
not (Bool -> Bool) -> (Card -> Bool) -> Card -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Card -> Bool
f

labelMatch :: String -> CardMatcher -> CardMatcher
labelMatch :: String -> CardMatcher -> CardMatcher
labelMatch String
label (CardMatcher String
d Card -> Bool
f) = String -> (Card -> Bool) -> CardMatcher
CardMatcher String
label Card -> Bool
f

applyMatcherWithDesc :: CardMatcher -> Card -> Either String ()
applyMatcherWithDesc :: CardMatcher -> Card -> Either String ()
applyMatcherWithDesc (CardMatcher String
d Card -> Bool
f) Card
c =
  if Card -> Bool
f Card
c then
    () -> Either String ()
forall a b. b -> Either a b
Right ()
  else
    String -> Either String ()
forall a b. a -> Either a b
Left String
d

applyMatcher :: CardMatcher -> Card -> Bool
applyMatcher :: CardMatcher -> Card -> Bool
applyMatcher CardMatcher
matcher Card
c =
  case CardMatcher -> Card -> Either String ()
applyMatcherWithDesc CardMatcher
matcher Card
c of
    Left String
_ -> Bool
False
    Right ()
_ -> Bool
True