import Control.Monad
import Control.Concurrent
import Data.Serialize
import Network.Socket hiding (recv)
import Network.Socket.ByteString
import Network.OpenFlow
import Network.Info
import System.Environment
import Data.Bits
import GHC.Word

main :: IO ()
main = do
  addrinfos <- getAddrInfo
               (Just (defaultHints {addrFlags = [AI_PASSIVE]}))
               Nothing (Just "6633")
  let serveaddr = head addrinfos
  sock <- socket (addrFamily serveaddr) Stream defaultProtocol
  setSocketOption sock ReuseAddr 1
  bindSocket sock (addrAddress serveaddr)
  listen sock 4
  serveConns sock
  
serveConns :: Socket -> IO ()
serveConns sock =
  forever $ do
    (csock, caddr) <- accept sock
    handshake csock
    flood csock
    _ <- forkIO $ handleSwitch csock caddr
    return ()

handshake :: Socket -> IO ()
handshake sock = do
  sendAll sock $ encode (OfpFrame (OfpHeader 1 0 0 0) (OfptHello []))
  frame <- readOfpFrame sock
  putStrLn $ "RX: " ++ show frame
  sendAll sock $ encode (OfpFrame (OfpHeader 1 0 0 1) (OfptGetConfigRequest))
  frame <- readOfpFrame sock
  putStrLn $ "RX: " ++ show frame  
  sendAll sock $ encode (OfpFrame (OfpHeader 1 0 0 2) (OfptFeaturesRequest))
  frame <- readOfpFrame sock
  putStrLn $ "RX: " ++ show frame  

handleSwitch :: Socket -> SockAddr -> IO ()  
handleSwitch sock caddr =
  forever $ do
    frame <- readOfpFrame sock
    putStrLn $ "RX: " ++ show frame
    case frame of
      (OfpFrame (OfpHeader _ _ _ xid) (OfptEchoRequest dat)) -> do
        let resp = (OfpFrame (OfpHeader 1 0 0 xid) (OfptEchoReply dat))
        sendAll sock $ encode resp
        putStrLn $ "TX: " ++ show resp
      (OfpFrame (OfpHeader _ _ _ xid) (OfptPacketIn (OfpPacketIn bid len inp reason dat))) -> do
        let resp = (OfpFrame (OfpHeader 1 0 0 xid) (OfptPacketOut (OfpPacketOut bid inp [(OfpOutput 6 0)] dat)))
        sendAll sock $ encode resp
        putStrLn $ "TX: " ++ show resp
      _ -> putStrLn " UNHANDLED"


inp  = 1
outp = 6
ofppController = 0xfffd
ofppNone = 0xffff

pri = 1
uint32max = 0xffffffff
emptyMac = MAC 0x00 0x00 0x00 0x00 0x00 0x00
dlVlan' = 10

wc = [
  OfpfwInPort,
  OfpfwDlVlan,
  OfpfwDlSrc,
  OfpfwDlDst,
  OfpfwDlType,
  OfpfwNwProto,
  OfpfwTpSrc,
  OfpfwTpDst,
  OfpfwNwSrc,
  OfpfwNwDst,
  OfpfwDlVlanPcp,
  OfpfwNwTos
  ]


incrMac :: MAC -> Word64 -> MAC
incrMac (MAC a b c d e f) v =
  MAC (fromIntegral $ (m `shiftR` 40) .&. 0xFF)
      (fromIntegral $ (m `shiftR` 32) .&. 0xFF)
      (fromIntegral $ (m `shiftR` 24) .&. 0xFF)
      (fromIntegral $ (m `shiftR` 16) .&. 0xFF)
      (fromIntegral $ (m `shiftR`  8) .&. 0xFF)
      (fromIntegral $ m .&. 0xFF)
  where
    m = v + fromIntegral ((a `shiftL` 40) .|.
                          (b `shiftL` 32) .|.
                          (c `shiftL` 24) .|.
                          (d `shiftL` 16) .|.
                          (e `shiftL`  8) .|. f)

flood :: Socket -> IO()
flood sock = flood' 4 0
  where
    flood' 0 xid = putStrLn $ "done! xid=" ++ show xid
    flood' n xid = do
      let m  = OfpMatch wc inp emptyMac (incrMac emptyMac n) dlVlan' 0 0 0 0 0 0 0 0
      let fm = OfpFlowMod m 0 OfpfcAdd 65535 65535 pri uint32max outp OfpffSendFlowRem [(OfpOutput outp 0)]
      sendAll sock $ encode (OfpFrame (OfpHeader 1 0 0 xid) (OfptFlowMod fm))
      putStrLn $ "TX: " ++ show n
      flood' (n-1) (xid+1)
