{-# LANGUAGE TemplateHaskell, ScopedTypeVariables #-}
{-# OPTIONS_GHC -Wall -fno-warn-name-shadowing #-}
module Data.Cursor.CLASE.Gen.Adapters(adapterGen) where

import Data.List
import Data.Maybe
import Data.Cursor.CLASE.Gen.Util
import Language.Haskell.TH
import Data.Cursor.CLASE.Gen.PrintM
import System.FilePath
import System.IO

import qualified Data.Map as Map
import Data.Map (Map)

adapterGen :: [String] -> Name -> [Name] -> String -> Q [Dec]
adapterGen moduleNames rootName acceptableNames gendLanguage = do
  nameMap <- buildMap acceptableNames

  fileOut <- runIO $ openFile (joinPath moduleNames <.> "hs") WriteMode

  let rootNameModule = nameModule rootName
  let moduleName = concat . intersperse "." $ moduleNames


  runIO . runPrint fileOut $ do
    printHeader moduleName (fromMaybe "" rootNameModule) gendLanguage

    printClasses rootName nameMap

    printVisitCursor rootName

    printInstanceHeader rootName acceptableNames

    let contextCtrs = buildContextCtrs nameMap

    printVisitStep rootName nameMap contextCtrs

    printVisitPartial rootName acceptableNames contextCtrs

    printCursor
  runIO $ hFlush fileOut
  runIO $ hClose fileOut

  return [] 

printHeader :: String -> String -> String -> PrintM ()
printHeader modName rootModName rootLangModName = do
  printLn . unlines $ [ "{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses, FlexibleContexts,"
    , "             UndecidableInstances, RankNTypes, GADTs, TypeFamilies #-}"
    , "{-# OPTIONS_GHC -Wall -fno-warn-orphans -fno-warn-name-shadowing #-}"

    , "module " ++ modName ++ " where"
    , "{- AUTOGENERATED (See Data.Cursor.CLASE.Gen.Adapters) -}"
    , ""
    , "import " ++ rootModName 
    , "import " ++ rootLangModName
    , "import Data.Cursor.CLASE.Language"
    , "import Data.Cursor.CLASE.Bound"
    , "import Data.Cursor.CLASE.Traversal"
    ]

printClasses :: Name -> Map Name DataType -> PrintM ()
printClasses rootName = mapM_ (uncurry printClass) . Map.toList
  where
    rootNameS = nameBase rootName

    printClass :: Name -> DataType -> PrintM ()
    printClass clNam (DataType _ ctrs) = do
      printLn $ "class " ++ rootNameS ++ "TraversalAdapter" ++ clNamS ++ " t where"
      mapM_ printCases ctrs
      printLn ""
      where
        clNamS = nameBase clNam

        printCases :: Constructor -> PrintM ()
        printCases ctr = do
          printLn $ "  visit" ++ ctrNameS ++ " :: " ++ tipe
            where
              tipe = concat . intersperse " -> " $ clNamS : 
                (map (const "t") . filter isNavigable . ctrKids $ ctr) ++ ["t"]
              ctrNameS = nameBase .ctrName $ ctr 

    printClass _ _ = error "printClass for wraps list not supported [yet]!"
  
   

printVisitCursor :: Name -> PrintM ()
printVisitCursor nam = do
  printLn . unlines $ 
    [ "class " ++ (nameBase nam) ++ "TraversalAdapterCursor t where"
    , "  visitCursor :: " ++ (nameBase nam) ++ " -> t -> t"
    ]

printInstanceHeader :: Name -> [Name] -> PrintM ()
printInstanceHeader rootName okNames = do
  printLn $ "instance (" ++ preconds ++
            "\n          " ++ rootNameS ++ "TraversalAdapterCursor t," ++
            "\n          Bound " ++ rootNameS ++ " t) => Traversal " ++ rootNameS ++ " t where\n"
  where
    preconds = concat . intersperse "\n          " . 
                        map (\n -> rootNameS ++ "TraversalAdapter" ++ n ++ " t,") $ okNameS

    rootNameS = nameBase rootName
    okNameS = map nameBase okNames
  

printVisitStep :: Name -> Map Name DataType -> [ContextCtr] -> PrintM ()
printVisitStep rootName nameMap ctxCtrs = do
  printLn . unlines $ 
    [ "  visitStep it recurse = case reify it of"
    , "    TW x -> visitStep' x it recurse"
    , "    where"
    , "      visitStep' :: (" ++ preconds ++ ") =>"
    , "                    TypeRepI a -> a -> (forall b . Reify " ++ rootNameS ++ " b => Movement " ++ rootNameS ++ " Down a b -> t) -> t"
    ]
  mapM_ (uncurry printVisitStepCase) (Map.toList nameMap)
  printLn ""
  where
    preconds = concat . intersperse ",\n                     " . 
                        map (\n -> rootNameS ++ "TraversalAdapter" ++ n ++ " t") .
                        map nameBase . 
                        Map.keys $ nameMap
    rootNameS = nameBase rootName

    printVisitStepCase :: Name -> DataType -> PrintM ()
    printVisitStepCase tipe (DataType _ ctrs) = do
      printLn $ "      visitStep' " ++ tipeS ++ "T it recurse = case it of"
      mapM_ printVisitStepCtrCase ctrs
        where
          tipeS = nameBase tipe

          printVisitStepCtrCase (Ctr ctrName kids) = do
            printLn $ "        " ++ ctrNameS ++ " " ++ underscorePattern ++ " -> visit" ++ ctrNameS ++ " it " ++ recursePatterns
              where
                ctrNameS = nameBase ctrName
                underscorePattern = unwords $ replicate (length kids) "_" 
                recursePatterns =  unwords . map (\k -> "(recurse (MW " ++ (downCtrName k) ++ "))") . filter ( (== ctrName) . ctxCtrCtrTo) $ ctxCtrs
                --recursePatterns = unwords . map (\k -> "(recurse (MW M" ++ ctrNameS ++ "To" ++ (nameBase . childType $ k) ++ "))") . filter isNavigable $ kids
  
    printVisitStepCase _ _ = error "Can't prinit visitStep case for list dts yet"


printVisitPartial :: Name -> [Name] -> [ContextCtr] -> PrintM ()
printVisitPartial rootName okNames ctxCtrs = do
  printLn . unlines $ 
    [ "  visitPartial (CW ctx) = visitPartial' ctx"
    , "    where"
    , "      visitPartial' :: (" ++ preconds ++ ") =>"
    , "          ContextI a b -> b -> t -> (forall c . Reify " ++ rootNameS ++ " c => Movement " ++ rootNameS ++ " Down b c -> t) -> t"
    , "      visitPartial' ctx it hole recurse = case ctx of"
    ]
  mapM_ printCtrCase ctxCtrs
  printLn ""
  where
    rootNameS :: String
    rootNameS = nameBase rootName

    preconds :: String
    preconds = concat . intersperse ",\n                        " . 
                        map (\n -> rootNameS ++ "TraversalAdapter" ++ n ++ " t") $ okNameS
    okNameS = map nameBase okNames

    printCtrCase :: ContextCtr -> PrintM ()
    printCtrCase cc = do
      printLn $ "          " ++ (ctxCtrName cc) ++ " " ++ underscores ++ " -> visit" ++ (nameBase . ctxCtrCtrTo $ cc) ++ " it " ++ recursePatterns
        where
          underscores = unwords $ replicate (numCCArgs cc) "_"
          recursePatterns =  unwords . map (\k -> if k == cc 
                                                    then "hole"
                                                    else "(recurse (MW " ++ (downCtrName k) ++ "))") . filter ( (==(ctxCtrCtrTo cc) ) . ctxCtrCtrTo) $ ctxCtrs

printCursor :: PrintM ()
printCursor = printLn $ "  cursor = visitCursor"