#include "capesdk.h"
#include "twitchsdk/chat/chatapi.h"
#include "Internal.h"

namespace TwitchInGames {
	struct TtvInitializer {
		TtvInitializer();
		~TtvInitializer();
	};

	struct ChatImpl : TtvInitializer {
		struct CoreApiListener : public ttv::ICoreAPIListener {
			void CoreUserLoginComplete(std::string const& oauthToken, ttv::UserId userId, TTV_ErrorCode ec);
			void CoreUserLogoutComplete(ttv::UserId userId, TTV_ErrorCode ec);
			void CoreUserAuthenticationIssue(ttv::UserId userId, std::string const& oauthToken, TTV_ErrorCode ec);
			void CorePubSubStateChanged(ttv::UserId userId, ttv::PubSubState state, TTV_ErrorCode ec);
			void ModuleStateChanged(ttv::IModule* source, ttv::IModule::State state, TTV_ErrorCode ec);
		};

		struct ChatApiListener : public ttv::chat::IChatAPIListener {
			void ChatUserEmoticonSetsChanged(ttv::UserId userId, std::vector<ttv::chat::EmoticonSet> const& emoticonSets) override;
			void ModuleStateChanged(ttv::IModule* source, ttv::IModule::State state, TTV_ErrorCode ec) override;
		};

		struct ChatChannelListener : public ttv::chat::IChatChannelListener {
			void ChatChannelStateChanged(ttv::UserId userId, ttv::ChannelId channelId, ttv::chat::ChatChannelState state, TTV_ErrorCode ec);
			void ChatChannelInfoChanged(ttv::UserId userId, ttv::ChannelId channelId, ttv::chat::ChatChannelInfo const& channelInfo);
			void ChatChannelRestrictionsChanged(ttv::UserId userId, ttv::ChannelId channelId, ttv::chat::ChatChannelRestrictions const& restrictions);
			void ChatChannelLocalUserChanged(ttv::UserId userId, ttv::ChannelId channelId, ttv::chat::ChatUserInfo const& userInfo);
			void ChatChannelMessagesReceived(ttv::UserId userId, ttv::ChannelId channelId, std::vector<ttv::chat::LiveChatMessage> const& messageList);
			void ChatChannelSubscriptionNoticeReceived(ttv::UserId userId, ttv::ChannelId channelId, ttv::chat::SubscriptionNotice const& notice);
			void ChatChannelFirstTimeChatterNoticeReceived(ttv::UserId userId, ttv::ChannelId channelId, ttv::chat::FirstTimeChatterNotice const& notice);
			void ChatChannelRaidNoticeReceived(ttv::UserId userId, ttv::ChannelId channelId, ttv::chat::RaidNotice const& notice);
			void ChatChannelUnraidNoticeReceived(ttv::UserId userId, ttv::ChannelId channelId, ttv::chat::UnraidNotice const& notice);
			void ChatChannelMessagesCleared(ttv::UserId userId, ttv::ChannelId channelId);
			void ChatChannelUserMessagesCleared(ttv::UserId userId, ttv::ChannelId channelId, ttv::UserId clearUserId);
			void ChatChannelHostTargetChanged(ttv::UserId userId, ttv::ChannelId channelId, std::string const& targetChannelName, uint32_t numViewers);
			void ChatChannelNoticeReceived(ttv::UserId userId, ttv::ChannelId channelId, std::string const& noticeId, std::map<std::string, std::string> const& params);
		};

		ChatImpl();
		~ChatImpl();

		std::shared_ptr<ttv::chat::IChatChannel> CreateChannel(ttv::ChannelId channelId, std::shared_ptr<ttv::chat::IChatChannelListener> const& listener);
		tstring SignIn(tstring const& clientId_, tstring const& token_);
		void SignOut();

		bool GetIsSignedIn() const { return isSignedIn; }
		__declspec(property(get = GetIsSignedIn)) bool IsSignedIn;

	private:
		std::shared_ptr<ChatApiListener> apiListener;
		std::shared_ptr<ChatChannelListener> channelListener;
		// TODO:  std::shared_ptr<ttv::chat::IChatChannel> channel;
		std::shared_ptr<ttv::CoreAPI> coreApi;
		std::shared_ptr<ttv::chat::ChatAPI> chatApi;
		tstring clientId, token;
		ttv::UserId userId;
		std::future<void> updateTask;
		std::promise<void> updatePromise;
		bool isSignedIn;
	};
}
