/* +---------------------------------------------------------------------------+
   |          The Mobile Robot Programming Toolkit (MRPT) C++ library          |
   |                                                                           |
   |                   http://mrpt.sourceforge.net/                            |
   |                                                                           |
   |   Copyright (C) 2005-2008  University of Malaga                           |
   |                                                                           |
   |    This software was written by the Perception and Robotics               |
   |      research group, University of Malaga (Spain).                        |
   |    Contact: Jose-Luis Blanco  <jlblanco@ctima.uma.es>                     |
   |                                                                           |
   |  This file is part of the MRPT project.                                   |
   |                                                                           |
   |     MRPT is free software: you can redistribute it and/or modify          |
   |     it under the terms of the GNU General Public License as published by  |
   |     the Free Software Foundation, either version 3 of the License, or     |
   |     (at your option) any later version.                                   |
   |                                                                           |
   |   MRPT 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 General Public License for more details.                          |
   |                                                                           |
   |     You should have received a copy of the GNU General Public License     |
   |     along with MRPT.  If not, see <http://www.gnu.org/licenses/>.         |
   |                                                                           |
   +---------------------------------------------------------------------------+ */
#ifndef  MRPT_WX_SUBSYSTEM_H
#define  MRPT_WX_SUBSYSTEM_H

#include <MRPT/UTILS/utils_defs.h>
#include <MRPT/config.h>
#include <MRPT/UTILS/SYNCH.h>

#include <map>
#include <queue>

#if MRPT_HAS_WXWIDGETS

#include <wx/sizer.h>
#include <wx/statbmp.h>
#include <wx/menu.h>
#include <wx/toolbar.h>
#include <wx/frame.h>
#include <wx/timer.h>
#include <wx/statusbr.h>
#include <wx/msgdlg.h>
#include <wx/artprov.h>
#include <wx/bitmap.h>
#include <wx/intl.h>
#include <wx/image.h>
#include <wx/string.h>
#include <wx/msgdlg.h>
#include <wx/filedlg.h>
#include <wx/progdlg.h>
#include <wx/imaglist.h>
#include <wx/busyinfo.h>
#include <wx/log.h>
#include <wx/textdlg.h>
#include <wx/dirdlg.h>
#include <wx/colordlg.h>
#include <wx/dcmemory.h>
#include <wx/app.h>
#include <wx/pen.h>

// The wxMathPlot library
#include <mathplot.h>

#endif


class CMyGLCanvas;


namespace UTILS
{
    class CDisplayWindow;
    class CDisplayWindow3D;
    class CDisplayWindowPlots;

	/** This class implements the GUI thread required for the wxWidgets-based GUI.
	  *  This system is employed internally by UTILS::CDisplayWindow and UTILS::CDisplayWindow3D, and must be not used in any way directly by the MRPT user.
	  *
	  *  The system works by creating a invisible wxFrame that process timer events where it checks a queue of requests sent from the main MRPT thread. The
	  *   requests include the creation, deletion,... of windows (2D/3D). In that way, just one thread is required for all the GUI windows, and the wxWidgets
	  *   is initialized and clean-up correctly.
	  *
	  *  This header should be included just from the implementation files of CDisplayWindow and CDisplayWindow3D, since it uses wxWidgets classes.
	  *
	  *  \sa UTILS::CDisplayWindow, UTILS::CDisplayWindow3D
	  */
	class WxSubsystem
	{
#if MRPT_HAS_WXWIDGETS

    public:

		/** This method must be called in the destructor of the user class FROM THE MAIN THREAD, in order to wait for the shutdown of the wx thread if this was the last open window.
		  */
		static void waitWxShutdownsIfNoWindows();

        /** The main frame of the wxWidgets application
          */
        class CWXMainFrame: public wxFrame
        {
        	//friend  void WxSubsystem::waitWxShutdownsIfNoWindows();
			friend  void waitWxShutdownsIfNoWindows();

            public:
                CWXMainFrame(wxWindow* parent,wxWindowID id = -1);
                virtual ~CWXMainFrame();

                /** Atomically increments the number of windows created with the main frame as parent.
                  * \return The updated number of windows.
                  */
                static int notifyWindowCreation();

                /** Atomically decrements the number of windows created with the main frame as parent.
                  * \return The updated number of windows (0 if the calling was the last one).
                  */
                static int notifyWindowDestruction();

            //private:
                static SYNCH::CCriticalSection     cs_windowCount;
                static int                         m_windowCount;
			private:

                wxTimer                         *m_theTimer;

                void OnTimerProcessRequests(wxTimerEvent& event);

                DECLARE_EVENT_TABLE()

        }; // end class CWXMainFrame

		/** The thread ID of wxMainThread, or 0 if it is not running.
		  */
		static TThreadHandle  m_wxMainThreadId;

		/** This is signaled when wxMainThread is ready.
		  */
		static SYNCH::CSemaphore m_semWxMainThreadReady;

		/** The critical section for accessing "m_wxMainThreadId"
		  */
		static SYNCH::CCriticalSection m_csWxMainThreadId;

		/**  This will be the "MAIN" of wxWidgets: It starts an application object and does not end until all the windows are closed.
		  *   Only one instance of this thread can be running at a given instant, no matter how many windows are open.
		  */
		static void		wxMainThread(void *);

        /** The data structure for each inter-thread request:
          */
        struct TRequestToWxMainThread
        {
        	TRequestToWxMainThread()
        	{
        		source2D=NULL;
        		source3D=NULL;
        		sourcePlots=NULL;
        	}

            /** Only one source2D,source3D or sourcePlots can be non-NULL, indicating the class that generated the request.
              */
            UTILS::CDisplayWindow    *source2D;

            /** Only one source2D,source3D or sourcePlots can be non-NULL, indicating the class that generated the request.
              */
            UTILS::CDisplayWindow3D  *source3D;

            /** Only one source2D,source3D or sourcePlots can be non-NULL, indicating the class that generated the request.
              */
            UTILS::CDisplayWindowPlots *sourcePlots;

            /** Parameters, depending on OPCODE.
              */
            std::string  str;

            /** Parameters, depending on OPCODE.
              */
            void         *voidPtr, *voidPtr2;
            int          x,y;
            bool         boolVal;
            vector_float vector_x,vector_y;
            std::string  plotName;

            /** Valid codes are:
              *  For CDisplayWindow:
              *     - 200: Create a new 2D window, with caption "str" and initial size "x" & "y", and save the "wxFrame*" in the "void**" passed in voidPtr.
              *     - 201: Updates the image shown in the window, from a "wxImage*" passed in voidPtr2. The wxImage object will be freed with delete after that. voidPtr must be a "wxFrame*", a "CWindowDialog*" actually.
              *     - 202: Set position to x,y
              *     - 203: Change size to x,y
              *     - 204: Change title to "str"
              *     - 299: Delete the window associated with this source object.
              *
              *  For CDisplayWindow3D:
              *     - 300: Create a new 3D window, with caption "str" and initial size "x" & "y", and save the "wxFrame*" in the "void**" passed in voidPtr.
              *     - 302: Set position to x,y
              *     - 303: Change size to x,y
              *     - 304: Change title to "str"
              *     - 399: Delete the window associated with this source object.
              *
              *  For CDisplayWindowPlots:
              *     - 400: Create a new Plots window, with caption "str" and initial size "x" & "y",and save the "wxFrame*" in the "void**" passed in voidPtr.
              *     - 402: Set position to x,y
              *     - 403: Change size to x,y
              *     - 404: Change title to "str"
              *     - 499: Delete the window associated with this source object.
              *		- 410: Depending on "boolVal", enable/disable the mouse-zoom & pan
              *		- 411: Depending on "boolVal", enable/disable the aspect ratio fix
              *		- 412: Zoom over a rectangle vectorx[0-1] & vectory[0-1]
              *		- 413: Axis fit, with aspect ratio fix to boolVal.
              *		- 420: Add/update a 2D line/points plot: x/y data= vector_x/vector_y, format string=str, plot name =plotName.
              *		- 421: Add/update a 2D ellipse: format string=str, plot name =plotName, vector_x[0,1]:X/Y center, vector_x[2]:quantiles, vector_y[0,1,2]: Covariance matrix entries 00,11,01.
              *
              */
            int  OPCODE;

        };

        /** Thread-safe method to return the next pending request, or NULL if there is none (After usage, FREE the memory!)
          */
        static TRequestToWxMainThread  * popPendingWxRequest();

        /** Thread-safe method to insert a new pending request (The memory must be dinamically allocated with "new T[1]", will be freed by receiver.)
          */
        static void pushPendingWxRequest( TRequestToWxMainThread *data );

        /** Thread-safe method to create one single instance of the main wxWidgets thread: it will create the thread only if it is not running yet.
          */
        static bool createOneInstanceMainThread();

        /** An auxiliary function for passing MRPT images to wxWidgets images (The returned object MUST be deleted by the caller!)
          */
        static wxImage *MRPTImage2wxImage( const CMRPTImage &img );


    private:
        /** Do not access directly to this, use the thread-safe functions
          */
        static std::queue<TRequestToWxMainThread*>  listPendingWxRequests;
        static SYNCH::CCriticalSection              cs_listPendingWxRequests;
#endif
	}; // End of class def.


#if MRPT_HAS_WXWIDGETS

	/** The wx dialog for UTILS::CDisplayWindow
	  */
    class CWindowDialog: public wxFrame
    {
        friend class CMyGLCanvas;

        public:
            CWindowDialog( CDisplayWindow *win2D, WxSubsystem::CWXMainFrame* parent,wxWindowID id = -1, const std::string &caption = std::string("[MRPT-CDisplayWindow]"));
            virtual ~CWindowDialog();

            CDisplayWindow *m_win2D;
            WxSubsystem::CWXMainFrame   *m_mainFrame;

            wxStaticBitmap      *m_image;

            static const long         ID_IMAGE_BITMAP;

        private:

            void OnClose (wxCloseEvent& event);
            void OnMenuClose(wxCommandEvent& event);
            void OnMenuAbout(wxCommandEvent& event);
            void OnMenuSave(wxCommandEvent& event);
            void OnChar(wxKeyEvent& event);

            DECLARE_EVENT_TABLE()
    }; // end class CWindowDialog

    class C3DWindowDialog: public wxFrame
    {
        friend class CMyGLCanvas;

        public:

            C3DWindowDialog(CDisplayWindow3D *win3D, WxSubsystem::CWXMainFrame* parent,wxWindowID id = -1, const std::string &caption = std::string("[MRPT-CDisplayWindow3D]"));
            virtual ~C3DWindowDialog();

            CDisplayWindow3D *m_win3D;
            WxSubsystem::CWXMainFrame   *m_mainFrame;

            CMyGLCanvas		*m_canvas;
        private:

            void OnClose (wxCloseEvent& event);
            void OnMenuClose(wxCommandEvent& event);
            void OnMenuAbout(wxCommandEvent& event);

            static const long ID_MENUITEM1;
            static const long ID_MENUITEM2;

            DECLARE_EVENT_TABLE()
    };

	/** The wx dialog for UTILS::CDisplayWindowPlots
	  */
    class CWindowDialogPlots: public wxFrame
    {
        friend class CMyGLCanvas;

        public:
            CWindowDialogPlots( CDisplayWindowPlots *winPlots, WxSubsystem::CWXMainFrame* parent,wxWindowID id = -1, const std::string &caption = std::string("[MRPT-CDisplayWindowPlots]"), wxSize initialSize = wxDefaultSize );
            virtual ~CWindowDialogPlots();

            CDisplayWindowPlots 		*m_winPlots;
            WxSubsystem::CWXMainFrame   *m_mainFrame;

			mpWindow					*m_plot;
            static const long           ID_PLOT;

			/** Redirected from CDisplayWindowPlots::plot
			  */
			void plot(
				const vector_float &x,
				const vector_float &y,
				const std::string  &lineFormat,
				const std::string  &plotName);

			/** Redirected from CDisplayWindowPlots::plotEllipse
			  */
			void plotEllipse(
				const vector_float &x,
				const vector_float &y,
				const std::string  &lineFormat,
				const std::string  &plotName);


        private:

            void OnClose (wxCloseEvent& event);
            void OnMenuClose(wxCommandEvent& event);
            void OnMenuAbout(wxCommandEvent& event);
            void OnChar(wxKeyEvent& event);

            DECLARE_EVENT_TABLE()
    }; // end class CWindowDialog



#ifdef wxUSE_UNICODE
#define _U(x) wxString((x),wxConvUTF8)
#define _UU(x,y) wxString((x),y)
#else
#define _U(x) (x)
#define _UU(x,y) (x)
#endif


#endif

} // End of namespace

#endif
