/*
 * Copyright (c) 2010-2022 Belledonne Communications SARL.
 *
 * This file is part of Liblinphone 
 * (see https://gitlab.linphone.org/BC/public/liblinphone).
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef _MSC_VER
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
#endif // _MSC_VER

#ifndef _CHAT_MESSAGE_HH
#define _CHAT_MESSAGE_HH

#include <string>
#include <list>
#include <cstdint>
#include "chat_message.hh"
#include "object.hh"




struct _LinphoneChatMessage;


namespace linphone {

	class ChatRoom;
	class Content;
	class Core;
	class ErrorInfo;
	class Address;
	class ChatMessageReaction;
	class ChatMessage;
	class ParticipantImdnState;
	class ChatMessageListener;

	/**
	 * @brief A @ref ChatMessage represents an instant message that can be send or
	 * received through a @ref ChatRoom. 
	 *
	 * To create a @ref ChatMessage, use ChatRoom::createEmptyMessage(), then either
	 * add text using addUtf8TextContent() or a @ref Content with file informations
	 * using addFileContent(). A valid @ref Content for file transfer must contain a
	 * type and subtype, the name of the file and it's size. Finally call send() to
	 * send it.
	 * To send files through a @ref ChatMessage, you need to have configured a file
	 * transfer server URL with Core::setFileTransferServer(). On the receiving side,
	 * either use downloadContent() to download received files or enable auto-download
	 * in the @ref Core using Core::setMaxSizeForAutoDownloadIncomingFiles(), -1
	 * disabling the feature and 0 always downloading files no matter it's size.
	 * Keep in mind a @ref ChatMessage created by a @ref ChatRoom::Backend::Basic @ref
	 * ChatRoom can only contain one @ref Content, either text or file. 
	 */
	class ChatMessage: public MultiListenableObject {
	
		public:
			/**
			* @ref Direction is used to indicate if a message is outgoing or incoming. 
			*
			*/
			enum class Direction {
				/**
				* Incoming message. 
				*/
				Incoming = 0,
				/**
				* Outgoing message. 
				*/
				Outgoing = 1
			};

			/**
			* @ref State is used to notify if messages have been successfully delivered or
			* not. 
			*
			*/
			enum class State {
				/**
				* Initial state. 
				*/
				Idle = 0,
				/**
				* Delivery in progress. 
				*/
				InProgress = 1,
				/**
				* Message successfully delivered and acknowledged by the server. 
				*/
				Delivered = 2,
				/**
				* Message was not delivered. 
				*/
				NotDelivered = 3,
				/**
				* Message was received and acknowledged but cannot get file from server. 
				*/
				FileTransferError = 4,
				/**
				* File transfer has been completed successfully. 
				*/
				FileTransferDone = 5,
				/**
				* Message successfully delivered an acknowledged by the remote user. 
				*/
				DeliveredToUser = 6,
				/**
				* Message successfully displayed to the remote user. 
				*/
				Displayed = 7,
				/**
				* File transfer is in progress. 
				*/
				FileTransferInProgress = 8,
				/**
				* Message is pending delivery. 
				*/
				PendingDelivery = 9,
				/**
				* The user cancelled the file transfer. 
				*/
				FileTransferCancelling = 10
			};


			LINPHONECXX_PUBLIC ChatMessage(void *ptr, bool takeRef=true);
			LINPHONECXX_PUBLIC _LinphoneChatMessage *cPtr() {return (_LinphoneChatMessage *)mPrivPtr;}

			/**
			 * @brief Add an application listener to the ChatMessage instance.
			 *
			 * @param listener the application listener
			 */
			LINPHONECXX_PUBLIC void addListener(const std::shared_ptr<ChatMessageListener> &listener);
			/**
			 * @brief Remove a previously added listener from the ChatMessage instance.
			 *
			 * @param listener the application listener
			 */
			LINPHONECXX_PUBLIC void removeListener(const std::shared_ptr<ChatMessageListener> &listener);
	
		public:
			
		
			/**
			 * @brief Linphone message has an app-specific field that can store a text. 
			 *
			 * The application might want to use it for keeping data over restarts, like
			 * thumbnail image path. 
			 * @return the application-specific data or nullptr if none has been stored.    
			 */
			LINPHONECXX_PUBLIC std::string getAppdata() const;
			
			/**
			 * @brief Linphone message has an app-specific field that can store a text. 
			 *
			 * The application might want to use it for keeping data over restarts, like
			 * thumbnail image path.
			 * Invoking this function will attempt to update the message storage to reflect
			 * the change if it is enabled.
			 * @param data the data to store into the message.    
			 */
			LINPHONECXX_PUBLIC void setAppdata(const std::string & data);
			
			/**
			 * @brief Gets the SIP call-id accociated with the message. 
			 *
			 * @return the call-id    
			 */
			LINPHONECXX_PUBLIC std::string getCallId() const;
			
			/**
			 * @brief Returns the chatroom this message belongs to. 
			 *
			 * @return the @ref ChatRoom in which this message has been sent or received.    
			 */
			LINPHONECXX_PUBLIC std::shared_ptr<linphone::ChatRoom> getChatRoom() const;
			
			/**
			 * @brief Gets the content type of a chat message. 
			 *
			 * @return The content type of the chat message    
			 */
			LINPHONECXX_PUBLIC std::string getContentType() const;
			
			/**
			 * @brief Sets the content type of a chat message. 
			 *
			 * This content type must match a content that is text representable, such as
			 * text/plain, text/html or image/svg+xml. 
			 * @param contentType The new content type of the chat message    
			 */
			LINPHONECXX_PUBLIC void setContentType(const std::string & contentType);
			
			/**
			 * @brief Returns the list of contents in the message. 
			 *
			 * @return The list of @ref Content.      
			 */
			LINPHONECXX_PUBLIC std::list<std::shared_ptr<linphone::Content>> getContents() const;
			
			/**
			 * @brief Returns back pointer to @ref Core object. 
			 *
			 * @return the @ref Core object associated with this message.    
			 */
			LINPHONECXX_PUBLIC std::shared_ptr<linphone::Core> getCore() const;
			
			/**
			 * @brief Returns the real time at which an ephemeral message expires and will be
			 * deleted. 
			 *
			 * @see isEphemeral() 
			 * @return the time at which an ephemeral message expires. 0 means the message has
			 * not been read. 
			 */
			LINPHONECXX_PUBLIC time_t getEphemeralExpireTime() const;
			
			/**
			 * @brief Returns lifetime of an ephemeral message. 
			 *
			 * The lifetime is the duration after which the ephemeral message will disappear
			 * once viewed. @see isEphemeral() 
			 * @return the lifetime of an ephemeral message, by default 0 (disabled). 
			 */
			LINPHONECXX_PUBLIC long getEphemeralLifetime() const;
			
			/**
			 * @brief Gets full details about delivery error of a chat message. 
			 *
			 * @return a @ref ErrorInfo describing the details.    
			 */
			LINPHONECXX_PUBLIC std::shared_ptr<const linphone::ErrorInfo> getErrorInfo() const;
			
			/**
			 * @brief Messages can carry external body as defined by rfc2017. 
			 *
			 * @return external body url or nullptr if not present.    
			 */
			LINPHONECXX_PUBLIC std::string getExternalBodyUrl() const;
			
			/**
			 * @brief Messages can carry external body as defined by rfc2017. 
			 *
			 * @param externalBodyUrl ex: access-type=URL; URL="http://www.foo.com/file"    
			 */
			LINPHONECXX_PUBLIC void setExternalBodyUrl(const std::string & externalBodyUrl);
			
			/**
			 * @brief Gets the file transfer information (used by callbacks to recover
			 * informations during a rcs file transfer). 
			 *
			 * @return a pointer to the @ref Content structure or nullptr if not present.    
			 */
			LINPHONECXX_PUBLIC std::shared_ptr<linphone::Content> getFileTransferInformation() const;
			
			/**
			 * @brief Gets the forward info if available as a string. 
			 *
			 * @return the original sender of the message if it has been forwarded, nullptr
			 * otherwise.    
			 */
			LINPHONECXX_PUBLIC std::string getForwardInfo() const;
			
			/**
			 * @brief Gets origin of the message. 
			 *
			 * @return the @ref Address of the sender.    
			 */
			LINPHONECXX_PUBLIC std::shared_ptr<const linphone::Address> getFromAddress() const;
			
			/**
			 * @brief Returns wether the chat message is an ephemeral message or not. 
			 *
			 * An ephemeral message will automatically disappear from the recipient's screen
			 * after the message has been viewed. 
			 * @return true if it is an ephemeral message, false otherwise 
			 */
			LINPHONECXX_PUBLIC bool isEphemeral() const;
			
			/**
			 * @brief Return whether or not a chat message is a file transfer. 
			 *
			 * @return Whether or not the message is a file transfer 
			 * @deprecated 06/07/2020 check if getContents() contains a @ref Content for which
			 * Content::isFileTransfer() returns true. 
			 */
			LINPHONECXX_PUBLIC LINPHONECXX_DEPRECATED bool isFileTransfer() const;
			
			/**
			 * @brief Gets whether or not a file is currently being downloaded or uploaded. 
			 *
			 * @return true if download or upload is in progress, false otherwise 
			 */
			LINPHONECXX_PUBLIC bool isFileTransferInProgress() const;
			
			/**
			 * @brief Returns wether the chat message is a forward message or not. 
			 *
			 * @return true if it is a forward message, false otherwise 
			 */
			LINPHONECXX_PUBLIC bool isForward();
			
			/**
			 * @brief Returns wehther the message has been sent or received. 
			 *
			 * @return true if message has been sent, false if it has been received. 
			 */
			LINPHONECXX_PUBLIC bool isOutgoing() const;
			
			/**
			 * @brief Returns wether the message has been read or not. 
			 *
			 * @return true if message has been marked as read, false otherwise. 
			 */
			LINPHONECXX_PUBLIC bool isRead() const;
			
			/**
			 * @brief Returns wether the chat message is a reply message or not. 
			 *
			 * @return true if it is a reply message, false otherwise 
			 */
			LINPHONECXX_PUBLIC bool isReply();
			
			/**
			 * @brief Gets if the message was end-to-end encrypted when transferred. 
			 *
			 * @return true if the message was end-to-end encrypted when transferred, false
			 * otherwise. 
			 */
			LINPHONECXX_PUBLIC bool isSecured() const;
			
			/**
			 * @brief Return whether or not a chat message is a text. 
			 *
			 * @return Whether or not the message is a text 
			 * @deprecated 06/07/2020 check if getContents() contains a @ref Content with a
			 * PlainText content type. 
			 */
			LINPHONECXX_PUBLIC LINPHONECXX_DEPRECATED bool isText() const;
			
			/**
			 * @brief Returns the local address the message was sent or received with. 
			 *
			 * @return the @ref Address of the local address used to send/receive this
			 * message.    
			 */
			LINPHONECXX_PUBLIC std::shared_ptr<const linphone::Address> getLocalAddress() const;
			
			/**
			 * @brief Get the message identifier. 
			 *
			 * It is used to identify a message so that it can be notified as delivered and/or
			 * displayed. 
			 * @return The message identifier.    
			 */
			LINPHONECXX_PUBLIC std::string getMessageId() const;
			
			/**
			 * @brief Returns our own reaction for a given chat message, if any. 
			 *
			 * @return Our own @ref ChatMessageReaction for that message if any, nullptr
			 * otherwise.    
			 */
			LINPHONECXX_PUBLIC std::shared_ptr<const linphone::ChatMessageReaction> getOwnReaction() const;
			
			/**
			 * @brief Returns the peer (remote) address of the message. 
			 *
			 * @return the @ref Address of the peer address used to send/receive this message.
			 *    
			 */
			LINPHONECXX_PUBLIC std::shared_ptr<const linphone::Address> getPeerAddress() const;
			
			/**
			 * @brief Gets the list of reactions received for this chat message. 
			 *
			 * Warning: list is ordered by content of reaction message, not by received
			 * timestamp! 
			 * @return The sorted list of reaction if any.         
			 */
			LINPHONECXX_PUBLIC std::list<std::shared_ptr<linphone::ChatMessageReaction>> getReactions() const;
			
			/**
			 * @brief Returns the @ref ChatMessage this message is a reply to. 
			 *
			 * @return the original message @ref ChatMessage.    
			 */
			LINPHONECXX_PUBLIC std::shared_ptr<linphone::ChatMessage> getReplyMessage();
			
			/**
			 * @brief Returns the ID of the message this is a reply to. 
			 *
			 * @return the original message id.    
			 */
			LINPHONECXX_PUBLIC std::string getReplyMessageId();
			
			/**
			 * @brief Returns the address of the sender of the message this is a reply to. 
			 *
			 * @return the original message sender @ref Address.    
			 */
			LINPHONECXX_PUBLIC std::shared_ptr<const linphone::Address> getReplyMessageSenderAddress();
			
			/**
			 * @brief Gets the state of the message. 
			 *
			 * @return the current @ref State of the message. 
			 */
			LINPHONECXX_PUBLIC State getState() const;
			
			/**
			 * @brief Gets the text content if available as a string. 
			 *
			 * @return the @ref Content buffer if available in System Locale, null otherwise. 
			 *   
			 * @deprecated 01/07/2020. Use getUtf8Text() instead. 
			 */
			LINPHONECXX_PUBLIC LINPHONECXX_DEPRECATED std::string getTextContent() const;
			
			/**
			 * @brief Gets the time the message was sent. 
			 *
			 * @return the timestamp of when the message was sent. 
			 */
			LINPHONECXX_PUBLIC time_t getTime() const;
			
			/**
			 * @brief Gets destination of the message. 
			 *
			 * @return the @ref Address of the recipient.    
			 */
			LINPHONECXX_PUBLIC std::shared_ptr<const linphone::Address> getToAddress() const;
			
			/**
			 * @brief Gets if a chat message is to be stored. 
			 *
			 * @return Whether or not the message is to be stored 
			 */
			LINPHONECXX_PUBLIC bool getToBeStored() const;
			
			/**
			 * @brief Sets if a chat message is to be stored. 
			 *
			 * This content type must match a content that is text representable, such as
			 * text/plain, text/html or image/svg+xml. 
			 * @param toBeStored Whether or not the chat message is to be stored 
			 */
			LINPHONECXX_PUBLIC void setToBeStored(bool toBeStored);
			
			/**
			 * @brief Gets text part of this message. 
			 *
			 * @return The text as UTF8 characters or nullptr if no text.    
			 */
			LINPHONECXX_PUBLIC std::string getUtf8Text() const;
			
			/**
			 * @brief Sets a text to be sent, given as a string of UTF-8 characters. 
			 *
			 * @param text The text in UTF8 to set.    
			 * @return 0 if succeed. 
			 */
			LINPHONECXX_PUBLIC int setUtf8Text(const std::string & text);
			
			/**
			 * @brief Adds a content to the ChatMessage. 
			 *
			 * @param content the @ref Content object to add.    
			 */
			LINPHONECXX_PUBLIC void addContent(const std::shared_ptr<linphone::Content> & content);
			
			/**
			 * @brief Adds custom headers to the message. 
			 *
			 * @param headerName name of the header    
			 * @param headerValue header value    
			 */
			LINPHONECXX_PUBLIC void addCustomHeader(const std::string & headerName, const std::string & headerValue);
			
			/**
			 * @brief Adds a file content to the ChatMessage. 
			 *
			 * @param content the @ref Content object to add.    
			 */
			LINPHONECXX_PUBLIC void addFileContent(const std::shared_ptr<linphone::Content> & content);
			
			/**
			 * @brief Creates a @ref Content of type PlainText with the given text as body. 
			 *
			 * @param text The text in System Locale to add to the message.    
			 * @deprecated 01/07/2020. Use addUtf8TextContent() instead. 
			 */
			LINPHONECXX_PUBLIC LINPHONECXX_DEPRECATED void addTextContent(const std::string & text);
			
			/**
			 * @brief Creates a @ref Content of type text/plain with the provided string, and
			 * attach it to the message. 
			 *
			 * @param text The text to add to the message.    
			 */
			LINPHONECXX_PUBLIC void addUtf8TextContent(const std::string & text);
			
			/**
			 * @brief Cancels an ongoing file transfer attached to this message (upload or
			 * download). 
			 *
			 */
			LINPHONECXX_PUBLIC void cancelFileTransfer();
			
			/**
			 * @brief Creates a emoji reaction for the given chat mesage. 
			 *
			 * To send it, use ChatMessageReaction::send(). 
			 * @param utf8Reaction the emoji character(s) as UTF-8.    
			 * @return a @ref ChatMessageReaction object.    
			 */
			LINPHONECXX_PUBLIC std::shared_ptr<linphone::ChatMessageReaction> createReaction(const std::string & utf8Reaction);
			
			/**
			 * @brief Starts the download of the @ref Content referenced in the @ref
			 * ChatMessage from remote server. 
			 *
			 * @param content the @ref Content object to download (must have the
			 * Content::isFileTransfer() method return true).    
			 * @return false if there is an error, true otherwise. 
			 */
			LINPHONECXX_PUBLIC bool downloadContent(const std::shared_ptr<linphone::Content> & content);
			
			/**
			 * @brief Starts the download of all the @ref Content objects representing file
			 * transfers included in the message (Content::isFileTransfer() method returns
			 * true). 
			 *
			 * @return false if there is an error, true otherwise. 
			 */
			LINPHONECXX_PUBLIC bool downloadContents();
			
			/**
			 * @brief Retrieves a custom header value given its name. 
			 *
			 * @param headerName header name searched    
			 * @return the custom header value or nullptr if not found.    
			 */
			LINPHONECXX_PUBLIC std::string getCustomHeader(const std::string & headerName) const;
			
			/**
			 * @brief Gets the list of participants for which the imdn state has reached the
			 * specified state and the time at which they did. 
			 *
			 * @param state The @ref State the imdn have reached (only use
			 * LinphoneChatMessageStateDelivered, LinphoneChatMessageStateDeliveredToUser,
			 * LinphoneChatMessageStateDisplayed and LinphoneChatMessageStateNotDelivered) 
			 * @return The list of participants.         
			 */
			LINPHONECXX_PUBLIC std::list<std::shared_ptr<linphone::ParticipantImdnState>> getParticipantsByImdnState(State state) const;
			
			/**
			 * @brief Returns wether the chat message has a conference invitation content or
			 * not. 
			 *
			 * @return true if it has one, false otherwise. 
			 */
			LINPHONECXX_PUBLIC bool hasConferenceInvitationContent() const;
			
			/**
			 * @brief Returns wether the chat message has a text content or not. 
			 *
			 * @return true if it has one, false otherwise. 
			 * @deprecated 27/10/2020. Check if getContents() contains a @ref Content for
			 * which it's content type is PlainText. 
			 */
			LINPHONECXX_PUBLIC LINPHONECXX_DEPRECATED bool hasTextContent() const;
			
			/**
			 * @brief Marks the message as read. 
			 *
			 * Only triggers LinphoneChatRoomCbsChatRoomReadCb if it was the last unread
			 * message. 
			 */
			LINPHONECXX_PUBLIC void markAsRead();
			
			/**
			 * @brief Fulfills a chat message character by character and send the character
			 * immediately as real-time text (RFC4103 / T.140) or as Baudot tones. 
			 *
			 * The method used to send the character depends on if real-time text is enabled
			 * or not. If it is, real-time text is of course used, otherwise Baudot will be
			 * used if it is enabled in the @ref Core (see Core::enableBaudot()). If real-time
			 * text is used, the @ref ChatRoom the message was created from must be a
			 * real-time text capable chat room: it must be obtained by placing or receiving a
			 * call with real-time text capabilities (see CallParams::enableRealtimeText() ),
			 * and getting the @ref ChatRoom interface from the call with Call::getChatRoom().
			 * When the message is terminated (ie a new line needs to be started), use send()
			 * in order to trigger the sending of the new line character and have the full
			 * message (comprising all characters sent so far) stored in local database. 
			 * @param character The character to send (T.140 char for real-time text). 
			 * @return 0 if succeed. 
			 */
			LINPHONECXX_PUBLIC linphone::Status putChar(uint32_t character);
			
			/**
			 * @brief Removes a content from the ChatMessage. 
			 *
			 * @param content the @ref Content object to remove.    
			 */
			LINPHONECXX_PUBLIC void removeContent(const std::shared_ptr<linphone::Content> & content);
			
			/**
			 * @brief Removes a custom header from the message. 
			 *
			 * @param headerName name of the header to remove    
			 */
			LINPHONECXX_PUBLIC void removeCustomHeader(const std::string & headerName);
			
			/**
			 * @brief Sends a chat message. 
			 *
			 */
			LINPHONECXX_PUBLIC void send();
			
			
		private:
			void *createCallbacks() override;
	};

};

#endif // _CHAT_MESSAGE_HH

#ifndef _MSC_VER
#pragma GCC diagnostic pop
#endif // _MSC_VER
