content: {-# LANGUAGE OverloadedStrings #-}
  module Code ( generate, generateWithAnnotation ) where
  import Data.List (partition, intersperse)
  import qualified Data.HashMap.Strict as Map
  import qualified Data.Text as T
  import Text.Parsec.Pos
  
  import Types
  generate :: [Chunk] -> T.Text
  generate = expand . merge . (filter isDef)
  generateWithAnnotation :: String -> [Chunk] -> T.Text
  generateWithAnnotation ext = expand . merge . (annotate ext) . (filter isDef)
  
  annotate :: String -> [Chunk] -> [Chunk]
  annotate langExt chunks = map annotateChunk chunks
      where
          annotateChunk (Def sourcePos name parts) =
              Def sourcePos name $ (Code $ annotation sourcePos):parts
          annotation sourcePos = T.pack $ annotateForLang langExt (sourceName sourcePos) (sourceLine sourcePos) ++ "\n"
          annotateForLang ext filePath lineNo = (comment ext) ++ " " ++ filePath ++ ":" ++ (show lineNo)
          comment ".sh" = "#"
          comment ".hs" = "--"
          comment _ = "//"
  merge :: [Chunk] -> [Chunk]
  merge = mergeAux []
  mergeAux ans [] = ans
  mergeAux ans (next:rest) =
      let
          name = getName next
          chunkHasName name = (== name) . getName
          (found, rem) = partition (chunkHasName name) rest
          merged = combineChunks (next:found)
      in
          mergeAux (merged:ans) rem
  combineChunks :: [Chunk] -> Chunk
  combineChunks (a:[]) = a
  combineChunks l@(c:cs) = Def line name parts
      where
          parts = concatMap getParts l
          name = getName c
          line = getLineNo c
  expand :: [Chunk] -> T.Text
  expand chunks =
      expandParts parts partMap T.empty
      where
          -- map (name, parts)
          partMap = Map.fromList $ zip (map getName chunks) (map getParts chunks)
          backup = getParts $ last chunks
          parts = Map.lookupDefault backup "*" partMap
  expandParts :: [Part] -> Map.HashMap T.Text [Part] -> T.Text -> T.Text
  expandParts parts partMap baseIndent =
      T.concat $ map toText parts
      where
          toText part =
              case part of
              Code txt -> T.append baseIndent txt
              Ref name indent -> (expandParts refParts partMap (T.append baseIndent indent))
                  where refParts = Map.lookupDefault [] (T.strip name) partMap
  