module Eventloop.Utility.Websockets
    ( module Eventloop.Utility.Websockets
    , S.Socket
    , Connection
    ) where

import qualified Network.Socket as S
import qualified Data.Text as T
import Network.WebSockets hiding (Message)
import Control.Concurrent.MVar
import Control.Concurrent
import Control.Exception
import Data.ByteString.Lazy

type Host = [Char]
type Port = Int
type Message = [Char]
type ReceiveBuffer = MVar Message

                                    
createBindListenServerSocket :: Host -> Port -> IO S.Socket
createBindListenServerSocket host port = do
                                        host' <- S.inet_addr host
                                        socket <- S.socket S.AF_INET S.Stream S.defaultProtocol
                                        --Prelude.putStrLn "created socket"
                                        S.setSocketOption socket S.ReuseAddr 1
                                        --Prelude.putStrLn "set option"
                                        S.bindSocket socket (S.SockAddrInet (fromIntegral port) host')
                                        --Prelude.putStrLn "binded it"
                                        --threadDelay 500000
                                        S.listen socket 5
                                        --threadDelay 500000
                                        --Prelude.putStrLn "listening"
                                        return socket

                                        
acceptFirstConnection :: S.Socket -> IO Connection
acceptFirstConnection serverSocket = do
                                        --Prelude.putStrLn "before accept"
                                        (clientSocket, clientAddr) <- S.accept serverSocket
                                        --Prelude.putStrLn "accepted it!"
                                        pendingConnection <- makePendingConnection clientSocket defaultConnectionOptions
                                        --Prelude.putStrLn "made pending connection"
                                        connection <- acceptRequest pendingConnection
                                        --Prelude.putStrLn "accepted connection"
                                        return connection

                                        
setupWebsocketConnection :: Host -> Port -> IO (ReceiveBuffer, Connection, S.Socket)
setupWebsocketConnection host port = S.withSocketsDo $ do
                                                        serverSocket <- createBindListenServerSocket host port
                                                        clientConnection <- acceptFirstConnection serverSocket
                                                        recvBuffer <- newEmptyMVar
                                                        spawnReader recvBuffer clientConnection
                                                        return (recvBuffer, clientConnection, serverSocket)
                                        
                                        
spawnReader :: ReceiveBuffer -> Connection -> IO ThreadId
spawnReader recvBuffer conn = do
                                forkIO (handle handleCloseRequestException $ readIntoBuffer recvBuffer conn)
                                
                                
readIntoBuffer :: ReceiveBuffer -> Connection -> IO ()
readIntoBuffer recvBuffer conn =  do
                                    textMessage <- receiveData conn
                                    let
                                        message = T.unpack textMessage
                                    putMVar recvBuffer message
                                    readIntoBuffer recvBuffer conn
                            
handleCloseRequestException :: ConnectionException -> IO ()
handleCloseRequestException (CloseRequest i reason) | i == 1000 = return ()
                                                    | otherwise = Prelude.putStrLn ("Connection was closed but reason unknown: " ++ show i ++ show reason)
handleCloseRequestException (ConnectionClosed)    = Prelude.putStrLn ("Connection was closed unexpectedly")
handleCloseRequestException (ParseException text) = Prelude.putStrLn ("Parse exception on message: " ++ text)
                            
                            
hasMessage :: ReceiveBuffer -> IO Bool
hasMessage recvBuffer = do
                            buffer <- tryReadMVar recvBuffer
                            return (buffer /= Nothing)
                            
                            
takeMessage :: ReceiveBuffer -> IO Message
takeMessage recvBuffer = do
                            message <- takeMVar recvBuffer
                            return message
                                
                                
writeMessage :: Connection -> Message -> IO ()
writeMessage conn message = sendTextData conn (T.pack message)
                            

writeBinaryMessage :: Connection -> ByteString -> IO ()
writeBinaryMessage conn message = sendBinaryData conn message


closeWebsocketConnection :: S.Socket -> Connection -> IO ()
closeWebsocketConnection serverSocket clientConnection = do
                                                            S.sClose serverSocket
                                                            sendClose clientConnection (T.pack "Shutting down..")