{- sv2v
 - Author: Zachary Snow <zach@zachjs.com>
 -
 - Conversion for named function and task arguments
 -
 - This conversion takes the named arguments and moves them into their
 - corresponding position in the argument list, with names removed.
 -}

module Convert.KWArgs (convert) where

import Data.List (elemIndex, sortOn)
import Data.Maybe (mapMaybe)
import Control.Monad.Writer
import qualified Data.Map.Strict as Map

import Convert.Traverse
import Language.SystemVerilog.AST

type TFs = Map.Map Identifier [Identifier]

convert :: [AST] -> [AST]
convert = map $ traverseDescriptions convertDescription

convertDescription :: Description -> Description
convertDescription description =
    traverseModuleItems
        (traverseExprs $ traverseNestedExprs $ convertExpr tfs)
        description
    where
        tfs = execWriter $ collectModuleItemsM collectTF description

collectTF :: ModuleItem -> Writer TFs ()
collectTF (MIPackageItem (Function _ _ f decls _)) = collectTFDecls f decls
collectTF (MIPackageItem (Task     _   f decls _)) = collectTFDecls f decls
collectTF _ = return ()

collectTFDecls :: Identifier -> [Decl] -> Writer TFs ()
collectTFDecls name decls =
    tell $ Map.singleton name $ mapMaybe getInput decls
    where
        getInput :: Decl -> Maybe Identifier
        getInput (Variable Input _ ident _ _) = Just ident
        getInput _ = Nothing

convertExpr :: TFs -> Expr -> Expr
convertExpr _ (orig @ (Call Nothing _ (Args _ []))) = orig
convertExpr tfs (Call Nothing func (Args pnArgs kwArgs)) =
    case tfs Map.!? func of
        Nothing -> Call Nothing func (Args pnArgs kwArgs)
        Just ordered -> Call Nothing func (Args args [])
            where
                args = pnArgs ++ (map snd $ sortOn position kwArgs)
                position (x, _) = elemIndex x ordered
convertExpr _ other = other