import Data.List.Split
import Data.Matrix
import Data.Maybe
import Data.Foldable
import Data.PQueue.Min

type Value = Integer
type Distance = Integer
type Position = (Int, Int)
data Element = Element {
    value :: Value,
    distance :: Maybe Distance,  -- Nothing represents infinity
    position :: Position
 } deriving (Eq, Show)
data PQE = PQE Element Distance deriving (Eq, Show)

instance Ord PQE where
    PQE _ Nothing <= _ = False
    _ <= PQE _ Nothing = True
    PQE _ (Just d1) <= PQE _ (Just d2) = d1 <= d2

getNeighbours :: Matrix Element -> Element -> [Element]
getNeighbours m e =
    let (i, j) = position e
    in  catMaybes [safeGet (i - 1) j      m,
                   safeGet (i + 1) j      m,
                   safeGet  i     (j - 1) m,
                   safeGet  i     (j + 1) m]    

dijkstra :: Position -> Matrix Element -> MinQueue PQE -> Distance
dijkstra p m q =
    let PQE minElement minDistance = findMin q
    in  if position minElement == p
        then minDistance
        else let (newM, newQ) = foldr update (m, q) $ getNeighbours m minElement
             in  dijkstra p newM newQ
        where update neighbour (prevM, prevQ) =
                let newDistance = minDistance + value neighbour
                in  case distance neighbour of
                    Nothing -> undefined
                    Just d  -> undefined

findShortestPathLength :: Matrix Element -> Distance
findShortestPathLength m =
    let initialElement = m ! (1, 1)
        initialMinQ = singleton $ PQE initialElement $ value initialElement
        lastPos = (nrows m, ncols m)
    in  dijkstra lastPos m initialMinQ


setInitial :: Matrix Element -> Matrix Element
setInitial m =
    let v = value $ m ! (1, 1)
    in unsafeSet (Element v (Just v) (1, 1)) (1, 1) m

initElement :: Matrix Integer -> Position -> Element
initElement m p =
    let value = m ! p
    in  Element value Nothing p

toElementMatrix :: Matrix Integer -> Matrix Element
toElementMatrix m =
    matrix (nrows m) (ncols m) (initElement m)

main :: IO ()
main = do
    contents <- readFile "p083_matrix.txt"
    let listsMatrix = fmap (fmap read . (splitOn ",")) $ lines contents :: [[Integer]]
        valueMatrix = fromLists listsMatrix
        unvisitedMatrix = toElementMatrix valueMatrix
        mtrx = setInitial unvisitedMatrix
    print $ findShortestPathLength mtrx