module Board(
        Color(..),
        Board(..),
        Coordinates(..),
        emptyBoard,
        addToBoard,
        hasWon,
        toString,
        negateColor,
        hasNeighbors,
        checkAllDirections,
        areCoordinatesEmpty,
        neighborsOfCoordinates        
    )where

import Data.Map as Map
import Data.Maybe
import Data.Char
import Data.Tree
import Data.List

data Coordinates = Coordinates Int Int deriving (Eq)

instance Ord Coordinates where
  compare (Coordinates x1 y1) (Coordinates x2 y2)
      | x1 == x2 && y1 == y2 = EQ
      | x1 > x2 = GT
      | x1 == x2  && y1 > y2 = GT
      | x1 == x2  && y1 < y2 = LT
      | x1 < x2 = LT

data Color = White | Black deriving Eq

instance Show Color where
   show Black = "X"
   show White = "O"

data Board = Board (Map Coordinates Color)

instance Show Board where
    show = toString

sizeOfBoard::Int
sizeOfBoard = 19

emptyBoard:: Board
emptyBoard = Board (Map.empty)

areCoordinatesEmpty:: Board->Coordinates->Bool
areCoordinatesEmpty (Board mapInBoard) coordinates =  areCoodinatesWithinBounds coordinates && notMember coordinates mapInBoard

areCoodinatesWithinBounds::Coordinates->Bool
areCoodinatesWithinBounds (Coordinates x y) = x>0 && x<(sizeOfBoard+1) && y>0 && y<(sizeOfBoard+1)

addToBoard:: Board->Coordinates->Color->Board
addToBoard (Board map) coordinates color = Board (Map.insert coordinates color map)

hasWon:: Board->Color->Bool
hasWon (Board map) color = Prelude.any (checkAllDirections listOfCoordinates) listOfCoordinates
    where
        listOfCoordinates = [coords | coords <- Map.keys map, fromJust (Map.lookup coords map) == color]

checkAllDirections:: [Coordinates]->Coordinates->Bool
checkAllDirections listOfCoordinates coords = Prelude.any (isFiveInDirection 1 coords listOfCoordinates) [(x,y)| x<-[-1..1], y<-[-1..1], not(x==0 && y==0)]

isFiveInDirection:: Int->Coordinates->[Coordinates]->(Int,Int)->Bool
isFiveInDirection howMany (Coordinates x y) listOfCoordinates (xTranslation, yTranslation)
    | ((Coordinates x y) `elem` listOfCoordinates) && (howMany==5) = True
    | (Coordinates x y) `elem` listOfCoordinates = True && isFiveInDirection (howMany+1) (Coordinates (x+xTranslation) (y+yTranslation)) listOfCoordinates (xTranslation, yTranslation)
    | otherwise = False

negateColor:: Color->Color
negateColor color
    | color==White = Black
    | otherwise = White

joinListOfStrings:: [String]->String
joinListOfStrings = intercalate ""

charToString:: Char->String
charToString a = [a]

toString:: Board->String
toString (Board map) =
    Prelude.concat ["  ",joinListOfStrings $ Prelude.map charToString [x | x<-['A'..'S']],"\n",joinListOfStrings $ Prelude.map combine [Coordinates x y | x <- [1..sizeOfBoard], y <- [1..sizeOfBoard]]]
        where
            combine s = index s ++ mapToCharacter s ++ newLine s
            mapToCharacter coordinates
                | member coordinates map = show $ fromJust (Map.lookup coordinates map)
                | otherwise = " "
            newLine (Coordinates x y)
                | y == sizeOfBoard = "\n"
                | otherwise = ""
            index (Coordinates x y)
                | y == 1 && x < 10 = " " ++ show x
                | y == 1 = show x
                | otherwise = ""
            
hasNeighbors:: Coordinates->[Coordinates]->Bool
hasNeighbors (Coordinates x y) listOfCoordinates = 
    Prelude.or $ Prelude.map (\(xTranslation, yTranslation) -> ((Coordinates (x+xTranslation) (y+yTranslation)) `elem` listOfCoordinates)) [(x,y) | x<-[-1..1], y<-[-1..1], not(x==0 && y==0)]

neighborsOfCoordinates:: Coordinates->Board->[Coordinates]
neighborsOfCoordinates (Coordinates x y) (Board board) = [a | a<-allNeighbors (Coordinates x y), notMember a board]
    where
        allNeighbors (Coordinates x y) = [Coordinates (x+a) (y+b) | a<-[-1..1], b<-[-1..1], not(a==0 && b==0), (x+a)>0, (y+b)>0, (x+a)<(sizeOfBoard+1), (y+b)<(sizeOfBoard+1)]