module PathFindingCore.PathingMap.Interpreter(fromMapString, PathingGrid, PathingMapString(..), PathingMapData(..)) where

import Data.Array.IArray(Array, assocs, listArray)
import Data.List(drop, isSuffixOf, last, reverse, unzip)
import Data.List.Split(splitOn)

import PathFindingCore.PathingMap.Coordinate(Coordinate(Coord))
import PathFindingCore.PathingMap.Terrain(charToTerrain, Terrain(Goal, Self))

type PathingGrid = Array Coordinate Terrain

data PathingMapString
  = PathingMapString {
      str   :: Text,
      delim :: Text
    } deriving (Eq)

data PathingMapData
  = PathingMapData {
      start :: Coordinate,
      goal  :: Coordinate,
      grid  :: PathingGrid
    } deriving (Eq, Show)

fromMapString :: PathingMapString -> PathingMapData
fromMapString (PathingMapString ""  _)     = error "Cannot build map from empty string"
fromMapString (PathingMapString str delim) = PathingMapData start goal grid
  where
    sDelim        = asString delim
    grid          = str |> (asString >>> (dropDelim sDelim) >>> (splitOn sDelim) >>> strListToGrid)
    (start, goal) = findStartAndGoal grid
    dropDelim d s = if (isSuffixOf d s) then s |> (reverse >>> (drop $ length d) >>> reverse) else s

-- The need for `rotateClockwise` likely seems bizarre, at first glance.  To frame the reason for its necessity:
-- We start with rows of strings (strs[a][b] => a: 0 = top row, b: 0 = leftmost character) and we need to transform
-- it such that it follows normal Cartesian coordinate rules (strs[a][b] => a: 0 = leftmost character, b: 0 = bottom
-- row) --JAB (8/13/14)
strListToGrid :: [String] -> PathingGrid
strListToGrid strList = listArray (Coord 0 0, endCoord) terrains
  where
    str      = fold $ rotateClockwise strList
    terrains = fmap charToTerrain str
    length'  = length >>> (subtract 1)
    xLength  = strList |> (last >>> length')
    yLength  = strList |> (length')
    endCoord = Coord xLength yLength

findStartAndGoal :: PathingGrid -> (Coordinate, Coordinate)
findStartAndGoal arr = analyzeResult $ foldr findBest (Nothing, Nothing) (assocs arr)
  where

    findBest (coord, Self) (_, g) = (Just coord, g)
    findBest (coord, Goal) (s, _) = (s,          Just coord)
    findBest _             (s, g) = (s,          g)

    analyzeResult (Nothing,    _      )   = error "No start in given grid."
    analyzeResult (_,          Nothing)   = error "No goal in given grid."
    analyzeResult (Just start, Just goal) = (start, goal)

rotateClockwise :: [[t]] -> [[t]]
rotateClockwise ts = reverse (helper ts [])
  where
    helper xs acc | isUseless xs = acc
    helper xs acc                = xs |> ((fmap unsnap) >>> unzip >>> (recurse acc))
    isUseless                    = concat >>> null
    unsnap (x : xs)              = (x, xs)
    unsnap []                    = error "Impossible condition achieved"
    recurse acc (row, remainder) = helper remainder $ (reverse row) : acc