module Politics(Relation(..), 
  RelationshipMap,
  mkRelationshipMap, Friendliness,
  AttitudeMap,
  modAttitude,
  nullAttitudes,
  attitude,
  consequences
  )
where

import Data.List

import qualified Data.Edison.Assoc.StandardMap as M

data Relation = Unknown
              | Peace
              | War
              | ColonyOf
              | Allied
  deriving (Show)

type Friendliness = Int
type RelationshipMap = M.FM (String, String) Relationship
type Relationship = (Relation, Friendliness)

emptyRelationshipMap :: RelationshipMap
emptyRelationshipMap = M.empty

defaultRelationship = (Unknown, 0)

setRelationship :: Relationship -> String -> String -> RelationshipMap -> RelationshipMap
setRelationship r s1 s2 = M.insert (s1, s2) r

setRelationship2 :: Relationship -> String -> String -> RelationshipMap -> RelationshipMap
setRelationship2 r s1 s2 = setRelationship r s1 s2 . setRelationship r s2 s1

modRelationship :: (Relationship -> Relationship) -> String -> String -> RelationshipMap -> RelationshipMap
modRelationship f s1 s2 = M.adjustOrInsert f (f defaultRelationship) (s1, s2)

modRelationship2 :: (Relationship -> Relationship) -> String -> String -> RelationshipMap -> RelationshipMap
modRelationship2 f s1 s2 = modRelationship f s1 s2 . modRelationship f s2 s1

mkRelationshipMap :: [((String, String), Relationship)] -> RelationshipMap
mkRelationshipMap = foldl' go emptyRelationshipMap
  where go acc ((s1, s2), r) = setRelationship2 r s1 s2 acc

type AttitudeMap = M.FM String Friendliness

nullAttitudes :: [String] -> AttitudeMap
nullAttitudes as = M.fromSeq $ zip as (repeat 0)

friendliness :: String -> String -> RelationshipMap -> Friendliness
friendliness s1 s2 = snd . M.lookupWithDefault defaultRelationship (s1, s2)

attitude :: String -> AttitudeMap -> Friendliness
attitude = M.lookupWithDefault 0

modAttitude :: (Friendliness -> Friendliness) -> String -> AttitudeMap -> AttitudeMap
modAttitude f = M.adjustOrInsert f (f 0)

setAttitude :: Friendliness -> String -> AttitudeMap -> AttitudeMap
setAttitude = flip M.insert

setAttitudes :: [(String, Friendliness)] -> AttitudeMap -> AttitudeMap
setAttitudes = flip (foldl' go)
  where go acc (s, a) = setAttitude a s acc

consequences :: Friendliness    -- ^ Act done by an actor.
             -> String          -- ^ Name of the state which was the target of the act.
             -> [String]        -- ^ Observing states.
             -> RelationshipMap -- ^ Map of relationships.
             -> AttitudeMap     -- ^ Map of attitudes.
             -> AttitudeMap     -- ^ New attitudes towards the actor.
consequences act tgt obss rm atts = 
  let nas = map (\obs -> (attitude obs atts) + (act * (friendliness obs tgt rm))) obss
  in setAttitudes (zip obss nas) atts

