/*
 * Copyright (C) 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved.
 * Copyright (C) 2008 Collabora Ltd. All rights reserved.
 * Copyright (C) 2009 Girish Ramakrishnan <girish@forwardbias.in>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
 */

#ifndef PluginView_h
#define PluginView_h

#include "FrameLoadRequest.h"
#include "IntRect.h"
#include "MediaCanStartListener.h"
#include "PluginViewBase.h"
#include "ResourceRequest.h"
#include "Timer.h"
#include <wtf/HashMap.h>
#include <wtf/HashSet.h>
#include <wtf/OwnPtr.h>
#include <wtf/PassRefPtr.h>
#include <wtf/RefPtr.h>
#include <wtf/Vector.h>
#include <wtf/text/CString.h>

#if USE(CAIRO)
#include <cairo.h>
#include "RefPtrCairo.h"
#endif

#include "npapi.h"

#if ENABLE(NETSCAPE_PLUGIN_API)
#include "PluginStream.h"
#include "npruntime_internal.h"
#endif

#if OS(WINDOWS) && (PLATFORM(GTK) || PLATFORM(QT) || PLATFORM(WX))
typedef struct HWND__* HWND;
typedef HWND PlatformPluginWidget;
#else
typedef PlatformWidget PlatformPluginWidget;
#if defined(XP_MACOSX) && PLATFORM(QT)
#include <QPixmap>
#endif
#endif
#if PLATFORM(QT)
#if USE(TEXTURE_MAPPER)
#include "TextureMapperPlatformLayer.h"
#endif

#include <QImage>
QT_BEGIN_NAMESPACE
class QPainter;
QT_END_NAMESPACE
#endif
#if PLATFORM(QT) && USE(ACCELERATED_COMPOSITING) && ENABLE(NETSCAPE_PLUGIN_API) && defined(XP_UNIX)
#ifndef WTF_USE_ACCELERATED_COMPOSITING_PLUGIN_LAYER
#define WTF_USE_ACCELERATED_COMPOSITING_PLUGIN_LAYER 1
#endif
#endif
#if PLATFORM(GTK)
typedef struct _GtkSocket GtkSocket;
#endif

#if USE(JSC)
namespace JSC {
    namespace Bindings {
        class Instance;
    }
}
#endif

namespace WebCore {
    class Frame;
    class Image;
    class HTMLPlugInElement;
    class GraphicsLayer;
    class KeyboardEvent;
    class MouseEvent;
    class KURL;
#if OS(WINDOWS) && ENABLE(NETSCAPE_PLUGIN_API)
    class PluginMessageThrottlerWin;
#endif
    class PluginPackage;
    class PluginRequest;
    class PluginStream;
    class ResourceError;
    class ResourceResponse;

    typedef void (*TIMERFUNC)(NPP npp, uint32_t timerID);

    class PluginView;

    class PluginTimer : public RefCounted<PluginTimer> {
    public:
    	static WTF::PassRefPtr<PluginTimer> create(PluginView* pluginView, NPP instance, bool repeat, double interval, TIMERFUNC timerFunc)
    	{
    		return adoptRef(new PluginTimer(pluginView, instance, repeat, interval, timerFunc));
    	}
    		        
        uint32_t timerID() const { return m_timerID; }
        
        void pluginViewDestroyed() { m_pluginView = 0; }

    private:
        PluginTimer(PluginView* pluginView, NPP instance, bool repeat, double interval, TIMERFUNC timerFunc);
        
        void fired(Timer<PluginTimer>*);
        
        PluginView* m_pluginView;
        NPP         m_instance;
        TIMERFUNC   m_timerFunc;
        uint32_t    m_timerID;
        bool        m_repeat;
        double      m_interval;
        int         m_disabledCount;
        
        Timer<PluginTimer> m_timer;
    };

    enum PluginStatus {
        PluginStatusCanNotFindPlugin,
        PluginStatusCanNotLoadPlugin,
        PluginStatusLoadedSuccessfully
    };

    class PluginRequest {
        WTF_MAKE_NONCOPYABLE(PluginRequest); WTF_MAKE_FAST_ALLOCATED;
    public:
        PluginRequest(const FrameLoadRequest& frameLoadRequest, bool sendNotification, void* notifyData, bool shouldAllowPopups)
            : m_frameLoadRequest(frameLoadRequest)
            , m_notifyData(notifyData)
            , m_sendNotification(sendNotification)
            , m_shouldAllowPopups(shouldAllowPopups) { }
    public:
        const FrameLoadRequest& frameLoadRequest() const { return m_frameLoadRequest; }
        void* notifyData() const { return m_notifyData; }
        bool sendNotification() const { return m_sendNotification; }
        bool shouldAllowPopups() const { return m_shouldAllowPopups; }
        void setHTTPReferrer(const String& httpReferrer) { m_frameLoadRequest.resourceRequest().setHTTPReferrer(httpReferrer); }

    private:
        FrameLoadRequest m_frameLoadRequest;
        void* m_notifyData;
        bool m_sendNotification;
        bool m_shouldAllowPopups;
    };

    class PluginManualLoader {
    public:
        virtual ~PluginManualLoader() {}
        virtual void didReceiveResponse(const ResourceResponse&) = 0;
        virtual void didReceiveData(const char*, int) = 0;
        virtual void didFinishLoading() = 0;
        virtual void didFail(const ResourceError&) = 0;
    };

    class PluginView : public PluginViewBase
#if ENABLE(NETSCAPE_PLUGIN_API)
                     , private PluginStreamClient
#endif
                     , public PluginManualLoader
                     , private MediaCanStartListener{
    	
    	friend class PluginViewAudio;
    public:
        static PassRefPtr<PluginView> create(Frame* parentFrame, const IntSize&, HTMLPlugInElement*, const KURL&, const Vector<String>& paramNames, const Vector<String>& paramValues, const String& mimeType, bool loadManually);
        virtual ~PluginView();

        PluginPackage* plugin() const { return m_plugin.get(); }
#if ENABLE(NETSCAPE_PLUGIN_API)
        NPP instance() const { return m_instance; }
#endif

        void setNPWindowRect(const IntRect&);
        static PluginView* currentPluginView();

#if ENABLE(NETSCAPE_PLUGIN_API)
        NPObject* npObject();
#endif
#if USE(JSC)
        PassRefPtr<JSC::Bindings::Instance> bindingInstance();
#endif

        PluginStatus status() const { return m_status; }

#if ENABLE(NETSCAPE_PLUGIN_API)
        // NPN functions
        NPError getURLNotify(const char* url, const char* target, void* notifyData);
        NPError getURL(const char* url, const char* target);
        NPError postURLNotify(const char* url, const char* target, uint32_t len, const char* but, NPBool file, void* notifyData);
        NPError postURL(const char* url, const char* target, uint32_t len, const char* but, NPBool file);
        NPError newStream(NPMIMEType type, const char* target, NPStream** stream);
        int32_t write(NPStream* stream, int32_t len, void* buffer);
        NPError destroyStream(NPStream* stream, NPReason reason);
#endif
        const char* userAgent();
#if ENABLE(NETSCAPE_PLUGIN_API)
        static const char* userAgentStatic();
#endif
        void status(const char* message);
        
#if ENABLE(NETSCAPE_PLUGIN_API)
        NPError getValue(NPNVariable variable, void* value);
        static NPError getValueStatic(NPNVariable variable, void* value);
        NPError setValue(NPPVariable variable, void* value);
        NPError getValueForURL(NPNURLVariable variable, const char* url, char** value, uint32_t* len);
        NPError setValueForURL(NPNURLVariable variable, const char* url, const char* value, uint32_t len);
        NPError getAuthenticationInfo(const char* protocol, const char* host, int32_t port, const char* scheme, const char* realm, char** username, uint32_t* ulen, char** password, uint32_t* plen);
        void invalidateRect(NPRect*);
        void invalidateRegion(NPRegion);
#endif
        void forceRedraw();
        void pushPopupsEnabledState(bool state);
        void popPopupsEnabledState();

        virtual void invalidateRect(const IntRect&);

        virtual void attributeChanged(const QualifiedName&, const AtomicString&) { }

        bool arePopupsAllowed() const;

        void setJavaScriptPaused(bool);

        void privateBrowsingStateChanged(bool);

        void notifyStreamDidFinishLoading(PluginStream* stream);
        
        void disconnectStream(PluginStream*);        
        void streamDidFinishLoading(PluginStream* stream)
        {
        	notifyStreamDidFinishLoading(stream);
        	disconnectStream(stream);
        }
        
        // soft keyboard
        void notifySoftKeyboardInput(const String&);
        
        // Widget functions
        virtual void setFrameRect(const IntRect&);
        virtual void frameRectsChanged();
        virtual void setFocus(bool);
        virtual void show();
        virtual void hide();
        virtual void paint(GraphicsContext*, const IntRect&);

        // This method is used by plugins on all platforms to obtain a clip rect that includes clips set by WebCore,
        // e.g., in overflow:auto sections.  The clip rects coordinates are in the containing window's coordinate space.
        // This clip includes any clips that the widget itself sets up for its children.
        IntRect windowClipRect() const;

        virtual void handleEvent(Event*);
        virtual void setParent(ScrollView*);
        virtual void setParentVisible(bool);

        virtual bool isPluginView() const { return true; }

        Frame* parentFrame() const { return m_parentFrame.get(); }

        void focusPluginElement();

        const String& pluginsPage() const { return m_pluginsPage; }
        const String& mimeType() const { return m_mimeType; }
        const KURL& url() const { return m_url; }

#if OS(WINDOWS) && ENABLE(NETSCAPE_PLUGIN_API)
        static LRESULT CALLBACK PluginViewWndProc(HWND, UINT, WPARAM, LPARAM);
        LRESULT wndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
        WNDPROC pluginWndProc() const { return m_pluginWndProc; }
#endif

        // Used for manual loading
        void didReceiveResponse(const ResourceResponse&);
        void didReceiveData(const char*, int);
        void didFinishLoading();
        void didFail(const ResourceError&);

        static bool isCallingPlugin();
    	
    private:        
    	bool m_fullscreen : 1;
    	bool m_skipOn2 : 1;
    	bool m_skipSorenson : 1;
    	
    	void exitFullScreenMode();
    	
    	RefPtr<Image>  m_on2CodecImage;
    	RefPtr<Image>  m_sorensonCodecImage;
    	
    	uint32_t m_codecType;
    	void paintCodecTypeIcon(GraphicsContext* context, const IntRect& rect);
    	
    public:
        // NPN Timers for Flash
        uint32_t scheduleTimer(NPP instance, uint32_t interval, bool repeat, void (*timerFunc)(NPP instance, uint32_t timerID));
        void unscheduleTimer(NPP instance, uint32_t timerID);
        
        bool start();
        void stop();

#if ENABLE(NETSCAPE_PLUGIN_API)
        static void keepAlive(NPP);
#endif
        void keepAlive();

#if USE(ACCELERATED_COMPOSITING)
#if USE(ACCELERATED_COMPOSITING_PLUGIN_LAYER)
        virtual PlatformLayer* platformLayer() const;
        bool shouldUseAcceleratedCompositing() const;
#else
        virtual PlatformLayer* platformLayer() const { return 0; }
#endif
#endif

#if PLATFORM(QT) && ENABLE(NETSCAPE_PLUGIN_API) && defined(XP_UNIX)
        // PluginViewQt (X11) needs a few workarounds when running under DRT
        static void setIsRunningUnderDRT(bool flag) { s_isRunningUnderDRT = flag; }
#endif

        bool isTransparent() const { return m_isTransparent; }
        bool isVideoPunchThrough() const { return m_isVideoPunchThrough; }

    	const IntRect& screenBounds() const { return m_screenBounds; }

#if USE(ACCELERATED_COMPOSITING)
        // only for comparison
        GraphicsLayer* graphicsLayer() const { return m_graphicsLayer; }
#endif

    private:
        PluginView(Frame* parentFrame, const IntSize&, PluginPackage*, HTMLPlugInElement*, const KURL&, const Vector<String>& paramNames, const Vector<String>& paramValues, const String& mimeType, bool loadManually);

        void notifyBeforeNPPNew();
        void notifyNPPNew(NPError npErr);
        void notifyNPPDestroy(NPError npErr);
        void notifyScheduleRequest(PluginRequest*);

        void log(const String&);
        
        bool handleGetURL(const char* url, const char* target);
        
        void setParameters(const Vector<String>& paramNames, const Vector<String>& paramValues);
        bool startOrAddToUnstartedList();
        void init();
        bool platformStart();
        void platformDestroy();
        static void setCurrentPluginView(PluginView*);
        NPError load(const FrameLoadRequest&, bool sendNotification, void* notifyData);
        NPError handlePost(const char* url, const char* target, uint32_t len, const char* buf, bool file, void* notifyData, bool sendNotification, bool allowHeaders);
        NPError handlePostReadFile(Vector<char>& buffer, uint32_t len, const char* buf);
        static void freeStringArray(char** stringArray, int length);
        void setCallingPlugin(bool) const;

        void invalidateWindowlessPluginRect(const IntRect&);

        virtual void mediaCanStart();

#if OS(WINDOWS) && ENABLE(NETSCAPE_PLUGIN_API)
        void paintWindowedPluginIntoContext(GraphicsContext*, const IntRect&);
        static HDC WINAPI hookedBeginPaint(HWND, PAINTSTRUCT*);
        static BOOL WINAPI hookedEndPaint(HWND, const PAINTSTRUCT*);
#endif

#if ENABLE(NETSCAPE_PLUGIN_API)
        static bool platformGetValueStatic(NPNVariable variable, void* value, NPError* result);
        bool platformGetValue(NPNVariable variable, void* value, NPError* result);
#endif

        RefPtr<Frame> m_parentFrame;
        RefPtr<PluginPackage> m_plugin;
        HTMLPlugInElement* m_element;
        bool m_isStarted;
        KURL m_url;
        PluginStatus m_status;
        Vector<IntRect> m_invalidRects;

        void performRequest(PluginRequest*);
        void scheduleRequest(PluginRequest*);
        void requestTimerFired(Timer<PluginView>*);
        void invalidateTimerFired(Timer<PluginView>*);
        Timer<PluginView> m_requestTimer;
        Timer<PluginView> m_invalidateTimer;

        void popPopupsStateTimerFired(Timer<PluginView>*);
        Timer<PluginView> m_popPopupsStateTimer;

        // NPN Timers for Flash
        Vector< RefPtr<PluginTimer> > m_timers;
        
        void lifeSupportTimerFired(Timer<PluginView>*);
        Timer<PluginView> m_lifeSupportTimer;

#ifndef NP_NO_CARBON
#if ENABLE(NETSCAPE_PLUGIN_API)
        bool dispatchNPEvent(NPEvent&);
#endif // ENABLE(NETSCAPE_PLUGIN_API)
#endif
        void updatePluginWidget();
        //void paintMissingPluginIcon(GraphicsContext*, const IntRect&);

        void handleKeyboardEvent(KeyboardEvent*);
        void handleMouseEvent(MouseEvent*);
#if defined(XP_UNIX) && ENABLE(NETSCAPE_PLUGIN_API)
        void handleFocusInEvent();
        void handleFocusOutEvent();
#endif

#if OS(WINDOWS)
        void paintIntoTransformedContext(HDC);
        PassRefPtr<Image> snapshot();
#endif

        int m_mode;
        int m_paramCount;
        char** m_paramNames;
        char** m_paramValues;
        String m_pluginsPage;

        String m_mimeType;
        WTF::CString m_userAgent;

        NPP m_instance;
        NPP_t m_instanceStruct;
        NPWindow m_npWindow;

        Vector<bool, 4> m_popupStateStack;

#if ENABLE(NETSCAPE_PLUGIN_API)
        HashSet<RefPtr<PluginStream> > m_streams;
#endif
        Vector<PluginRequest*> m_requests;
        HashSet<String> m_loadingURLs;

        bool m_isWindowed;
        bool m_isTransparent;
        bool m_isVideoPunchThrough;
        bool m_haveInitialized;
        bool m_isWaitingToStart;

    	IntRect m_screenBounds;

#if USE(ACCELERATED_COMPOSITING)
        GraphicsLayer* m_graphicsLayer;
#endif

#if defined(XP_UNIX)
        bool m_needsXEmbed;
#endif

#if OS(WINDOWS) && ENABLE(NETSCAPE_PLUGIN_API)
        OwnPtr<PluginMessageThrottlerWin> m_messageThrottler;
        WNDPROC m_pluginWndProc;
        unsigned m_lastMessage;
        bool m_isCallingPluginWndProc;
        HDC m_wmPrintHDC;
        bool m_haveUpdatedPluginWidget;
#endif

#if ((PLATFORM(GTK) || PLATFORM(QT) || PLATFORM(WX)) && OS(WINDOWS)) || defined(XP_MACOSX) || PLATFORM(EFL)
        // On Mac OSX and Qt/Windows the plugin does not have its own native widget,
        // but is using the containing window as its reference for positioning/painting.
        PlatformPluginWidget m_window;
public:
        PlatformPluginWidget platformPluginWidget() const { return m_window; }
        void setPlatformPluginWidget(PlatformPluginWidget widget) { m_window = widget; }
#else
public:
        void setPlatformPluginWidget(PlatformPluginWidget widget) { setPlatformWidget(widget); }
        PlatformPluginWidget platformPluginWidget() const { return platformWidget(); }
#endif

private:

#if defined(XP_UNIX) || PLATFORM(GTK)
        void setNPWindowIfNeeded();
#elif defined(XP_MACOSX)
        NP_CGContext m_npCgContext;
        OwnPtr<Timer<PluginView> > m_nullEventTimer;
        NPDrawingModel m_drawingModel;
        NPEventModel m_eventModel;
        CGContextRef m_contextRef;
        WindowRef m_fakeWindow;
#if PLATFORM(QT)
        QPixmap m_pixmap;
#endif

        Point m_lastMousePos;
        void setNPWindowIfNeeded();
        void nullEventTimerFired(Timer<PluginView>*);
        Point globalMousePosForPlugin() const;
        Point mousePosForPlugin(MouseEvent* event = 0) const;
#endif

#if defined(HAS_X11) && HAS_X11 && defined(XP_UNIX) && ENABLE(NETSCAPE_PLUGIN_API)
        bool m_hasPendingGeometryChange;
        Pixmap m_drawable;
        Visual* m_visual;
        Colormap m_colormap;
        Display* m_pluginDisplay;

        void initXEvent(XEvent* event);
#endif

#if PLATFORM(QT)
#if defined(XP_UNIX) && ENABLE(NETSCAPE_PLUGIN_API)
        static bool s_isRunningUnderDRT;
        static void setXKeyEventSpecificFields(XEvent*, KeyboardEvent*);
        void paintUsingXPixmap(QPainter* painter, const QRect &exposedRect);
        QWebPageClient* platformPageClient() const;
#endif
#if USE(ACCELERATED_COMPOSITING_PLUGIN_LAYER)
        OwnPtr<PlatformLayer> m_platformLayer;
        friend class PluginGraphicsLayerQt;
#endif // USE(ACCELERATED_COMPOSITING_PLUGIN_LAYER)
#endif // PLATFORM(QT)

#if PLATFORM(GTK) && !PLATFORM(ESPIAL)
        static gboolean plugRemovedCallback(GtkSocket*, PluginView*);
        static void plugAddedCallback(GtkSocket*, PluginView*);
        void updateWidgetAllocationAndClip();
        bool m_plugAdded;
        IntRect m_delayedAllocation;
#endif

public:		
        IntRect m_clipRect; // The clip rect to apply to a windowed plug-in
        IntRect m_windowRect; // Our window rect.

        bool m_loadManually;
#if ENABLE(NETSCAPE_PLUGIN_API)
        RefPtr<PluginStream> m_manualStream;
#endif

        bool m_isJavaScriptPaused;

        bool m_haveCalledSetWindow;

        static PluginView* s_currentPluginView;
        
#if PLATFORM(ESPIAL)
	public:
		virtual bool hasPluginError() const { return m_hasPluginError; }	
		typedef int (*LOG_FUNC)(const char * format, ... );	
		LOG_FUNC logFunc() const { return m_logFunc; }

    private:
        bool m_hasPluginError;        
        int m_pluginWarningType;
        
        LOG_FUNC m_logFunc;
                    
        void handleErrorTimerFired(Timer<PluginView>*);     
        Timer<PluginView> m_errorTimer;
        
        bool allocateSurfaceData(int width, int height);
        
        Vector<unsigned char> m_surfaceData;
        int m_surfaceWidth;
        int m_surfaceHeight;
        int m_surfaceStride;

    	void* m_webView;
#endif
    	
    };

} // namespace WebCore

#endif
