/** 
 * @file llpluginclassmedia.h
 * @brief LLPluginClassMedia handles interaction with a plugin which knows about the "media" message class.
 *
 * @cond
 * $LicenseInfo:firstyear=2008&license=viewerlgpl$
 * Second Life Viewer Source Code
 * Copyright (C) 2010, Linden Research, Inc.
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation;
 * version 2.1 of the License only.
 * 
 * This library 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
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 * 
 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 * $/LicenseInfo$
 * @endcond
 */

#ifndef LL_LLPLUGINCLASSMEDIA_H
#define LL_LLPLUGINCLASSMEDIA_H

#include "llgltypes.h"
#include "llpluginprocessparent.h"
#include "llrect.h"
#include "llpluginclassmediaowner.h"
#include <queue>
#include "v4color.h"

class LLPluginClassMedia final : public LLPluginProcessParentOwner
{
	LOG_CLASS(LLPluginClassMedia);
public:
	LLPluginClassMedia(LLPluginClassMediaOwner *owner);
	virtual ~LLPluginClassMedia();

	// local initialization, called by the media manager when creating a source
	bool init(const std::string &launcher_filename, 
					  const std::string &plugin_dir, 
					  const std::string &plugin_filename, 
					  bool debug);

	// undoes everything init() didm called by the media manager when destroying a source
	void reset();
	
	void idle(void);
	
	// All of these may return 0 or an actual valid value.
	// Callers need to check the return for 0, and not use the values in that case.
	int getWidth() const { return (mMediaWidth > 0) ? mMediaWidth : 0; };
	int getHeight() const { return (mMediaHeight > 0) ? mMediaHeight : 0; };
	int getNaturalWidth() const { return mNaturalMediaWidth; };
	int getNaturalHeight() const { return mNaturalMediaHeight; };
	int getSetWidth() const { return mSetMediaWidth; };
	int getSetHeight() const { return mSetMediaHeight; };
	int getBitsWidth() const { return (mTextureWidth > 0) ? mTextureWidth : 0; };
	int getBitsHeight() const { return (mTextureHeight > 0) ? mTextureHeight : 0; };
	int getTextureWidth() const;
	int getTextureHeight() const;
	int getFullWidth() const { return mFullMediaWidth; };
	int getFullHeight() const { return mFullMediaHeight; };
	F64 getZoomFactor() const { return mZoomFactor; };
	
	// This may return NULL.  Callers need to check for and handle this case.
	unsigned char* getBitsData();

	// gets the format details of the texture data
	// These may return 0 if they haven't been set up yet.  The caller needs to detect this case.
	int getTextureDepth() const { return mRequestedTextureDepth; };
	int getTextureFormatInternal() const { return mRequestedTextureInternalFormat; };
	int getTextureFormatPrimary() const { return mRequestedTextureFormat; };
	int getTextureFormatType() const { return mRequestedTextureType; };
	bool getTextureFormatSwapBytes() const { return mRequestedTextureSwapBytes; };
	bool getTextureCoordsOpenGL() const { return mRequestedTextureCoordsOpenGL; };

	void setSize(int width, int height);
	void setAutoScale(bool auto_scale);
	void setZoomFactor(F64 zoom_factor) { mZoomFactor = zoom_factor; }

	void setBackgroundColor(LLColor4 color) { mBackgroundColor = color; };
	
	void setOwner(LLPluginClassMediaOwner *owner) { mOwner = owner; };
	
	// Returns true if all of the texture parameters (depth, format, size, and texture size) are set up and consistent.
	// This will initially be false, and will also be false for some time after setSize while the resize is processed.
	// Note that if this returns true, it is safe to use all the get() functions above without checking for invalid return values
	// until you call idle() again.
	bool textureValid(void);
	
	bool getDirty(LLRect *dirty_rect = NULL);
	void resetDirty(void);
	
	typedef enum 
	{
		MOUSE_EVENT_DOWN,
		MOUSE_EVENT_UP,
		MOUSE_EVENT_MOVE,
		MOUSE_EVENT_DOUBLE_CLICK
	}EMouseEventType;
	
	void mouseEvent(EMouseEventType type, int button, int x, int y, MASK modifiers);

	typedef enum 
	{
		KEY_EVENT_DOWN,
		KEY_EVENT_UP,
		KEY_EVENT_REPEAT
	}EKeyEventType;
	
	bool keyEvent(EKeyEventType type, int key_code, MASK modifiers, LLSD native_key_data);

	void scrollEvent(int x, int y, int clicks_x, int clicks_y, MASK modifiers);

	// enable/disable media plugin debugging messages and info spam
	void enableMediaPluginDebugging( bool enable );

	// Javascript <-> viewer events
	void jsEnableObject( bool enable );
	void jsAgentLocationEvent( double x, double y, double z );
	void jsAgentGlobalLocationEvent( double x, double y, double z );
	void jsAgentOrientationEvent( double angle );
	void jsAgentLanguageEvent( const std::string& language );
	void jsAgentRegionEvent( const std::string& region_name );
	void jsAgentMaturityEvent( const std::string& maturity );
		
	// Text may be unicode (utf8 encoded)
	bool textInput(const std::string &text, MASK modifiers, LLSD native_key_data);
	
    static std::string sOIDcookieUrl;
    static std::string sOIDcookieName;
    static std::string sOIDcookieValue;
    static std::string sOIDcookieHost;
    static std::string sOIDcookiePath;
    static bool sOIDcookieHttpOnly;
    static bool sOIDcookieSecure;
    void storeOpenIDCookie(const std::string url,
        const std::string name, const std::string value,
        const std::string host, const std::string path,
        bool httponly, bool secure);

    void injectOpenIDCookie();

	void setCookie(std::string uri, std::string name, std::string value, std::string domain, std::string path, bool httponly, bool secure);

	void loadURI(const std::string &uri);
	
	void executeJavaScript(const std::string &code);

	// "Loading" means uninitialized or any state prior to fully running (processing commands)
	bool isPluginLoading(void) { return mPlugin?mPlugin->isLoading():false; };

	// "Running" means the steady state -- i.e. processing messages
	bool isPluginRunning(void) { return mPlugin?mPlugin->isRunning():false; };
	
	// "Exited" means any regular or error state after "Running" (plugin may have crashed or exited normally)
	bool isPluginExited(void) { return mPlugin?mPlugin->isDone():false; };

	std::string getPluginVersion() { return mPlugin?mPlugin->getPluginVersion():std::string(); };

	bool getDisableTimeout() { return mPlugin?mPlugin->getDisableTimeout():false; };
	void setDisableTimeout(bool disable) { if(mPlugin) mPlugin->setDisableTimeout(disable); };
	
	// Inherited from LLPluginProcessParentOwner
	/* virtual */ void receivePluginMessage(const LLPluginMessage &message);
	/* virtual */ void pluginLaunchFailed();
	/* virtual */ void pluginDied();
	
	
	typedef enum 
	{
		PRIORITY_UNLOADED,	// media plugin isn't even loaded.
		PRIORITY_STOPPED,	// media is not playing, shouldn't need to update at all.
		PRIORITY_HIDDEN,	// media is not being displayed or is out of view, don't need to do graphic updates, but may still update audio, playhead, etc.
		PRIORITY_SLIDESHOW,	// media is in the far distance, updates very infrequently
		PRIORITY_LOW,		// media is in the distance, may be rendered at reduced size
		PRIORITY_NORMAL,	// normal (default) priority
		PRIORITY_HIGH		// media has user focus and/or is taking up most of the screen
	}EPriority;

	static const char* priorityToString(EPriority priority);
	void setPriority(EPriority priority);
	void setLowPrioritySizeLimit(int size);
	
	F64 getCPUUsage();
	
	void sendPickFileResponse(const std::vector<std::string> files);

	void sendAuthResponse(bool ok, const std::string &username, const std::string &password);

	// Valid after a MEDIA_EVENT_CURSOR_CHANGED event
	std::string getCursorName() const { return mCursorName; };

	LLPluginClassMediaOwner::EMediaStatus getStatus() const { return mStatus; }

	void	undo();
	bool	canUndo() const { return mCanUndo; };

	void	redo();
	bool	canRedo() const { return mCanRedo; };

	void	cut();
	bool	canCut() const { return mCanCut; };

	void	copy();
	bool	canCopy() const { return mCanCopy; };

	void	paste();
	bool	canPaste() const { return mCanPaste; };

	void	doDelete();
	bool	canDoDelete() const { return mCanDoDelete; };

	void	selectAll();
	bool	canSelectAll() const { return mCanSelectAll; };
	
	void	showPageSource();

	// These can be called before init(), and they will be queued and sent before the media init message.
	void	setCEFProgramDirs(const std::string& helper_path, const std::string& resources_path = std::string(), const std::string& locales_path = std::string());
	void	setUserDataPath(const std::string &user_data_path_cache, const std::string &username, const std::string &user_data_path_cef_log, bool verbose_log);
	void	setLanguageCode(const std::string &language_code);
	void	setPluginsEnabled(const bool enabled);
	void	setJavascriptEnabled(const bool enabled);
	void	setWebSecurityDisabled(const bool disabled);
	void	setFileAccessFromFileUrlsEnabled(const bool enabled);
	void	setTarget(const std::string &target);
	
	///////////////////////////////////
	// media browser class functions
	bool pluginSupportsMediaBrowser(void);
	
	void focus(bool focused);
	void set_page_zoom_factor( F64 factor );
	void clear_cache();
	void clear_cookies();
	void set_cookies(const std::string &cookies);
	void cookies_enabled(bool enable);
	void proxy_setup(bool enable, const std::string &host = LLStringUtil::null, int port = 0);
	void browse_stop();
	void browse_reload(bool ignore_cache = false);
	void browse_forward();
	void browse_back();
	void setBrowserUserAgent(const std::string& user_agent);
	void showWebInspector( bool show );
	void proxyWindowOpened(const std::string &target, const std::string &uuid);
	void proxyWindowClosed(const std::string &uuid);
	void ignore_ssl_cert_errors(bool ignore);
	void addCertificateFilePath(const std::string& path);
	
	// This is valid after MEDIA_EVENT_NAVIGATE_BEGIN or MEDIA_EVENT_NAVIGATE_COMPLETE
	std::string	getNavigateURI() const { return mNavigateURI; };

	// These are valid after MEDIA_EVENT_NAVIGATE_COMPLETE
	S32			getNavigateResultCode() const { return mNavigateResultCode; };
	std::string getNavigateResultString() const { return mNavigateResultString; };
	bool		getHistoryBackAvailable() const { return mHistoryBackAvailable; };
	bool		getHistoryForwardAvailable() const { return mHistoryForwardAvailable; };

	// This is valid after MEDIA_EVENT_PROGRESS_UPDATED
	int			getProgressPercent() const { return mProgressPercent; };
	
	// This is valid after MEDIA_EVENT_STATUS_TEXT_CHANGED
	std::string getStatusText() const { return mStatusText; };
	
	// This is valid after MEDIA_EVENT_LOCATION_CHANGED
	std::string getLocation() const { return mLocation; };
	
	// This is valid after MEDIA_EVENT_CLICK_LINK_HREF or MEDIA_EVENT_CLICK_LINK_NOFOLLOW
	std::string getClickURL() const { return mClickURL; };

	// This is valid after MEDIA_EVENT_CLICK_LINK_NOFOLLOW
	std::string getClickNavType() const { return mClickNavType; };

	// This is valid after MEDIA_EVENT_CLICK_LINK_HREF
	std::string getClickTarget() const { return mClickTarget; };

	// This is valid during MEDIA_EVENT_CLICK_LINK_HREF and MEDIA_EVENT_GEOMETRY_CHANGE
	std::string getClickUUID() const { return mClickUUID; };

	// mClickTarget is received from message and governs how link will be opened
	// use this to enforce your own way of opening links inside plugins
	void setOverrideClickTarget(const std::string &target);
	void resetOverrideClickTarget() { mClickEnforceTarget = false; };
	bool isOverrideClickTarget() const { return mClickEnforceTarget; }
	std::string getOverrideClickTarget() const { return mOverrideClickTarget; };

	// These are valid during MEDIA_EVENT_DEBUG_MESSAGE
	std::string getDebugMessageText() const { return mDebugMessageText; };
	std::string getDebugMessageLevel() const { return mDebugMessageLevel; };

	// This is valid after MEDIA_EVENT_NAVIGATE_ERROR_PAGE
	S32 getStatusCode() const { return mStatusCode; };
	
	// These are valid during MEDIA_EVENT_GEOMETRY_CHANGE
	S32 getGeometryX() const { return mGeometryX; };
	S32 getGeometryY() const { return mGeometryY; };
	S32 getGeometryWidth() const { return mGeometryWidth; };
	S32 getGeometryHeight() const { return mGeometryHeight; };
	
	// These are valid during MEDIA_EVENT_AUTH_REQUEST
	std::string	getAuthURL() const { return mAuthURL; };
	std::string	getAuthRealm() const { return mAuthRealm; };

	// These are valid during MEDIA_EVENT_PICK_FILE_REQUEST
	bool getIsMultipleFilePick() const { return mIsMultipleFilePick; }

	// These are valid during MEDIA_EVENT_LINK_HOVERED
	std::string	getHoverText() const { return mHoverText; };
	std::string	getHoverLink() const { return mHoverLink; };
	
	// these are valid during MEDIA_EVENT_LINK_HOVERED 
	std::string getFileDownloadFilename() const { return mFileDownloadFilename; }


	const std::string& getMediaName() const { return mMediaName; };
	std::string getMediaDescription() const { return mMediaDescription; };

	// Crash the plugin.  If you use this outside of a testbed, you will be punished.
	void		crashPlugin();
	
	// Hang the plugin.  If you use this outside of a testbed, you will be punished.
	void		hangPlugin();

	///////////////////////////////////
	// media time class functions
	bool pluginSupportsMediaTime(void);
	void stop();
	void start(float rate = 0.0f);
	void pause();
	void seek(float time);
	void setLoop(bool loop);
	void setVolume(float volume);
	float getVolume();
	
	F64 getCurrentTime(void) const { return mCurrentTime; };
	F64 getDuration(void) const { return mDuration; };
	F64 getCurrentPlayRate(void) { return mCurrentRate; };
	F64 getLoadedDuration(void) const { return mLoadedDuration; };
	
	// Initialize the URL history of the plugin by sending
	// "init_history" message 
	void initializeUrlHistory(const LLSD& url_history);

	boost::shared_ptr<LLPluginClassMedia> getSharedPrt() { return boost::dynamic_pointer_cast<LLPluginClassMedia>(shared_from_this()); } // due to enable_shared_from_this

protected:

	LLPluginClassMediaOwner *mOwner;

	// Notify this object's owner that an event has occurred.
	void mediaEvent(LLPluginClassMediaOwner::EMediaEvent event);
		
	void sendMessage(const LLPluginMessage &message);  // Send message internally, either queueing or sending directly.
	std::queue<LLPluginMessage> mSendQueue;		// Used to queue messages while the plugin initializes.
	
	void setSizeInternal(void);

	bool		mTextureParamsReceived;		// the mRequestedTexture* fields are only valid when this is true
	S32 		mRequestedTextureDepth;
	LLGLenum	mRequestedTextureInternalFormat;
	LLGLenum	mRequestedTextureFormat;
	LLGLenum	mRequestedTextureType;
	bool		mRequestedTextureSwapBytes;
	bool		mRequestedTextureCoordsOpenGL;
	
	std::string mTextureSharedMemoryName;
	size_t		mTextureSharedMemorySize;
	
	// True to scale requested media up to the full size of the texture (i.e. next power of two)
	bool		mAutoScaleMedia;

	// default media size for the plugin, from the texture_params message.
	int			mDefaultMediaWidth;
	int			mDefaultMediaHeight;

	// Size that has been requested by the plugin itself
	int			mNaturalMediaWidth;
	int			mNaturalMediaHeight;

	// Size that has been requested with setSize()
	int			mSetMediaWidth;
	int			mSetMediaHeight;
	
	// Full calculated media size (before auto-scale and downsample calculations)
	int			mFullMediaWidth;
	int			mFullMediaHeight;

	// Actual media size being set (after auto-scale)
	int			mRequestedMediaWidth;
	int			mRequestedMediaHeight;
	
	// Texture size calculated from actual media size
	int			mRequestedTextureWidth;
	int			mRequestedTextureHeight;
	
	// Size that the plugin has acknowledged
	int			mTextureWidth;
	int			mTextureHeight;
	int			mMediaWidth;
	int			mMediaHeight;

	F64			mZoomFactor;
	
	float		mRequestedVolume;
	
	// Priority of this media stream
	EPriority	mPriority;
	int			mLowPrioritySizeLimit;
	
	bool		mAllowDownsample;
	int			mPadding;
	
	
	LLPluginProcessParent::ptr_t mPlugin;
	
	LLRect mDirtyRect;
	
	std::string translateModifiers(MASK modifiers);
	
	std::string mCursorName;
	int			mLastMouseX;
	int			mLastMouseY;

	LLPluginClassMediaOwner::EMediaStatus mStatus;
	
	F64				mSleepTime;

	bool			mCanUndo;
	bool			mCanRedo;
	bool			mCanCut;
	bool			mCanCopy;
	bool			mCanPaste;
	bool			mCanDoDelete;
	bool			mCanSelectAll;
	
	std::string		mMediaName;
	std::string		mMediaDescription;
	
	LLColor4		mBackgroundColor;
	
	std::string		mTarget;
	
	/////////////////////////////////////////
	// media_browser class
	std::string		mNavigateURI;
	S32				mNavigateResultCode;
	std::string		mNavigateResultString;
	bool			mHistoryBackAvailable;
	bool			mHistoryForwardAvailable;
	std::string		mStatusText;
	int				mProgressPercent;
	std::string		mLocation;
	std::string		mClickURL;
	std::string		mClickNavType;
	std::string		mClickTarget;
	std::string		mClickUUID;
	bool			mClickEnforceTarget;
	std::string		mOverrideClickTarget;
	std::string		mDebugMessageText;
	std::string		mDebugMessageLevel;
	S32				mGeometryX;
	S32				mGeometryY;
	S32				mGeometryWidth;
	S32				mGeometryHeight;
	S32				mStatusCode;
	std::string		mAuthURL;
	std::string		mAuthRealm;
	std::string		mHoverText;
	std::string		mHoverLink;
	std::string     mFileDownloadFilename;
	bool			mIsMultipleFilePick;
	
	/////////////////////////////////////////
	// media_time class
	F64				mCurrentTime;
	F64				mDuration;
	F64				mCurrentRate;
	F64				mLoadedDuration;
	
//--------------------------------------
	//debug use only
	//
private:
	bool  mDeleteOK ;
public:
	void setDeleteOK(bool flag) { mDeleteOK = flag ;}
//--------------------------------------
};

#endif // LL_LLPLUGINCLASSMEDIA_H
