module StateSizedIO.LIB.HDBC.HDBC where
open import Data.Bool.Base
open import Data.Integer
open import Data.Nat.Base hiding (_+_)
open import Data.String.Base renaming (_++_ to _++Str_)
open import Data.Product renaming (map to mapProduct)
open import Data.Integer
open import Data.List
open import Function
open import StateSizedIO.LIB.library
open import NativeIO renaming (NativeIO to IO; nativeReturn to return; _native>>=_ to _>>=_)
{-# FOREIGN GHC import qualified Database.HDBC.Sqlite3 #-}
{-# FOREIGN GHC import qualified Database.HDBC #-}
{-# FOREIGN GHC import qualified Control.Monad #-}
{-# FOREIGN GHC import qualified Database.HDBC.SqlValue #-}
{-# FOREIGN GHC import qualified Data.ByteString #-}
{-# FOREIGN GHC import qualified Data.Int #-}
{-# FOREIGN GHC import qualified Data.ByteString.Char8 #-}
{-# FOREIGN GHC import qualified Data.Text #-}
{-# FOREIGN GHC
  data SqlValueShort = SqlStringShort Data.Text.Text
                     | SqlIntShort Integer
                     | SqlNullShort
                     | SqlUndefinedShort
  toSqlValueShort :: Database.HDBC.SqlValue -> SqlValueShort
  toSqlValueShort (Database.HDBC.SqlByteString s) = SqlStringShort $ Data.Text.pack $ Data.ByteString.Char8.unpack  s
  toSqlValueShort (Database.HDBC.SqlInt64 x) = SqlIntShort $ fromIntegral x
  toSqlValueShort Database.HDBC.SqlNull = SqlNullShort
  toSqlValueShort _ = SqlUndefinedShort
  -- throws an exception (Non-exhaustive patterns) for the value SqlUndefinedShort!
  --
  toSqlValue :: SqlValueShort -> Database.HDBC.SqlValue
  toSqlValue (SqlStringShort str) =  Database.HDBC.SqlValue.SqlString (Data.Text.unpack str)
  toSqlValue (SqlIntShort x) =  Database.HDBC.SqlValue.SqlInt64 (fromIntegral x)
  toSqlValue SqlNullShort = Database.HDBC.SqlValue.SqlNull
  toSqlValue SqlUndefinedShort = error "ERROR!!! toSqlValue function with SqlUndefinedShort, can't convert it."
#-}
data SqlVal : Set where
  sqlString    : String → SqlVal
  sqlInteger   : ℤ → SqlVal
  sqlNull      : SqlVal
  sqlUndefined : SqlVal
{-# COMPILE GHC SqlVal = data SqlValueShort (SqlStringShort | SqlIntShort | SqlNullShort | SqlUndefinedShort) #-}
postulate SqlValue : Set
{-# COMPILE GHC SqlValue = type Database.HDBC.SqlValue.SqlValue #-}
postulate toSqlVal : SqlValue → SqlVal
{-# COMPILE GHC toSqlVal = toSqlValueShort #-}
postulate  toSqlValue : SqlVal → SqlValue
{-# COMPILE GHC toSqlValue = toSqlValue #-}
toStringSqlVal : SqlVal → String
toStringSqlVal (sqlString s)   = s
toStringSqlVal (sqlInteger x)  = Data.Integer.show x
toStringSqlVal sqlNull         = "SqlValNull"
toStringSqlVal sqlUndefined    = "SqlValUndefined"
toTypeNameSqlVal : SqlVal → String
toTypeNameSqlVal (sqlString _)   = "[String]"
toTypeNameSqlVal (sqlInteger _)  = "[Integer]"
toTypeNameSqlVal sqlNull         = "SqlValNull"
toTypeNameSqlVal sqlUndefined    = "SqlValUndefined"
toStringListSqlVal : List SqlVal → String
toStringListSqlVal (x ∷ xs) = toStringSqlVal x ++Str "\n" ++Str toStringListSqlVal xs
toStringListSqlVal [] = ""
postulate Connection : Set
{-# COMPILE GHC Connection = type Database.HDBC.Sqlite3.Connection #-}
postulate Statement : Set
{-# COMPILE GHC Statement = type Database.HDBC.Statement #-}
postulate PairStringSqlValue : Set
{-# COMPILE GHC PairStringSqlValue = type (String, Database.HDBC.SqlValue.SqlValue) #-}
postulate fstString : PairStringSqlValue → String
{-# COMPILE GHC fstString = (\(a , b) -> Data.Text.pack a) #-}
postulate sndSqlValue : PairStringSqlValue → SqlValue
{-# COMPILE GHC sndSqlValue = (\(a , b) -> b) #-}
convertPair : PairStringSqlValue → String × SqlValue
convertPair x = fstString x , sndSqlValue x
postulate nativeConnectDB : String → IO Connection
{-# COMPILE GHC nativeConnectDB = (\tableName -> Database.HDBC.Sqlite3.connectSqlite3 (Data.Text.unpack tableName)) #-}
postulate nativeDisconnect : Connection → IO Unit
{-# COMPILE GHC nativeDisconnect = Database.HDBC.disconnect #-}
postulate nativeCommit : Connection → IO Unit
{-# COMPILE GHC nativeCommit = Database.HDBC.commit #-}
postulate nativeRollback : Connection → IO Unit
{-# COMPILE GHC nativeRollback = Database.HDBC.rollback #-}
postulate nativeRunSqlValue : Connection → String → List SqlValue → IO ℤ
{-# COMPILE GHC nativeRunSqlValue = (\conn str values -> Database.HDBC.run conn (Data.Text.unpack str) values) #-}
nativeRunSqlValueAgda : Connection → String → List SqlVal → IO ℤ
nativeRunSqlValueAgda conn str values = nativeRunSqlValue conn str (map toSqlValue values)
postulate nativePrepare : Connection → String → IO Statement
{-# COMPILE GHC nativePrepare = (\conn str -> Database.HDBC.prepare conn (Data.Text.unpack str)) #-}
postulate nativeExecuteSqlValue : Statement → List SqlValue → IO ℤ
{-# COMPILE GHC nativeExecuteSqlValue = Database.HDBC.execute #-}
nativeExecute : Statement → List SqlVal → IO ℤ
nativeExecute statement list = nativeExecuteSqlValue statement (map toSqlValue list)
postulate runSqlValue : Connection → String → List SqlValue → IO ℤ
{-# COMPILE GHC runSqlValue = (\conn str list -> Database.HDBC.run conn (Data.Text.unpack str) list) #-}
nativeRun : Connection → String → List SqlVal → IO ℤ
nativeRun conn str list = runSqlValue conn str (map toSqlValue list)
postulate getTables : Connection → IO (List String)
{-# COMPILE GHC getTables = (\conn -> (Database.HDBC.getTables conn) >>= (\x -> return $ map Data.Text.pack x) ) #-}
postulate fetchAllRowsALStrictSqlValue : Statement → IO (List (List PairStringSqlValue))
{-# COMPILE GHC fetchAllRowsALStrictSqlValue = (\statement -> (Database.HDBC.fetchAllRowsAL' statement)) #-}
nativeFetchAllRowsAL' : Statement → IO (List (List (String × SqlVal)))
nativeFetchAllRowsAL' statement = fetchAllRowsALStrictSqlValue statement >>= λ list →
                            return (map (map (λ y → mapProduct (λ x → x) toSqlVal (convertPair y))) list)
nativePrintList : List (List (String × SqlVal)) → String
nativePrintList l = unlines (map concat' listListString)
  where
    concat' = foldr (λ x y → x ++Str ", " ++Str y) ""
    toStr : String × SqlVal → String
    toStr (str , val) = "(" ++Str str ++Str ", " ++Str toStringSqlVal val ++Str " " ++Str toTypeNameSqlVal val ++Str ")"
    listListString = map (map toStr) l
dropTableIfExisits : Connection → (tableName : String) →  IO Unit
dropTableIfExisits conn tableName = nativeRun conn ("DROP TABLE IF EXISTS " ++Str tableName) [] >>= λ _ → return _