sliceCppGen :: Map.Map [String] [MethodDecl] -> CppGenArgs -> SliceDecl -> [(FilePath,BS.ByteString)]
sliceCppGen fctyMthds args decl = go [] decl
  where
    go ns (ModuleDecl (Ident mName) decls)     = concatMap (go (ns++[mName])) decls
    go ns (InterfaceDecl (Ident nm) ext mthds) = [ (targetDir args </> nm ++ "I.h",   genH   ns nm mthds)
                                                 , (targetDir args </> nm ++ "I.cpp", genCpp ns nm mthds)]
    go ns _                                    = []
    
    genH :: [String] -> String -> [MethodDecl] -> BS.ByteString
    genH ns nm mthds = let staticMthds = fromMaybe [] $ Map.lookup (ns ++ [nm++"Factory"]) fctyMthds
                           hnames      = [replaceExtension (takeFileName $ icefile args) ".h"]
                           classCont   = \indent -> genClass indent nm mthds staticMthds
                           ctnt        =  genInclds hnames <> genNs "" ns classCont
                           guardItems  = if longguard args then ns ++ [nm] else [nm]
                       in genIfDef guardItems ctnt
    
    genCpp ns nm mthds = let fwdMthds    = fromMaybe [] $ Map.lookup (ns++[nm]) fctyMthds
                             staticMthds = fromMaybe [] $ Map.lookup (ns ++ [nm++"Factory"]) fctyMthds
                             mthdsCont   = \indent -> genMethods indent nm (mthds ++ staticMthds) fwdMthds
                             hnames      = (targetDir args </> nm ++ "I.h")
                                           : if null fwdMthds then [] else [targetDir args </> replaceTail "Factory" "" nm ++ "I.h"]
                         in genInclds hnames <> genNs "" ns mthdsCont
                            
    genIfDef tkns ctnt = let ident = fromString $ intercalate "_" $ map (map toUpper) (concatMap camelSplit tkns) ++ ["I","H"]
                         in ("#ifndef " <> ident <>"\n#define " <> ident <> "\n\n") <> ctnt <> "\n#endif"
                            
    genInclds xs = mconcat (map (\x -> "#include <" <> fromString x <> ">\n") xs) <> "\n"
    
    genNs :: String -> [String] -> (String -> BS.ByteString) -> BS.ByteString
    genNs indent (ns:nss) cont = let indent' = fromString indent 
                                 in indent' <> "namespace " <> fromString ns <> "\n" <> indent' <> "{\n"
                                    <> genNs ('\t':indent) nss cont <> indent' <> "};\n"
    genNs indent []       cont = cont indent
    
    genClass :: String -> String -> [MethodDecl] -> [MethodDecl] -> BS.ByteString
    genClass indent nm  mthds staticMthds = let indent'  = fromString indent
                                                nm'      = fromString nm
                                                override = if cpp98 args then "" else " override"
                                            in indent' <> "class " <> nm' <> "I: public " <> nm' <> "\n" <> indent' <> "{\n" <> indent'<> "public:\n" 
                                               <> genMethodHeads override ('\t':indent) mthds  
                                               <> (if null staticMthds then "" 
                                                   else "\n" <> genMethodHeads "" ('\t':indent ++ "static ") staticMthds)
                                               <> indent' <> "};\n"
                                                                                                                                              
                                   
    genMethods indent nm mthds fwdMthds = let indent'  = fromString indent
                                              nm'      = fromString nm
                                              bodyStub = "\n" <> indent' <> "{\n" <> indent' <> "}\n\n" <> indent'
                                          in if null fwdMthds
                                             then indent' <> (BS.intercalate bodyStub (map (genMethodHead (nm' <> "I::")) mthds) 
                                                   <> "\n" <> indent' <> "{\n" <> indent' <> "}\n") 
                                             else BS.intercalate "\n" (map (genFwdMethod indent' nm) fwdMthds)
    
    genMethodHeads override indent mthds = fromString indent <> BS.intercalate (override <> ";\n" <> fromString indent) (map (genMethodHead "") mthds) <> override <> ";\n"
    
    genMethodHead :: BS.ByteString -> MethodDecl -> BS.ByteString
    genMethodHead scope (MethodDecl tp (Ident nm) flds _ _) = genType tp <> " " <> scope <> fromString nm <> "(" <> genFields flds <> (if null flds then "" else ", ") <> "const Ice::Current& current)"
    
    fs = fromString
    
    genFwdMethod indent scope mdecl@(MethodDecl tp (Ident nm) flds _ _) = 
      let scope' = fs $ replaceTail "Factory" "" scope
          vals   = map (\(FieldDecl _ (Ident fnm) _) -> fs fnm) flds ++ ["current"]
          ret    = if tp == STVoid then "" else "return "
      in indent <> genMethodHead (fs scope <> "I::") mdecl <> "\n" <> indent <> "{\n" <> indent <> "\t" <>
         ret <> scope' <> "I::" <> fs nm <> "(" <> BS.intercalate ", " vals <> ");\n" <> indent <> "}\n"
    
    genField (FieldDecl tp (Ident nm) _) = passRefOrVal tp <> " " <> fromString nm
    
    genFields flds = BS.intercalate ", " $ map genField flds
    
    genType :: SliceType -> BS.ByteString
    genType STVoid                                 = "void"
    genType STBool                                 = "bool"
    genType STByte                                 = "Ice::Byte"
    genType STShort                                = "Ice::Short"
    genType STInt                                  = "Ice::Int"
    genType STLong                                 = "Ice::Long"
    genType STFloat                                = "float"
    genType STDouble                               = "double"
    genType STString                               = "std::string"
    genType (STUserDefined (NsQualIdent nm ns))    = fromString (intercalate "::" (ns ++ [nm]))
    genType (STUserDefinedPrx (NsQualIdent nm ns)) = fromString (intercalate "::" (ns ++ [nm])) <> "Prx"
    
    passRefOrVal tp@(STString)        = "const " <> genType tp <> "&"
    passRefOrVal tp@(STUserDefined _) = "const " <> genType tp <> "&"
    passRefOrVal tp                   = genType tp
sliceCppGen :: Map.Map [String] [MethodDecl] -> CppGenArgs -> SliceDecl -> [(FilePath,BS.ByteString)]
sliceCppGen fctyMthds args decl = go [] decl
  where
    go ns (ModuleDecl (Ident mName) decls)     = concatMap (go (ns++[mName])) decls
    go ns (InterfaceDecl (Ident nm) ext mthds) = [ (targetDir args </> nm ++ "I.h",   genH   ns nm mthds)
                                                 , (targetDir args </> nm ++ "I.cpp", genCpp ns nm mthds)]
    go ns _                                    = []
    
    genH :: [String] -> String -> [MethodDecl] -> BS.ByteString
    genH ns nm mthds = let staticMthds = fromMaybe [] $ Map.lookup (ns ++ [nm++"Factory"]) fctyMthds
                           hnames      = [replaceExtension (takeFileName $ icefile args) ".h"]
                           classCont   = \indent -> genClass indent nm mthds staticMthds
                           ctnt        =  genInclds hnames <> genNs "" ns classCont
                           guardItems  = if longguard args then ns ++ [nm] else [nm]
                       in genIfDef guardItems ctnt
    
    genCpp ns nm mthds = let fwdMthds    = fromMaybe [] $ Map.lookup (ns++[nm]) fctyMthds
                             staticMthds = fromMaybe [] $ Map.lookup (ns ++ [nm++"Factory"]) fctyMthds
                             mthdsCont   = \indent -> genMethods indent nm (mthds ++ staticMthds) fwdMthds
                             hnames      = (targetDir args </> nm ++ "I.h")
                                           : if null fwdMthds then [] else [targetDir args </> replaceTail "Factory" "" nm ++ "I.h"]
                         in genInclds hnames <> genNs "" ns mthdsCont
                            
    genIfDef tkns ctnt = let ident = fromString $ intercalate "_" $ map (map toUpper) (concatMap camelSplit tkns) ++ ["I","H"]
                         in ("#ifndef " <> ident <>"\n#define " <> ident <> "\n\n") <> ctnt <> "\n#endif"
                            
    genInclds xs = mconcat (map (\x -> "#include <" <> fromString x <> ">\n") xs) <> "\n"
    
    genNs :: String -> [String] -> (String -> BS.ByteString) -> BS.ByteString
    genNs indent (ns:nss) cont = let indent' = fromString indent 
                                 in indent' <> "namespace " <> fromString ns <> "\n" <> indent' <> "{\n"
                                    <> genNs ('\t':indent) nss cont <> indent' <> "};\n"
    genNs indent []       cont = cont indent
    
    genClass :: String -> String -> [MethodDecl] -> [MethodDecl] -> BS.ByteString
    genClass indent nm  mthds staticMthds = let indent'  = fromString indent
                                                nm'      = fromString nm
                                                override = if cpp98 args then "" else " override"
                                            in indent' <> "class " <> nm' <> "I: public " <> nm' <> "\n" <> indent' <> "{\n" <> indent'<> "public:\n" 
                                               <> genMethodHeads override ('\t':indent) mthds  
                                               <> (if null staticMthds then "" 
                                                   else "\n" <> genMethodHeads "" ('\t':indent ++ "static ") staticMthds)
                                               <> indent' <> "};\n"
                                                                                                                                              
                                   
    genMethods indent nm mthds fwdMthds = let indent'  = fromString indent
                                              nm'      = fromString nm
                                              bodyStub = "\n" <> indent' <> "{\n" <> indent' <> "}\n\n" <> indent'
                                          in if null fwdMthds
                                             then indent' <> (BS.intercalate bodyStub (map (genMethodHead (nm' <> "I::")) mthds) 
                                                   <> "\n" <> indent' <> "{\n" <> indent' <> "}\n") 
                                             else BS.intercalate "\n" (map (genFwdMethod indent' nm) fwdMthds)
    
    genMethodHeads override indent mthds = fromString indent <> BS.intercalate (override <> ";\n" <> fromString indent) (map (genMethodHead "") mthds) <> override <> ";\n"
    
    genMethodHead :: BS.ByteString -> MethodDecl -> BS.ByteString
    genMethodHead scope (MethodDecl tp (Ident nm) flds _ _) = genType tp <> " " <> scope <> fromString nm <> "(" <> genFields flds <> (if null flds then "" else ", ") <> "const Ice::Current& current)"
    
    fs = fromString
    
    genFwdMethod indent scope mdecl@(MethodDecl tp (Ident nm) flds _ _) = 
      let scope' = fs $ replaceTail "Factory" "" scope
          vals   = map (\(FieldDecl _ (Ident fnm) _) -> fs fnm) flds ++ ["current"]
          ret    = if tp == STVoid then "" else "return "
      in indent <> genMethodHead (fs scope <> "I::") mdecl <> "\n" <> indent <> "{\n" <> indent <> "\t" <>
         ret <> scope' <> "I::" <> fs nm <> "(" <> BS.intercalate ", " vals <> ");\n" <> indent <> "}\n"
    
    genField (FieldDecl tp (Ident nm) _) = passRefOrVal tp <> " " <> fromString nm
    
    genFields flds = BS.intercalate ", " $ map genField flds
    
    genType :: SliceType -> BS.ByteString
    genType STVoid                                 = "void"
    genType STBool                                 = "bool"
    genType STByte                                 = "Ice::Byte"
    genType STShort                                = "Ice::Short"
    genType STInt                                  = "Ice::Int"
    genType STLong                                 = "Ice::Long"
    genType STFloat                                = "float"
    genType STDouble                               = "double"
    genType STString                               = "std::string"
    genType (STUserDefined (NsQualIdent nm ns))    = fromString (intercalate "::" (ns ++ [nm]))
    genType (STUserDefinedPrx (NsQualIdent nm ns)) = fromString (intercalate "::" (ns ++ [nm])) <> "Prx"
    
    passRefOrVal tp@(STString)        = "const " <> genType tp <> "&"
    passRefOrVal tp@(STUserDefined _) = "const " <> genType tp <> "&"
    passRefOrVal tp                   = genType tp
sliceCppGen fctyMthds args decl = go [] decl
  where
    go ns (ModuleDecl (Ident mName) decls)     = concatMap (go (ns++[mName])) decls
    go ns (InterfaceDecl (Ident nm) ext mthds) = [ (targetDir args </> nm ++ "I.h",   genH   ns nm mthds)
                                                 , (targetDir args </> nm ++ "I.cpp", genCpp ns nm mthds)]
    go ns _                                    = []
    
    genH :: [String] -> String -> [MethodDecl] -> BS.ByteString
    genH ns nm mthds = let staticMthds = fromMaybe [] $ Map.lookup (ns ++ [nm++"Factory"]) fctyMthds
                           hnames      = [replaceExtension (takeFileName $ icefile args) ".h"]
                           classCont   = \indent -> genClass indent nm mthds staticMthds
                           ctnt        =  genInclds hnames <> genNs "" ns classCont
                           guardItems  = if longguard args then ns ++ [nm] else [nm]
                       in genIfDef guardItems ctnt
    
    genCpp ns nm mthds = let fwdMthds    = fromMaybe [] $ Map.lookup (ns++[nm]) fctyMthds
                             staticMthds = fromMaybe [] $ Map.lookup (ns ++ [nm++"Factory"]) fctyMthds
                             mthdsCont   = \indent -> genMethods indent nm (mthds ++ staticMthds) fwdMthds
                             hnames      = (targetDir args </> nm ++ "I.h")
                                           : if null fwdMthds then [] else [targetDir args </> replaceTail "Factory" "" nm ++ "I.h"]
                         in genInclds hnames <> genNs "" ns mthdsCont
                            
    genIfDef tkns ctnt = let ident = fromString $ intercalate "_" $ map (map toUpper) (concatMap camelSplit tkns) ++ ["I","H"]
                         in ("#ifndef " <> ident <>"\n#define " <> ident <> "\n\n") <> ctnt <> "\n#endif"
                            
    genInclds xs = mconcat (map (\x -> "#include <" <> fromString x <> ">\n") xs) <> "\n"
    
    genNs :: String -> [String] -> (String -> BS.ByteString) -> BS.ByteString
    genNs indent (ns:nss) cont = let indent' = fromString indent 
                                 in indent' <> "namespace " <> fromString ns <> "\n" <> indent' <> "{\n"
                                    <> genNs ('\t':indent) nss cont <> indent' <> "};\n"
    genNs indent []       cont = cont indent
    
    genClass :: String -> String -> [MethodDecl] -> [MethodDecl] -> BS.ByteString
    genClass indent nm  mthds staticMthds = let indent'  = fromString indent
                                                nm'      = fromString nm
                                                override = if cpp98 args then "" else " override"
                                            in indent' <> "class " <> nm' <> "I: public " <> nm' <> "\n" <> indent' <> "{\n" <> indent'<> "public:\n" 
                                               <> genMethodHeads override ('\t':indent) mthds  
                                               <> (if null staticMthds then "" 
                                                   else "\n" <> genMethodHeads "" ('\t':indent ++ "static ") staticMthds)
                                               <> indent' <> "};\n"
                                                                                                                                              
                                   
    genMethods indent nm mthds fwdMthds = let indent'  = fromString indent
                                              nm'      = fromString nm
                                              bodyStub = "\n" <> indent' <> "{\n" <> indent' <> "}\n\n" <> indent'
                                          in if null fwdMthds
                                             then indent' <> (BS.intercalate bodyStub (map (genMethodHead (nm' <> "I::")) mthds) 
                                                   <> "\n" <> indent' <> "{\n" <> indent' <> "}\n") 
                                             else BS.intercalate "\n" (map (genFwdMethod indent' nm) fwdMthds)
    
    genMethodHeads override indent mthds = fromString indent <> BS.intercalate (override <> ";\n" <> fromString indent) (map (genMethodHead "") mthds) <> override <> ";\n"
    
    genMethodHead :: BS.ByteString -> MethodDecl -> BS.ByteString
    genMethodHead scope (MethodDecl tp (Ident nm) flds _ _) = genType tp <> " " <> scope <> fromString nm <> "(" <> genFields flds <> (if null flds then "" else ", ") <> "const Ice::Current& current)"
    
    fs = fromString
    
    genFwdMethod indent scope mdecl@(MethodDecl tp (Ident nm) flds _ _) = 
      let scope' = fs $ replaceTail "Factory" "" scope
          vals   = map (\(FieldDecl _ (Ident fnm) _) -> fs fnm) flds ++ ["current"]
          ret    = if tp == STVoid then "" else "return "
      in indent <> genMethodHead (fs scope <> "I::") mdecl <> "\n" <> indent <> "{\n" <> indent <> "\t" <>
         ret <> scope' <> "I::" <> fs nm <> "(" <> BS.intercalate ", " vals <> ");\n" <> indent <> "}\n"
    
    genField (FieldDecl tp (Ident nm) _) = passRefOrVal tp <> " " <> fromString nm
    
    genFields flds = BS.intercalate ", " $ map genField flds
    
    genType :: SliceType -> BS.ByteString
    genType STVoid                                 = "void"
    genType STBool                                 = "bool"
    genType STByte                                 = "Ice::Byte"
    genType STShort                                = "Ice::Short"
    genType STInt                                  = "Ice::Int"
    genType STLong                                 = "Ice::Long"
    genType STFloat                                = "float"
    genType STDouble                               = "double"
    genType STString                               = "std::string"
    genType (STUserDefined (NsQualIdent nm ns))    = fromString (intercalate "::" (ns ++ [nm]))
    genType (STUserDefinedPrx (NsQualIdent nm ns)) = fromString (intercalate "::" (ns ++ [nm])) <> "Prx"
    
    passRefOrVal tp@(STString)        = "const " <> genType tp <> "&"
    passRefOrVal tp@(STUserDefined _) = "const " <> genType tp <> "&"
    passRefOrVal tp                   = genType tp