
module Turbinado.Database.ORM.Output where

import qualified Data.Char
import Control.Monad
import Data.Dynamic
import qualified Data.Map as M
import Data.Maybe
import Data.List
import Database.HDBC
import System.Directory
import System.FilePath

import Turbinado.Database.ORM.Types

type TypeName = String

-- TODO: This file needs to be completely torn up and generalized.

writeModels ts = 
  do writeFile "App/Models/Bases/Common.hs" generateCommon
     mapM_ (\(t, (cs, pk)) -> 
                 let typeName = (toType t) in
                 do e <- doesFileExist (joinPath ["App/Models", typeName ++ ".hs"])
                    when (not e) (writeFile (joinPath ["App/Models", typeName++".hs"]) (generateModelFile typeName) ) 
                    writeFile (joinPath ["App/Models/Bases", typeName ++ "Type.hs"]) (generateType t typeName pk ts cs)
                    writeFile (joinPath ["App/Models/Bases", typeName ++ "Functions.hs"]) (generateFunctions t typeName pk ts cs)
                    writeFile (joinPath ["App/Models/Bases", typeName ++ "Relations.hs"]) (generateRelations t typeName pk ts cs)
           ) $ M.toList ts

---------------------------------------------------------------------------
--  File templates                                                       --
---------------------------------------------------------------------------

generateType ::   TableName ->
                  TypeName ->
                  PrimaryKey -> 
                  Tables -> 
                  Columns -> 
                  String
generateType t typeName pk ts cs = 
  unlines $
  ["{- DO NOT EDIT THIS FILE"
  ,"   THIS FILE IS AUTOMAGICALLY GENERATED AND YOUR CHANGES WILL BE EATEN BY THE GENERATOR OVERLORD"
  ,""
  ,"   All changes should go into the Model file (e.g. App/Models/ExampleModel.hs)"
  ,"-}"
  ,""
  ,"module App.Models.Bases." ++ typeName ++ "Type where"
  , ""
  , "import App.Models.Bases.Common"
  , "import Data.Maybe"
  , "import Data.Time"
  , "import Data.Typeable"
  , ""
  ] ++
  ["-- The data type for this model"] ++
  [ "data " ++ typeName ++ " = " ++ typeName ++ " {"
  ] ++
  [intercalate ",\n" (map columnToFieldLabel (M.toList cs))] ++
  [ "    } deriving (Show, Typeable)"
  , ""
  , "instance DatabaseModel " ++ typeName ++ " where"
  , "    tableName _ = \"" ++ t ++ "\""
  , ""
  ]

generateFunctions ::  TableName ->
                      TypeName ->
                      PrimaryKey -> 
                      Tables -> 
                      Columns -> 
                      String
generateFunctions t typeName pk ts cs = 
  unlines $
  ["{- DO NOT EDIT THIS FILE"
  ,"   THIS FILE IS AUTOMAGICALLY GENERATED AND YOUR CHANGES WILL BE EATEN BY THE GENERATOR OVERLORD"
  ,""
  ,"   All changes should go into the Model file (e.g. App/Models/ExampleModel.hs)"
  ,"-}"
  ,""
  ,"module App.Models.Bases." ++ typeName ++ "Functions where"
  , ""
  , "import App.Models.Bases.Common"
  , "import qualified Database.HDBC as HDBC"
  , "import Data.Maybe"
  , "import Data.Time"
  , ""
  , " -- My type"
  , "import App.Models.Bases." ++ typeName ++ "Type"
  , ""
  , "import Turbinado.Environment.Types"
  , "import Turbinado.Environment.Database"
  , ""
  ] ++
  [""] ++
  generateHasFindByPrimaryKey t cs typeName pk ++
  [""] ++
  generateIsModel t cs typeName
  ++
  [""
  ,"deleteWhere :: (HasEnvironment m) => SelectString -> SelectParams -> m Integer"
  ,"deleteWhere ss sp = do"
  ,"        conn <- getEnvironment >>= (return . fromJust . getDatabase )"
  ,"        res <- liftIO $ HDBC.handleSqlError $ HDBC.run conn (\"DELETE FROM " ++ t++ " WHERE (\" ++ ss ++ \") \")  sp"
  ,"        return res"
  ]
generateRelations ::  TableName ->
                      TypeName ->
                      PrimaryKey -> 
                      Tables -> 
                      Columns -> 
                      String
generateRelations t typeName pk ts cs = 
  unlines $
  ["{- DO NOT EDIT THIS FILE"
  ,"   THIS FILE IS AUTOMAGICALLY GENERATED AND YOUR CHANGES WILL BE EATEN BY THE GENERATOR OVERLORD"
  ,""
  ,"   All changes should go into the Model file (e.g. App/Models/ExampleModel.hs)"
  ,"-}"
  ,""
  ,"module App.Models.Bases." ++ typeName ++ "Relations where"
  , ""
  , "import App.Models.Bases.Common"
  , "import qualified Database.HDBC as HDBC"
  , "import Data.Maybe"
  , "import Data.Time"
  , ""
  , " -- Model imports"
  , "import App.Models.Bases." ++ typeName ++ "Type"
  , unlines $ generateChildModelImports cs 
  , unlines $ generateParentModelImports t ts
  , ""
  , "import Turbinado.Environment.Types"
  , "import Turbinado.Environment.Database"
  , ""
  ] ++
  [""] ++
  [""] ++
  generateHasChildren t cs typeName ++
  [""] ++
  [""] ++
  generateHasParents t ts

generateChildModelImports cs = 
    map (\ctn -> "import qualified App.Models.Bases." ++ toType ctn ++ "Type as " ++ toType ctn ++ "Type\nimport qualified App.Models.Bases." ++ toType ctn ++ "Functions as " ++ toType ctn ++ "Functions") $ 
      nub $
        map fst $ concat $
          map (\(_, fks, _) -> fks) $ M.elems cs

generateParentModelImports t ts = 
    map (\ptn -> "import qualified App.Models.Bases." ++ toType ptn ++ "Type as " ++ toType ptn ++ "Type\nimport qualified App.Models.Bases." ++ toType ptn ++ "Functions as " ++ toType ptn ++ "Functions") $ 
      nub $ filter (not . null) $ 
        map parentFilter $ M.assocs ts
    where parentFilter (ptn, (cs, _)) = 
             case (filter (\(tn, _) -> t == tn) $ concat $ map (\(_, fks, _) -> fks) $ M.elems cs) of
               [] -> []
               _  -> ptn

generateModelFile typeName =
  unlines $
  ["module App.Models." ++ typeName
  ,"  ( module App.Models." ++ typeName
  ,"  , module App.Models.Bases." ++ typeName ++ "Type"
  ,"  , module App.Models.Bases." ++ typeName ++ "Functions"
  ,"  , module App.Models.Bases." ++ typeName ++ "Relations"
  ,"  , module App.Models.Bases.Common"
  ,"  ) where"
  ,"import App.Models.Bases." ++ typeName ++ "Type"
  ,"import App.Models.Bases." ++ typeName ++ "Functions"
  ,"import App.Models.Bases." ++ typeName ++ "Relations"
  ,"import App.Models.Bases.Common"
  ]

generateCommon:: String
generateCommon = unlines $
  ["{- DO NOT EDIT THIS FILE"
  ,"   THIS FILE IS AUTOMAGICALLY GENERATED AND YOUR CHANGES WILL BE EATEN BY THE GENERATOR OVERLORD -}"
  ,""
  ,"module App.Models.Bases.Common("
  ,"  module App.Models.Bases.Common,"
  ,"  module Control.OldException,"
  ,"  module Control.Monad.Trans,"
  ,"  module Data.Int"
  ,"  ) where"
  ,""
  ,"import Control.Monad.Trans"
  ,"import Control.OldException"
  ,"import Database.HDBC"
  ,"import Data.Int"
  ,""
  ,"import Turbinado.Environment.Types"
  ,""
  ,"-- Using phantom types here "
  ,"class DatabaseModel m where"
  ,"  tableName :: m -> String"
  ,""
  ,"type SelectString = String"
  ,"type SelectParams = [SqlValue]"
  ,"type OrderByParams  = String"
  ,""
  ,"class (DatabaseModel model) =>"
  ,"        IsModel model where"
  ,"        insert    :: (HasEnvironment m) => model -> Bool -> m (Maybe Integer)"
  ,"        findAll   :: (HasEnvironment m) => m [model]"
  ,"        findAllWhere :: (HasEnvironment m) => SelectString -> SelectParams -> m [model]"
  ,"        findAllOrderBy :: (HasEnvironment m) => OrderByParams -> m [model]"
  ,"        findAllWhereOrderBy :: (HasEnvironment m) => SelectString -> SelectParams -> OrderByParams -> m [model]"
  ,"        findOneWhere :: (HasEnvironment m) => SelectString -> SelectParams -> m model"
  ,"        findOneOrderBy :: (HasEnvironment m) => OrderByParams -> m model"
  ,"        findOneWhereOrderBy :: (HasEnvironment m) => SelectString -> SelectParams -> OrderByParams -> m model"
  ,""
  ,"class (DatabaseModel model) =>"
  ,"        HasFindByPrimaryKey model primaryKey | model -> primaryKey where"
  ,"    find   :: (HasEnvironment m) => primaryKey -> m model"
  ,"    delete :: (HasEnvironment m) => primaryKey -> m ()"
  ,"    update :: (HasEnvironment m) => model      -> m ()"
  ,""
  ]

---------------------------------------------------------------------------
--  Generator templates                                                  --
---------------------------------------------------------------------------

generateIsModel :: TableName -> Columns -> TypeName -> [String]
generateIsModel t cs typeName =
    ["instance IsModel " ++ typeName ++ " where"
    ,"    insert m returnId = do"
    ,"        conn <- getEnvironment >>= (return . fromJust . getDatabase )"
    ,"        res  <- liftIO $ HDBC.handleSqlError $ HDBC.run conn (\" INSERT INTO " ++ t ++ " (" ++ (intercalate "," $ M.keys cs) ++") VALUES (" ++ (intercalate "," $ map generateQs (M.assocs cs) ) ++ ")\")  ( " ++ (intercalate " ++ " $ filter (not . null) $ map generateArgs (M.assocs cs) ) ++ ")"
    ,"        case res of"
    ,"          0 -> (liftIO $ HDBC.handleSqlError $ HDBC.rollback conn) >>"
    ,"               (throwDyn $ HDBC.SqlError"
    ,"                           {HDBC.seState = \"\","
    ,"                            HDBC.seNativeError = (-1),"
    ,"                            HDBC.seErrorMsg = \"Rolling back.  No record inserted :" ++ t ++ " : \" ++ (show m)"
    ,"                           })"
    ,"          1 -> liftIO $ HDBC.handleSqlError $ HDBC.commit conn >>"
    ,"               if returnId"
    ,"                 then do i <- liftIO $ HDBC.catchSql (HDBC.handleSqlError $ HDBC.quickQuery' conn \"SELECT lastval()\" []) (\\_ -> HDBC.commit conn >> (return $ [[HDBC.toSql (0 :: Integer)]]) ) "
    ,"                         return $ HDBC.fromSql $ head $ head i"
    ,"               else return Nothing"
    ,"    findAll = do"
    ,"        conn <- getEnvironment >>= (return . fromJust . getDatabase )"
    ,"        res <- liftIO $ HDBC.handleSqlError $ HDBC.quickQuery' conn \"SELECT " ++ cols cs ++ " FROM " ++ t ++ "\" []"
    ,"        return $ map (\\r -> " ++ generateConstructor cs typeName ++ ") res"
    ,"    findAllWhere ss sp = do"
    ,"        conn <- getEnvironment >>= (return . fromJust . getDatabase )"
    ,"        res <- liftIO $ HDBC.handleSqlError $ HDBC.quickQuery' conn (\"SELECT " ++ cols cs ++ " FROM " ++ t++ " WHERE (\" ++ ss ++ \") \")  sp"
    ,"        return $ map (\\r -> " ++ generateConstructor cs typeName ++ ") res"
    ,"    findAllOrderBy op = do"
    ,"        conn <- getEnvironment >>= (return . fromJust . getDatabase )"
    ,"        res <- liftIO $ HDBC.handleSqlError $ HDBC.quickQuery' conn (\"SELECT " ++ cols cs ++ " FROM " ++ t++ " ORDER BY \" ++ op) []"
    ,"        return $ map (\\r -> " ++ generateConstructor cs typeName ++ ") res"
    ,"    findAllWhereOrderBy ss sp op = do"
    ,"        conn <- getEnvironment >>= (return . fromJust . getDatabase )"
    ,"        res <- liftIO $ HDBC.handleSqlError $ HDBC.quickQuery' conn (\"SELECT " ++ cols cs ++ " FROM " ++ t++ " WHERE (\" ++ ss ++ \") ORDER BY \" ++ op) sp"
    ,"        return $ map (\\r -> " ++ generateConstructor cs typeName ++ ") res"
    ,"    findOneWhere ss sp = do"
    ,"        conn <- getEnvironment >>= (return . fromJust . getDatabase )"
    ,"        res <- liftIO $ HDBC.handleSqlError $ HDBC.quickQuery' conn (\"SELECT " ++ cols cs ++ " FROM " ++ t++ " WHERE (\" ++ ss ++ \") LIMIT 1\") sp"
    ,"        return $ (\\r -> " ++ generateConstructor cs typeName ++ ") (head res)"
    ,"    findOneOrderBy op = do"
    ,"        conn <- getEnvironment >>= (return . fromJust . getDatabase )"
    ,"        res <- liftIO $ HDBC.handleSqlError $ HDBC.quickQuery' conn (\"SELECT " ++ cols cs ++ " FROM " ++ t++ " ORDER BY \" ++ op ++ \" LIMIT 1\")  []"
    ,"        return $ (\\r -> " ++ generateConstructor cs typeName ++ ") (head res)"
    ,"    findOneWhereOrderBy ss sp op = do"
    ,"        conn <- getEnvironment >>= (return . fromJust . getDatabase )"
    ,"        res <- liftIO $ HDBC.handleSqlError $ HDBC.quickQuery' conn (\"SELECT " ++ cols cs ++ " FROM " ++ t++ " WHERE (\" ++ ss ++ \") ORDER BY \" ++ op ++\" LIMIT 1\")  sp"
    ,"        return $ (\\r -> " ++ generateConstructor cs typeName ++ ") (head res)"
     ]
       where generateQs   :: (String, (SqlColDesc, ForeignKeyReferences, HasDefault)) -> String
             generateQs (c, (desc, _, False)) = if ((colNullable desc) == Just True) then ("\" ++ (case (" ++ toFunction c ++ " m) of Nothing -> \"DEFAULT\"; Just x -> \"?\") ++ \"") else "?"
             generateQs (c, (_, _, True)) = "\" ++ (case (" ++ toFunction c ++ " m) of Nothing -> \"DEFAULT\"; Just x -> \"?\") ++ \"" 
             generateArgs :: (String, (SqlColDesc, ForeignKeyReferences, HasDefault)) -> String
             generateArgs (c, (desc, _, False)) = if ((colNullable desc) == Just True) then ("(case (" ++ toFunction c ++ " m) of Nothing -> []; Just x -> [HDBC.toSql x])") else ("[HDBC.toSql $ " ++ toFunction c ++ " m]")
             generateArgs (c, (_, _, True)) = "(case (" ++ toFunction c ++ " m) of Nothing -> []; Just x -> [HDBC.toSql x])" 

generateHasFindByPrimaryKey :: TableName -> Columns -> TypeName -> PrimaryKey -> [String]
generateHasFindByPrimaryKey t cs typeName pk =
  case (length  pk) of
    0 -> [""]
    _ -> ["instance HasFindByPrimaryKey " ++ typeName ++ " " ++ " (" ++ unwords (intersperse "," (map (\c -> getHaskellTypeString $ colType $ (\(c',_,_) -> c') $ fromJust $ M.lookup c cs) pk)) ++ ") " ++ " where"
         ,"    find pk@(" ++ (concat $ intersperse ", " $ map (\i -> "pk"++(show i)) [1..(length pk)]) ++ ") = do"
         ,"        conn <- getEnvironment >>= (return . fromJust . getDatabase )"
         ,"        res <- liftIO $ HDBC.handleSqlError $ HDBC.quickQuery' conn (\"SELECT " ++ cols cs ++ " FROM " ++ t ++ " WHERE (" ++ (generatePrimaryKeyWhere pk) ++ ")\") [" ++ (unwords $ intersperse "," $ map (\(c,i) -> "HDBC.toSql pk" ++ (show i)) (zip pk [1..])) ++ "]"
         ,"        case res of"
         ,"          [] -> throwDyn $ HDBC.SqlError"
         ,"                           {HDBC.seState = \"\","
         ,"                            HDBC.seNativeError = (-1),"
         ,"                            HDBC.seErrorMsg = \"No record found when finding by Primary Key:" ++ t ++ " : \" ++ (show pk)"
         ,"                           }"
         ,"          r:[] -> return $ " ++ (generateConstructor cs typeName)
         ,"          _ -> throwDyn $ HDBC.SqlError"
         ,"                           {HDBC.seState = \"\","
         ,"                            HDBC.seNativeError = (-1),"
         ,"                            HDBC.seErrorMsg = \"Too many records found when finding by Primary Key:" ++ t ++ " : \" ++ (show pk)"
         ,"                           }"
         ,""
         ,"    delete pk@(" ++ (concat $ intersperse ", " $ map (\i -> "pk"++(show i)) [1..(length pk)]) ++ ") = do"
         ,"        conn <- getEnvironment >>= (return . fromJust . getDatabase )"
         ,"        res <- liftIO $ HDBC.handleSqlError $ HDBC.run conn (\"DELETE FROM " ++ t ++ " WHERE (" ++ (generatePrimaryKeyWhere pk) ++ ")\") [" ++ (unwords $ intersperse "," $ map (\(c,i) -> "HDBC.toSql pk" ++ (show i)) (zip pk [1..])) ++ "]"
         ,"        case res of"
         ,"          0 -> (liftIO $ HDBC.handleSqlError $ HDBC.rollback conn) >>"
         ,"               (throwDyn $ HDBC.SqlError"
         ,"                           {HDBC.seState = \"\","
         ,"                            HDBC.seNativeError = (-1),"
         ,"                            HDBC.seErrorMsg = \"Rolling back.  No record found when deleting by Primary Key:" ++ t ++ " : \" ++ (show pk)"
         ,"                           })"
         ,"          1 -> (liftIO $ HDBC.handleSqlError $ HDBC.commit conn) >> return ()"
         ,"          _ -> (liftIO $ HDBC.handleSqlError $ HDBC.rollback conn) >>"
         ,"               (throwDyn $ HDBC.SqlError"
         ,"                           {HDBC.seState = \"\","
         ,"                            HDBC.seNativeError = (-1),"
         ,"                            HDBC.seErrorMsg = \"Rolling back.  Too many records deleted when deleting by Primary Key:" ++ t ++ " : \" ++ (show pk)"
         ,"                           })"
         ,""
         ,"    update m = do"
         ,"        conn <- getEnvironment >>= (return . fromJust . getDatabase )"
         ,"        res <- liftIO $ HDBC.handleSqlError $ HDBC.run conn \"UPDATE " ++ t ++ " SET (" ++ (unwords $ intersperse "," $ M.keys cs) ++ ") = (" ++ (intercalate "," $ (take (M.size cs) (repeat "?"))) ++ ") WHERE (" ++ (generatePrimaryKeyWhere pk)  ++")\""
         ,"                  [" ++ (unwords $ intersperse "," $ map (\c -> "HDBC.toSql $ " ++ toFunction c ++ " m") (M.keys cs) ) ++ ", " ++ (unwords $ intersperse "," $ map (\c -> "HDBC.toSql $ " ++ toFunction c ++ " m") pk ) ++ "]"
         ,"        liftIO $ HDBC.handleSqlError $ HDBC.commit conn"
         ,"        return ()"
         ]

generateHasChildren :: TableName -> Columns -> TypeName -> [String]
generateHasChildren t cs typeName = map (\(cn, cd) -> generateHasChildren_t t cn cd typeName) $ M.assocs cs

generateHasChildren_t :: TableName -> ColumnName -> ColumnDesc -> TypeName -> String
generateHasChildren_t t cn (_, fks, _) typeName = unlines $ map (\(fkt, fkc) -> generateHasChildren_t_k t cn fkt fkc typeName) fks

generateHasChildren_t_k :: TableName -> ColumnName -> TableName -> ColumnName -> TypeName -> String
generateHasChildren_t_k t cn fkt fkc typeName = 
  unlines $
    ["findAllChild" ++ toType fkt  ++ " :: (HasEnvironment m) => " ++ toType t ++ " -> m [" ++ toType fkt ++ "Type." ++ toType fkt ++ "]"
    ,"findAllChild" ++ toType fkt ++ " p = findAllWhere \"" ++ fkc ++ " = ?\" [HDBC.toSql $ " ++ toFunction cn ++ " p]"
    ]


generateHasParents :: TableName -> Tables -> [String]
generateHasParents ctn ts =
    map (\(tname, cname, ptname, pcname) -> generateHasParent_t tname cname ptname pcname) $ 
      nub $ concat $ 
        map parentFilter $ M.assocs ts
    where parentFilter (tn, (cs', _)) = filter (\(_, _, tn', _) -> ctn == tn') $ concat $ map (\(cn, (_, fks, _)) -> map (\(ptn, pcn) -> (tn, cn, ptn, pcn)) fks) $ M.assocs cs'



generateHasParent_t :: TableName -> ColumnName -> TableName -> ColumnName -> String
generateHasParent_t ptn pcn ctn ccn = 
  unlines $
    ["parent" ++ toType ptn ++ " :: (HasEnvironment m) => " ++ toType ctn ++ " -> m " ++ toType ptn ++ "Type." ++ toType ptn
    ,"parent" ++ toType ptn ++ " self = findOneWhere \"" ++ pcn ++ " = ?\" [HDBC.toSql $ " ++ toFunction ccn ++ " self]"
    ]


{-----------------------------------------------------------------------}


generatePrimaryKeyWhere pk = 
  unwords $
    intersperse "++ \" AND \" ++ \"" $
      map (\(c,i) -> c ++ " = ? ") (zip pk [1..])

generateConstructor cs typeName =
  typeName ++ " " ++ (unwords $
  map (\i -> "(HDBC.fromSql (r !! " ++ (show i) ++ "))") [0..((M.size cs) - 1)])

 
---------------------------------------------------------------------------
--  Utility functions                                                    --
---------------------------------------------------------------------------
cols :: Columns -> String
cols cs = unwords $  intersperse "," $ M.keys cs

columnToFieldLabel :: (String, (SqlColDesc, ForeignKeyReferences, HasDefault)) -> String
columnToFieldLabel cd@(name, (desc, _, _)) =
  "    " ++ toFunction name  ++ " :: " ++ 
  maybeColumnLabel cd ++
  getHaskellTypeString (colType desc)

maybeColumnLabel :: (String, (SqlColDesc, ForeignKeyReferences, HasDefault)) -> String
maybeColumnLabel (_, (_, _, True)) = "Maybe "  -- Does the column have a default
maybeColumnLabel (_, (desc, _, _)) = if ((colNullable desc) == Just True) then "Maybe " else ""

-- Derived from hdbc-postgresql/Database/PostgreSQL/Statement.hs and hdbc/Database/HDBC/SqlValue.hs
getHaskellTypeString :: SqlTypeId -> String
getHaskellTypeString    SqlCharT = "String"
getHaskellTypeString    SqlVarCharT = "String"
getHaskellTypeString    SqlLongVarCharT = "String"
getHaskellTypeString    SqlWCharT = "String"
getHaskellTypeString    SqlWVarCharT = "String"
getHaskellTypeString    SqlWLongVarCharT = "String"
getHaskellTypeString    SqlDecimalT = "Rational"
getHaskellTypeString    SqlNumericT = "Rational"
getHaskellTypeString    SqlTinyIntT = "Int32"
getHaskellTypeString    SqlSmallIntT ="Int32"
getHaskellTypeString    SqlIntegerT = "Int32"
getHaskellTypeString    SqlBigIntT = "Integer"
getHaskellTypeString    SqlRealT = "Double"
getHaskellTypeString    SqlFloatT = "Double"
getHaskellTypeString    SqlDoubleT = "Double"
getHaskellTypeString    SqlBitT = "Bool"
getHaskellTypeString    SqlDateT = "Day"
getHaskellTypeString    SqlTimestampWithZoneT = "ZonedTime"
getHaskellTypeString    SqlTimestampT = "UTCTime"
getHaskellTypeString    SqlUTCDateTimeT = "UTCTime"
getHaskellTypeString    SqlTimeT = "TimeOfDay"
getHaskellTypeString    SqlUTCTimeT = "TimeOfDay"
getHaskellTypeString    SqlTimeWithZoneT = error "Turbinado ORM Generator: SqlTimeWithZoneT is not supported"
getHaskellTypeString    SqlBinaryT = "B.ByteString"
getHaskellTypeString    SqlVarBinaryT = "B.ByteString"
getHaskellTypeString    SqlLongVarBinaryT = "B.ByteString"
getHaskellTypeString    t = error "Turbinado ORM Generator: Don't know how to translate this SqlTypeId (" ++ show t ++ " to a Haskell Type"


-- | Used for safety.  Lowercases the first letter to 
-- make a valid function.
toFunction [] = error "toFunction passed an empty string"
toFunction (firstLetter:letters) = (Data.Char.toLower firstLetter) : letters


-- | Used for safety.  Uppercases the first letter to 
-- make a valid type.
toType [] = error "toType passed an empty string"
toType (firstLetter:letters) = (Data.Char.toUpper firstLetter) : letters
