-----------------------------------------
-- Programm "Routenberechnung"  --
-----------------------------------------
module Route where
import System 
------------------------
-- Die Hauptfunktion  --
------------------------    

main :: IO ()
main =  do (from,to,datei) <- readArguments
           roads           <- readRoads datei
           putStr (formatRoute (route roads from to))

{-
readArguments :: IO (Town,Town,String)
readRoads     :: IO [Road]
route         :: [Road] -> Town -> Town -> Maybe Route
formatRoute   :: Maybe Route  -> String
-}

-----------------------------------------------------
-- Teilschritt 1: Einlesen der Argumente           --
-----------------------------------------------------

-- vordefiniert in Bibliothek System
-- getArgs :: IO [String] 

type Town = String

readArguments :: IO (Town,Town,String)
readArguments =  do [from,to,datei] <- getArgs
                    return (from,to,datei) 


-----------------------------------------------
-- Teilschritt 2: Einlesen der Straßeninfos  --
-----------------------------------------------

type Road = (Town,Town,Int) 
readRoads :: String -> IO [Road]
readRoads dat =  do cs <- readFile dat
                    return [ road (words line) 
                           | line <- lines cs ]

road :: [String] -> Road
road [from,to,dist] = (from,to,read dist) 


-----------------------------------------------
-- Teilschritt 3: Formatierung der Ausgabe   --
-----------------------------------------------

type Route = (Town, Town, Int, [(Town,Int)])

formatRoute :: Maybe Route -> String
formatRoute Nothing = "Keine Route gefunden"
formatRoute (Just (from, to, dist, stages)) 
  = unlines [formatStage s | s <- stages]

formatStage :: (String,Int) -> String
formatStage (t,d) = pad 5 (show d) ++ ' ':t

pad :: Int -> String -> String 
pad n s = [' ' | i <- [length s+1 .. n]] ++ s

----------------------------------------------------
-- Teilschritt 4: Berechnung der kürzesten Route  --
----------------------------------------------------

route :: [Road] -> Town -> Town -> Maybe Route
route =  routeAvoiding []

routeAvoiding :: [Town] -> [Road] -> Town -> Town -> Maybe Route
routeAvoiding avoid roads from to | from  == to 
  = Just (to,to,0,[])
routeAvoiding avoid roads from to 
  = best 
    [joinRoutes (road2Route road)
                (routeAvoiding (from:avoid) roads (endP road) to)
         | road <- bothWays roads, 
           startP road == from,
           not (endP road `elem` avoid)]

startP, endP :: Road -> Town
startP (from, to, dist) = from
endP   (from, to, dist) = to

better :: Maybe Route -> Maybe Route -> Maybe Route
better r1@(Just (from1,to1,dist1,stages1)) 
       r2@(Just (from2,to2,dist2,stages2))
   | dist1 < dist2 = r1
   | dist1 > dist2 = r2
   | lr1   >= lr2  = r1
   | lr1   <  lr2  = r2
   where lr1 = length stages1
         lr2 = length stages2
better Nothing r2 = r2
better r1 Nothing = r1


best :: [Maybe Route] -> Maybe Route 
best = foldr better Nothing 

joinRoutes :: Route -> Maybe Route -> Maybe Route 
joinRoutes r1@(from1,to1,dist1,stages1) 
           (Just r2@(from2,to2,dist2,stages2))
   | to1 == from2 = Just (from1, to2, dist1+dist2,
                          stages1 ++ [(t,d+dist1) 
                                     | (t,d) <- stages2])
   | otherwise    = Nothing 
joinRoutes r1 Nothing = Nothing 


road2Route :: Road -> Route
road2Route (from, to, dist) 
   = (from, to, dist, [(to,dist)])

bothWays :: [Road] -> [Road]
bothWays roads = roads ++ [(to,from,dist)| 
                             (from,to,dist) <- roads]








-- Routenverfolgung zu Debugging-Zwecken
debugmain :: Town -> Town -> String -> IO ()
debugmain from to datei 
       =  do -- (from,to,datei) <- readArguments
             roads           <- readRoads datei
             putStr $ unlines $ map show (debugrouteA [] roads from to)



debugrouteA :: [Town] -> [Road] -> Town -> Town -> [Road] 
debugrouteA avoid roads from to | from  == to = []
debugrouteA avoid roads from to 
   = concat [road : debugrouteA (from:avoid) roads (endP road) to
         | road <- bothWays roads, 
           startP road == from, 
           not (endP road `elem` avoid)]