-- | Provides core Discord functionallity. 
{-# LANGUAGE DataKinds, TypeOperators, KindSignatures, RankNTypes, FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses, GADTs, TypeFamilies, FlexibleContexts #-}
module Network.Discord
  ( module Network.Discord
  , module Network.Discord.Framework
  , module Network.Discord.Gateway
  , module Network.Discord.Rest
  , module Network.Discord.Types
  ) where
  import Network.Discord.Framework
  import Network.Discord.Rest
  import Network.Discord.Types
  import Network.Discord.Gateway

  import Control.Monad (mzero)
  import Control.Applicative ((<|>))
  import Data.Proxy
  import GHC.TypeLits hiding ((:<>:))

  class (DiscordAuth m, Event ~ Domain f, () ~ Codomain f, EventMap f (DiscordApp m))
    => EventHandler f m

  runBot :: (DiscordAuth m, EventHandler f m) => Proxy (m f) -> IO ()
  runBot p = runGateway gatewayUrl $ (DiscordApp (\_ e -> return e) >>= go p) <|> return ()
    where
      split :: Proxy (a b) -> (Proxy a, Proxy b)
      split _ = (Proxy, Proxy)
      go :: EventHandler f m => Proxy (m f) -> Event -> DiscordApp m ()
      go p' = let (_, b) = split p' in mapEvent b

  data ReadyEvent
  
  instance (DiscordGate m, DiscordRest m) => EventMap ReadyEvent m where
    type Domain   ReadyEvent = Event
    type Codomain ReadyEvent = Init

    mapEvent _ (Ready e) = return e
    mapEvent _ _ = mzero

  data ResumedEvent

  instance (DiscordGate m, DiscordRest m) =>  EventMap ResumedEvent m where
    type Domain   ResumedEvent = Event
    type Codomain ResumedEvent = Object

    mapEvent _ (Resumed e) = return e
    mapEvent _ _ = mzero

  data ChannelCreateEvent

  instance (DiscordGate m, DiscordRest m) =>  EventMap ChannelCreateEvent m where
    type Domain   ChannelCreateEvent = Event
    type Codomain ChannelCreateEvent = Channel

    mapEvent _ (ChannelCreate e) = return e
    mapEvent _ _ = mzero
  
  data ChannelUpdateEvent
  
  instance (DiscordGate m, DiscordRest m) =>  EventMap ChannelUpdateEvent m where
    type Domain   ChannelUpdateEvent = Event
    type Codomain ChannelUpdateEvent = Channel

    mapEvent _ (ChannelUpdate e) = return e
    mapEvent _ _ = mzero

  data ChannelDeleteEvent

  instance (DiscordGate m, DiscordRest m) =>  EventMap ChannelDeleteEvent m where
    type Domain   ChannelDeleteEvent = Event
    type Codomain ChannelDeleteEvent = Channel

    mapEvent _ (ChannelDelete e) = return e
    mapEvent _ _ = mzero

  data GuildCreateEvent

  instance (DiscordGate m, DiscordRest m) =>  EventMap GuildCreateEvent m where
    type Domain   GuildCreateEvent = Event
    type Codomain GuildCreateEvent = Guild

    mapEvent _ (GuildCreate e) = return e
    mapEvent _ _ = mzero

  data GuildUpdateEvent

  instance (DiscordGate m, DiscordRest m) =>  EventMap GuildUpdateEvent m where
    type Domain   GuildUpdateEvent = Event
    type Codomain GuildUpdateEvent = Guild

    mapEvent _ (GuildUpdate e) = return e
    mapEvent _ _ = mzero

  data GuildDeleteEvent

  instance (DiscordGate m, DiscordRest m) =>  EventMap GuildDeleteEvent m where
    type Domain   GuildDeleteEvent = Event
    type Codomain GuildDeleteEvent = Guild

    mapEvent _ (GuildDelete e) = return e
    mapEvent _ _ = mzero

  data GuildBanAddEvent

  instance (DiscordGate m, DiscordRest m) =>  EventMap GuildBanAddEvent m where
    type Domain   GuildBanAddEvent = Event
    type Codomain GuildBanAddEvent = Member

    mapEvent _ (GuildBanAdd e) = return e
    mapEvent _ _ = mzero

  data GuildBanRemoveEvent

  instance (DiscordGate m, DiscordRest m) =>  EventMap GuildBanRemoveEvent m where
    type Domain   GuildBanRemoveEvent = Event
    type Codomain GuildBanRemoveEvent = Member

    mapEvent _ (GuildBanRemove e) = return e
    mapEvent _ _ = mzero

  data GuildEmojiUpdateEvent

  instance (DiscordGate m, DiscordRest m) =>  EventMap GuildEmojiUpdateEvent m where
    type Domain   GuildEmojiUpdateEvent = Event
    type Codomain GuildEmojiUpdateEvent = Object

    mapEvent _ (GuildEmojiUpdate e) = return e
    mapEvent _ _ = mzero

  data GuildIntegrationsUpdateEvent

  instance (DiscordGate m, DiscordRest m) =>  EventMap GuildIntegrationsUpdateEvent m where
    type Domain   GuildIntegrationsUpdateEvent = Event
    type Codomain GuildIntegrationsUpdateEvent = Object

    mapEvent _ (GuildIntegrationsUpdate e) = return e
    mapEvent _ _ = mzero

  data GuildMemberAddEvent

  instance (DiscordGate m, DiscordRest m) =>  EventMap GuildMemberAddEvent m where
    type Domain   GuildMemberAddEvent = Event
    type Codomain GuildMemberAddEvent = Member

    mapEvent _ (GuildMemberAdd e) = return e
    mapEvent _ _ = mzero

  data GuildMemberRemoveEvent

  instance (DiscordGate m, DiscordRest m) =>  EventMap GuildMemberRemoveEvent m where
    type Domain   GuildMemberRemoveEvent = Event
    type Codomain GuildMemberRemoveEvent = Member

    mapEvent _ (GuildMemberRemove e) = return e
    mapEvent _ _ = mzero

  data GuildMemberUpdateEvent

  instance (DiscordGate m, DiscordRest m) =>  EventMap GuildMemberUpdateEvent m where
    type Domain   GuildMemberUpdateEvent = Event
    type Codomain GuildMemberUpdateEvent = Member

    mapEvent _ (GuildMemberUpdate e) = return e
    mapEvent _ _ = mzero

  data GuildMemberChunkEvent

  instance (DiscordGate m, DiscordRest m) =>  EventMap GuildMemberChunkEvent m where
    type Domain   GuildMemberChunkEvent = Event
    type Codomain GuildMemberChunkEvent = Object

    mapEvent _ (GuildMemberChunk e) = return e
    mapEvent _ _ = mzero

  data GuildRoleCreateEvent

  instance (DiscordGate m, DiscordRest m) =>  EventMap GuildRoleCreateEvent m where
    type Domain   GuildRoleCreateEvent = Event
    type Codomain GuildRoleCreateEvent = Object

    mapEvent _ (GuildRoleCreate e) = return e
    mapEvent _ _ = mzero

  data GuildRoleUpdateEvent

  instance (DiscordGate m, DiscordRest m) =>  EventMap GuildRoleUpdateEvent m where
    type Domain   GuildRoleUpdateEvent = Event
    type Codomain GuildRoleUpdateEvent = Object

    mapEvent _ (GuildRoleUpdate e) = return e
    mapEvent _ _ = mzero

  data GuildRoleDeleteEvent

  instance (DiscordGate m, DiscordRest m) =>  EventMap GuildRoleDeleteEvent m where
    type Domain   GuildRoleDeleteEvent = Event
    type Codomain GuildRoleDeleteEvent = Object

    mapEvent _ (GuildRoleDelete e) = return e
    mapEvent _ _ = mzero

  data MessageCreateEvent

  instance (DiscordGate m, DiscordRest m) =>  EventMap MessageCreateEvent m where
    type Domain   MessageCreateEvent = Event
    type Codomain MessageCreateEvent = Message

    mapEvent _ (MessageCreate e) = return e
    mapEvent _ _ = mzero
  
  data MessageUpdateEvent

  instance (DiscordGate m, DiscordRest m) =>  EventMap MessageUpdateEvent m where
    type Domain   MessageUpdateEvent = Event
    type Codomain MessageUpdateEvent = Message

    mapEvent _ (MessageUpdate e) = return e
    mapEvent _ _ = mzero

  data MessageDeleteEvent

  instance (DiscordGate m, DiscordRest m) =>  EventMap MessageDeleteEvent m where
    type Domain   MessageDeleteEvent = Event
    type Codomain MessageDeleteEvent = Object

    mapEvent _ (MessageDelete e) = return e
    mapEvent _ _ = mzero

  data MessageDeleteBulkEvent

  instance (DiscordGate m, DiscordRest m) =>  EventMap MessageDeleteBulkEvent m where
    type Domain   MessageDeleteBulkEvent = Event
    type Codomain MessageDeleteBulkEvent = Object

    mapEvent _ (MessageDeleteBulk e) = return e
    mapEvent _ _ = mzero

  data PresenceUpdateEvent

  instance (DiscordGate m, DiscordRest m) =>  EventMap PresenceUpdateEvent m where
    type Domain   PresenceUpdateEvent = Event
    type Codomain PresenceUpdateEvent = Object

    mapEvent _ (PresenceUpdate e) = return e
    mapEvent _ _ = mzero

  data TypingStartEvent

  instance (DiscordGate m, DiscordRest m) =>  EventMap TypingStartEvent m where
    type Domain   TypingStartEvent = Event
    type Codomain TypingStartEvent = Object

    mapEvent _ (TypingStart e) = return e
    mapEvent _ _ = mzero

  data UserSettingsUpdateEvent

  instance (DiscordGate m, DiscordRest m) =>  EventMap UserSettingsUpdateEvent m where
    type Domain   UserSettingsUpdateEvent = Event
    type Codomain UserSettingsUpdateEvent = Object

    mapEvent _ (UserSettingsUpdate e) = return e
    mapEvent _ _ = mzero

  data UserUpdateEvent

  instance (DiscordGate m, DiscordRest m) =>  EventMap UserUpdateEvent m where
    type Domain   UserUpdateEvent = Event
    type Codomain UserUpdateEvent = Object

    mapEvent _ (UserUpdate e) = return e
    mapEvent _ _ = mzero

  data VoiceStateUpdateEvent

  instance (DiscordGate m, DiscordRest m) =>  EventMap VoiceStateUpdateEvent m where
    type Domain   VoiceStateUpdateEvent = Event
    type Codomain VoiceStateUpdateEvent = Object

    mapEvent _ (VoiceStateUpdate e) = return e
    mapEvent _ _ = mzero

  data VoiceServerUpdateEvent

  instance (DiscordGate m, DiscordRest m) => EventMap VoiceServerUpdateEvent m where
    type Domain   VoiceServerUpdateEvent = Event
    type Codomain VoiceServerUpdateEvent = Object

    mapEvent _ (VoiceServerUpdate e) = return e
    mapEvent _ _ = mzero

  data OtherEvent (a :: Symbol)

  instance (DiscordGate m, DiscordRest m, KnownSymbol e) => EventMap (OtherEvent e) m where
    type Domain   (OtherEvent e) = Event
    type Codomain (OtherEvent e) = Object

    mapEvent p (UnknownEvent s e)
      | s == event p = return e
      | otherwise    = mzero
      where
        event :: KnownSymbol a => Proxy (OtherEvent a) -> String
        event op = symbolVal $ eventName op
        eventName :: Proxy (OtherEvent a) -> Proxy a
        eventName _ = Proxy
    mapEvent _ _ = mzero