/***************************************************************************
 *   Copyright (C) 2007 by Vadim Lopatin   *
 *   vadim.lopatin@coolreader.org   *
 *                                                                         *
 *   This program 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 2 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 General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/
#ifndef CR_GUI_INCLUDED
#define CR_GUI_INCLUDED

#include "lvtypes.h"
#include "lvstring.h"
#include "lvptrvec.h"
#include "lvdrawbuf.h"
#include "lvdocview.h"
#include "crskin.h"

#ifdef CR_WX_SUPPORT
#include <wx/wx.h>
#endif

class CRGUIWindowManager;

#define KEY_FLAG_LONG_PRESS 1

/// Accelerator table entry: to convert keypress to command
class CRGUIAccelerator
{
    public:
        int keyCode;
        int keyFlags;
        int commandId;
        int commandParam;
};

/// accelerator table
class CRGUIAcceleratorTable
{
protected:
    LVPtrVector<CRGUIAccelerator> _items;
    int indexOf( int keyCode, int keyFlags )
    {
        //CRLog::trace( "indexOf( %d, %d )", (int)keyCode, (int)keyFlags );
        for ( int i=0; i<_items.length(); i++ ) {
            //CRLog::trace( " compare( %d, %d )", (int)_items[i]->keyCode, (int)_items[i]->keyFlags );
            if ( _items[i]->keyCode==keyCode && _items[i]->keyFlags==keyFlags ) {
                //CRLog::trace(" found index = %d", i);
                return i;
            }
        }
        return -1;
    }
public:
	/// find key by command
    bool findCommandKey( int cmd, int param, int & keyCode, int & keyFlags )
    {
        //CRLog::trace( "indexOf( %d, %d )", (int)keyCode, (int)keyFlags );
        for ( int i=0; i<_items.length(); i++ ) {
            //CRLog::trace( " compare( %d, %d )", (int)_items[i]->keyCode, (int)_items[i]->keyFlags );
			if ( _items[i]->commandId==cmd && _items[i]->commandParam==param ) {
                //CRLog::trace(" found index = %d", i);
				keyCode = _items[i]->keyCode;
				keyFlags = _items[i]->keyFlags;
				return true;
            }
        }
        return false;
    }
	/// get item by index
	const CRGUIAccelerator * get( int index ) const
	{
		return _items[ index ];
	}

	/// debug dump of table
    void dump()
    {
        if ( CRLog::isTraceEnabled() ) {
#if 0
            CRLog::trace("Accelerator table:");
            for ( int i=0; i<_items.length(); i++ ) {
                CRGUIAccelerator * p = _items[i];
                CRLog::trace("%d, %d => %d, %d\n", p->keyCode, p->keyFlags, p->commandId, p->commandParam);
            }
#endif
        }
    }
	/// returns number of entries
    int length() { return _items.length(); }
    /// remove accelerator from table
    bool remove( int keyCode, int keyFlags )
    {
        int index = indexOf( keyCode, keyFlags );
        if ( index >= 0 ) {
            _items.erase( index, 1 );
            return true;
        }
        return false;
    }

    /// add accelerator to table or change existing
    bool add( int keyCode, int keyFlags, int commandId, int commandParam );

	/// add all items from another table
	void addAll( const CRGUIAcceleratorTable & v );

    /// translate keycode to command, returns true if translated
    bool translate( int keyCode, int keyFlags, int & commandId, int & commandParam )
    {
        int index = indexOf( keyCode, keyFlags );
        if ( index<0 )
            return false;
        commandId = _items[index]->commandId;
        commandParam = _items[index]->commandParam;
        return true;
    }
    /// translate keycode to command, returns true if translated
    const CRGUIAccelerator * findKeyAccelerator( int keyCode, int keyFlags )
    {
        int index = indexOf( keyCode, keyFlags );
        if ( index<0 )
            return NULL;
        return get(index);
    }
    /// empty table constructor
    CRGUIAcceleratorTable() { }
    /// copy constructor
    CRGUIAcceleratorTable( const CRGUIAcceleratorTable& v) 
	{
		for ( int i=0; i<v._items.length(); i++ ) {
			_items.add( new CRGUIAccelerator(*v._items[i]) );
		}
	}
    /// constructor from int array: 4 ints per entry (keyCode, keyFlags, commandId, commandParam), keyCode==0 indicates end of list 
    CRGUIAcceleratorTable( const int * tableQuadsArray )
    {
        while( *tableQuadsArray ) {
            CRGUIAccelerator * item = new CRGUIAccelerator();
            item->keyCode = *tableQuadsArray++;
            item->keyFlags = *tableQuadsArray++;
            item->commandId = *tableQuadsArray++;
            item->commandParam = *tableQuadsArray++;
            _items.add(item);
        }
    }
};

/// accelerator table reference
typedef LVRef<CRGUIAcceleratorTable> CRGUIAcceleratorTableRef;

/**
 * \brief Container for list of named accelerator tables read from files
 *
 * File format:
 * there are two files: 
 *   1) definition file which has key code and command id definitions
 *   2) keymap file which has several key->command tables
 *
 * definition file format:
 *   # there may be comment lines started with # character
 *   other lines should be in format
 *   identifier=value
 *   where identifier is alphanumeric identifier to name value
 *   value is either decimal number, hex number with 0x prefix, or character in ''
 * example: 
 *
 * # this is sample definition file
 * XK_Return=0xFF01
 * KEY_1='1'
 * KEY_SPACE=32
 * CMD_CLOSE=100
 *
 * keymap file format:
 *   # there may be comment lines started with # character
 *   file should be divided into sections using lines like
 *   [sectionname]
 *   other lines should be in format
 *   key[,keyflags]=cmd[,cmdparam]
 *   (keyflags and cmdparam are optional)
 *   key, keyflags, cmd, and cmdparam should be either
 *   identifier defined in definition file, decimal number, hex number with 0x prefix, or character in ''
 *   it's better to avoid constants in this file, instead just place them to definition file and use by identifiers
 *
 * example:
 *
 * # this is sample keymap file
 * [mainwindow]
 * #this is keymap table for main window, accessible by name "mainwindow"
 * XK_Return=CMD_SHOW_MENU, 0
 * XK_Return,LONG=CMD_SHOW_MENU_SETTINGS
 * XK_Down,LONG=CMD_MOVE_FORWARD, 10
 * '1'=CMD_GO_TO_PAGE
 *
 */
class CRGUIAcceleratorTableList
{
private:
    LVHashTable<lString16, CRGUIAcceleratorTableRef> _table;
public:
	/// add all tables
	void addAll( const CRGUIAcceleratorTableList & v );
    /// remove all tables
    void clear() { _table.clear(); }
    /// add accelerator table definition from array
    void add( const char * name, const int * defs )
    {
        add( lString16( name ), defs );
    }
    /// add accelerator table definition from array
    void add( const lString16 & name, const int * defs )
    {
        _table.set( name, CRGUIAcceleratorTableRef( new CRGUIAcceleratorTable( defs ) ) );
    }
    /// find accelerator table by name
    CRGUIAcceleratorTableRef get( const lString16 & name ) { return _table.get( name ); }
    CRGUIAcceleratorTableRef get( const lString16 & name, CRPropRef keyRemappingOptions );
    /// find accelerator table by name
    CRGUIAcceleratorTableRef get( const char * name ) { return _table.get( lString16( name ) ); }
    /// returns true if there are no no tables in list
    bool empty() { return _table.length()==0; }
    /// constructs empty list, then it should be filled from file using openFromFile()
    CRGUIAcceleratorTableList() : _table( 32 )
    {
    }
    ~CRGUIAcceleratorTableList() { }
    /// reads definitions from files
    bool openFromFile( const char  * defFile, const char * mapFile );
};

class CRKeyboardLayout
{
	lString16Collection _items;
public:
	CRKeyboardLayout() { }
	const lString16Collection & getItems() { return _items; }
	lString16 get( int i )
	{
		if ( i<0 || i>= (int)_items.length() )
            return lString16::empty_str;
		return _items[i];
	}
	void set( int index, lString16 chars )
	{
		if ( index<0 || index>20 )
			return;
		while ( (int)_items.length() <= index )
            _items.add(lString16::empty_str);
		_items[ index ] = chars;
	}
};

class CRKeyboardLayoutSet
{
public:
	lString16 name;
	LVRef<CRKeyboardLayout> vKeyboard;
	LVRef<CRKeyboardLayout> tXKeyboard;
	CRKeyboardLayoutSet()
		: vKeyboard( new CRKeyboardLayout() ), tXKeyboard( new CRKeyboardLayout() )
	{
	}
	CRKeyboardLayoutSet( const CRKeyboardLayoutSet & v )
		: name( v.name), vKeyboard( v.vKeyboard ), tXKeyboard( v.tXKeyboard )
	{
	}
	CRKeyboardLayoutSet & operator = ( const CRKeyboardLayoutSet & v )
	{
		name = v.name;
		vKeyboard = v.vKeyboard;
		tXKeyboard = v.tXKeyboard;
        return *this;
	}
};

typedef LVRef<CRKeyboardLayoutSet> CRKeyboardLayoutRef;

class CRKeyboardLayoutList
{
    LVHashTable<lString16, CRKeyboardLayoutRef> _table;
	CRKeyboardLayoutRef _current;
public:
	// get currently set layout
	CRKeyboardLayoutRef getCurrentLayout();
	// get next layout
	CRKeyboardLayoutRef nextLayout();
	// get previous layout
	CRKeyboardLayoutRef prevLayout();

	CRKeyboardLayoutRef get( lString16 name ) { return _table.get( name ); }
	void set( lString16 name, CRKeyboardLayoutRef v ) { _table.set( name, v ); }
	CRKeyboardLayoutList() : _table(16) { }
    /// reads definitions from files
    bool openFromFile( const char  * layoutFile );
};

/// i18n support interface
class CRGUIStringTranslator
{
public:
    /// translate string by key, return default value if not found
    virtual lString16 translateString( const char *, const char * defValue )
    {
        return Utf8ToUnicode( lString8(defValue) );
    }
    virtual ~CRGUIStringTranslator() { }
};

enum CRGUIEventType {
    CREV_WINDOW_EVENTS_START=1,
    CREV_KEYDOWN = 1,
    CREV_KEYUP,
    CREV_COMMAND,

    CREV_WM_EVENTS_START=100,
    CREV_UPDATE = 100,
    CREV_RESIZE

};

class CRGUIWindow;
class CRGUIWindowManager;

class CRGUIEvent
{
protected:
    int _type;
    CRGUIWindow * _targetWindow;
    int _param1;
    int _param2;
public:
    virtual bool isForVisibleOnly() { return false; }
    virtual bool isForModalOnly() { return false; }
    virtual bool isWindowEvent() { return _type<CREV_WM_EVENTS_START; }
    virtual bool isWMEvent() { return _type>=CREV_WM_EVENTS_START; }
    int getType() { return _type; }
    virtual bool handle( CRGUIWindow * window ) { return false; }
    virtual bool handle( CRGUIWindowManager * wm ) { return false; }
    CRGUIEvent & setParam1( int v ) { _param1=v; return *this; }
    CRGUIEvent & setParam2( int v ) { _param2=v; return *this; }
    CRGUIEvent & setTargetWindow( CRGUIWindow * targetWindow ) { _targetWindow = targetWindow; return *this; }
    int getParam1() { return _param1; }
    int getParam2() { return _param2; }
    CRGUIEvent( int type ) : _type(type), _targetWindow(NULL)
    {

    }
    virtual ~CRGUIEvent() { }
};


/// Screen object - provides canvas and interface to device screen
class CRGUIScreen
{
    public:
        // for turbo updates
        enum UpdateMode {
            NormalMode,
            PrepareMode
        };
        virtual void setTurboUpdateEnabled( bool flg ) { }
        virtual bool getTurboUpdateEnabled() {  return false; }
        virtual bool getTurboUpdateSupported() {  return false; }
        virtual void setTurboUpdateMode( UpdateMode mode ) { }
        /// fast update feature parameter setting
        virtual void setFullUpdateInterval( int pagesBeforeFullupdate=1 ) = 0;
        /// creates compatible canvas of specified size
        virtual LVDrawBuf * createCanvas( int dx, int dy ) = 0;
        /// sets new screen size, returns true if size is changed
        virtual bool setSize( int dx, int dy ) = 0;
        /// returns screen width
        virtual int getWidth() = 0;
        /// returns screen height
        virtual int getHeight() = 0;
        /// returns screen dimension
        virtual lvRect getRect() { return lvRect(0, 0, getWidth(), getHeight() ); }
        /// return pointer to screen canvas
        virtual LVRef<LVDrawBuf> getCanvas() = 0;
        /// draw image on screen canvas
        virtual void draw( LVDrawBuf * img, int x = 0, int y = 0) = 0;
        /// transfers contents of buffer to device, if full==true, redraws whole screen, otherwise only changed area
        virtual void flush( bool full ) = 0;
        /// invalidates rectangle: add it to bounding box of next partial update
        virtual void invalidateRect( const lvRect & rc ) { }
        virtual ~CRGUIScreen() { }
};

/// window configure flag, on screen size change
#define CRGUI_CONFIGURE_FLAG_SCREEN_SIZE 1
/// window configure flag, on screen orientation change
#define CRGUI_CONFIGURE_FLAG_SCREEN_ORIENTATION 2

/// Window interface
class CRGUIWindow
{
    public:
        /// override to handle
        virtual bool handleEvent( CRGUIEvent * event )
        {
            if ( !event->isWindowEvent() )
                return false;
            // by default, allow event to do something with window
            return event->handle( this );
        }
        /// sets scroll label (e.g. "Page $1 of $2" or "$1 / $2")
        virtual void setScrollLabelTemplate( lString16 text ) = 0;
        /// returns scroll label (e.g. "$1 of $2")
        virtual lString16 getScrollLabelTemplate() = 0;
        /// sets skin name for window
        virtual void setSkinName( const lString16  & skin ) = 0;
        /// returns skin name for window
        virtual lString16 getSkinName() = 0;
        /// set accelerator table for window
        virtual void setAccelerators( CRGUIAcceleratorTableRef ) { }
        /// get window accelerator table
        virtual CRGUIAcceleratorTableRef getAccelerators() { return CRGUIAcceleratorTableRef(); }
        /// returns true if key is processed
        virtual bool onKeyPressed( int key, int flags = 0 ) = 0;
        /// returns true if command is processed
        virtual bool onCommand( int command, int params = 0 ) = 0;
        /// returns true if window is visible
        virtual bool isVisible() const = 0;
        /// returns true if window is fullscreen
        virtual bool isFullscreen() = 0;
        /// returns true if window is changed but now drawn
        virtual bool isDirty() = 0;
        /// sets dirty flag
        virtual void setDirty() = 0;
        /// shows or hides window
        virtual void setVisible( bool visible ) = 0;
        /// called on system configuration change: screen size and orientation
        virtual void reconfigure( int flags ) = 0;
        /// returns window rectangle
        virtual const lvRect & getRect() = 0;
        /// sets window rectangle
        virtual void setRect( const lvRect & rc ) = 0;
        /// draws content of window to screen
        virtual void flush() = 0;
        /// called if window gets focus
        virtual void activated() { setDirty(); }
        virtual void reactivated() {}
        /// called if window loss focus
        virtual void covered() { }
        /// called if window is being closed
        virtual void closing() { }
        /// returns window manager
        virtual CRGUIWindowManager * getWindowManager() = 0;
        /// destroys window
        virtual ~CRGUIWindow() { }
};

/// Window manager
class CRGUIWindowManager : public CRGUIStringTranslator
{
    protected:
        LVPtrVector<CRGUIWindow, true> _windows;
        LVPtrVector<CRGUIEvent, true> _events;
        CRGUIScreen * _screen;
        /// if true, we should delete screen in destructor
        bool _ownScreen;
        LVRef<CRGUIStringTranslator> _i18n;
        int _postedCommand;
        int _postedCommandParam;
        time_t _lastProgressUpdate;
        int _lastProgressPercent;
        CRSkinRef _skin;
        CRGUIAcceleratorTableList _accTables;
		CRKeyboardLayoutList _kbLayouts;
        cr_rotate_angle_t _orientation;
        LVRefVec<LVImageSource> m_batteryIcons;
        bool _stopFlag;
    public:
        /// forward events from system queue to application queue
        virtual void forwardSystemEvents( bool waitForEvent ) { }
        /// post application event to message queue
        virtual void postEvent( CRGUIEvent * event );
        /// peeks head of application message queue, w/o removing from queue (returns NULL if no events in queue)
        virtual CRGUIEvent * peekEvent()
        {
            forwardSystemEvents( false );
            return _events.peekHead();
        }
        /// returns head of application message queue, removing from queue (returns NULL if no events in queue)
        virtual CRGUIEvent * getEvent()
        {
            forwardSystemEvents( false );
            return _events.popHead();
        }
        /// handle all events from queue
        virtual bool handleAllEvents( bool waitForEvent );
        /// override to handle
        virtual bool handleEvent( CRGUIEvent * event );
        /// called when message queue is empty and application is going to wait for event
        virtual void idle() { }

        /// returns list of battery icons
        virtual LVRefVec<LVImageSource> & getBatteryIcons() { return m_batteryIcons; }
        /// set list of battery icons to display battery state
        virtual void setBatteryIcons( LVRefVec<LVImageSource> icons )
        {
            m_batteryIcons.clear();
            m_batteryIcons.add(icons);
        }
        /// draw battery state to specified rectangle of screen
        virtual void drawBattery( LVDrawBuf & buf, const lvRect & rc );
        /// sets screen orientation value, to be red by corresponding getter. Doesn't rotate screen actually.
        virtual void setScreenOrientation( cr_rotate_angle_t angle ) { _orientation = angle; }
        /// returns current screen orientation
        virtual cr_rotate_angle_t getScreenOrientation() { return _orientation; }
        /// draws icon at center of screen, with optional progress gauge
        virtual void showWaitIcon( lString16 filename, int progressPercent=-1 );
        /// draws icon with gauge at center of screen, skipping too frequent updates
        virtual void showProgress( lString16 filename, int progressPercent );
		/// loads skin from file
	    virtual bool loadSkin( lString16 pathname );
		/// returns keyboard layouts
		virtual CRKeyboardLayoutList & getKeyboardLayouts() { return _kbLayouts; }
        /// returns accelerator table list
        virtual CRGUIAcceleratorTableList & getAccTables() { return _accTables; }
        /// return battery status
        virtual bool getBatteryStatus( int & percent, bool & charging )
        {
            // stub
            percent = 0; charging = false; return false;
        }
        /// set skin
        virtual void setSkin( CRSkinRef skin ) { _skin = skin; }
        /// returns currently selected skin
        virtual CRSkinRef getSkin() { return _skin; }
        /// sets another i18n translator
        virtual void setTranslator( LVRef<CRGUIStringTranslator> i18n )
        {
            _i18n = i18n;
        }
        /// translate string by key, return default value if not found
        virtual lString16 translateString( const char * key, const char * defValue )
        {
            if ( _i18n.isNull() )
                return Utf8ToUnicode( lString8(defValue) );
            return _i18n->translateString( key, defValue );
        }
        /// returns count of windows
        virtual int getWindowCount() { return _windows.length(); }
        /// changes screen size and orientation
        virtual void reconfigure( int dx, int dy, cr_rotate_angle_t orientation );
        /// adds command to message queue
        virtual void postCommand( int command, int params = 0 );
        /// runs posted events (commands)
        virtual bool processPostedEvents()
        {
            return handleAllEvents( false );
        }
        /// returns true if command is processed
        virtual bool onCommand( int command, int params = 0 )
        {
            for ( int i=_windows.length()-1; i>=0; i-- ) {
                if ( _windows[i]->isVisible() && _windows[i]->onCommand( command, params ) )
                    return true;
            }
            return false;
        }
        /// returns true if key is processed
        virtual bool onKeyPressed( int key, int flags = 0 );
        /// returns top visible window
        CRGUIWindow * getTopVisibleWindow()
        {
            for ( int i=_windows.length()-1; i>=0; i-- ) {
                if ( !_windows[i]->isVisible() )
                    continue;
                return _windows[i];
            }
            return NULL;
        }
        /// shows or hides window
        void showWindow( CRGUIWindow * window, bool visible )
        {
            int index = _windows.indexOf( window );
            if ( index >= 0  ) { //&& window->isVisible()!=visible
                window->setVisible( visible );
                if ( !visible ) {
                    window->covered();
                    CRGUIWindow * wnd = getTopVisibleWindow();
                    if ( wnd )
                        activateWindow( wnd );
                } else
                    activateWindow( window );
            }
        }
        /// activates window, brings it on top; add to stack if not added
        void activateWindow( CRGUIWindow * window );
        /// closes window, removes from stack, destroys object
        void closeWindow( CRGUIWindow * window );
        /// redraw one window
        virtual void updateWindow( CRGUIWindow * window );
        /// full redraw of all windows
        void update( bool fullScreenUpdate, bool forceFlushScreen=true );
        /// returns screen associated with window manager
        virtual CRGUIScreen * getScreen()
        {
            return _screen;
        }
        /// runs event loop
        virtual int runEventLoop();
        /// constructor
        CRGUIWindowManager(CRGUIScreen * screen)
        : _screen( screen ), _ownScreen(false)
        , _postedCommand(0)
        , _postedCommandParam(0)
        ,_lastProgressUpdate(0)
        ,_lastProgressPercent(-1)
        ,_orientation(CR_ROTATE_ANGLE_0)
        ,_stopFlag(false)
        {
        }
        virtual void closeAllWindows()
        {
            for ( int i=_windows.length()-1; i>=0; i-- ) {
                closeWindow(_windows[i]);
            }
        }
        /// destroy all windows on close
        virtual ~CRGUIWindowManager()
        {
            closeAllWindows();
            if ( _ownScreen )
                delete _screen;
        }
};

/// Window base implementation
class CRGUIWindowBase : public CRGUIWindow
{
    protected:
        CRGUIWindowManager * _wm;
        lvRect _rect;
        bool _visible;
        bool _fullscreen;
        bool _dirty;
        bool _passKeysToParent;
        bool _passCommandsToParent;
        int _page;
        int _pages;
        CRGUIAcceleratorTableRef _acceleratorTable;
        lString16 _skinName;
        lString16 _scrollLabel;
        lString16 _caption;
        lString16 _statusText;
        lString16 _inputText;
        LVImageSourceRef _icon; // window title icon
        // draws frame, title, status and client
        virtual void draw();

        /// use to override status text
        virtual lString16 getStatusText() { return _statusText; }

        /// draw status bar using current skin, with optional status text and scroll/tab/page indicator
        virtual void drawStatusBar();
        /// draw status text
        virtual void drawStatusText( LVDrawBuf & buf, const lvRect & rc, CRRectSkinRef skin );
        /// draw title bar using current skin, with optional scroll/tab/page indicator
        virtual void drawTitleBar();
        /// draw input box, if any
        virtual void drawInputBox();
        /// draw title bar using current skin, with optional scroll/tab/page indicator
        virtual void drawClient();

        /// calculates title rectangle for window rectangle
        virtual bool getTitleRect( lvRect & rc );
        /// calculates status rectangle for window rectangle
        virtual bool getStatusRect( lvRect & rc );
        /// calculates client rectangle for window rectangle
        virtual bool getClientRect( lvRect & rc );
        /// calculates input box rectangle for window rectangle
        virtual bool getInputRect( lvRect & rc );
        /// calculates scroll rectangle for window rectangle
        virtual bool getScrollRect( lvRect & rc );
    public:
        /// use to override status text
        virtual void setStatusText( lString16 s ) { _statusText = s; }
        /// formats scroll label (like "1 of 2")
        virtual lString16 getScrollLabel( int page, int pages );
        /// calculates minimum scroll size
        virtual lvPoint getMinScrollSize( int page, int pages );
        /// sets scroll label (e.g. "Page $1 of $2" or "$1 / $2")
        virtual void setScrollLabelTemplate( lString16 text ) { _scrollLabel=text; }
        /// returns scroll label (e.g. "$1 of $2")
        virtual lString16 getScrollLabelTemplate() { return _scrollLabel; }
        /// called on system configuration change: screen size and orientation
        virtual void reconfigure( int flags );
        /// sets skin name for window
        virtual void setSkinName( const lString16  & skin ) { _skinName = skin; }
        /// returns skin name for window
        virtual lString16 getSkinName() { return _skinName; }
        /// returns true if command is processed
        virtual bool onCommand( int command, int params = 0 ) { return !_passCommandsToParent; }
        /// returns true if key is processed (by default, let's translate key to command using accelerator table)
        virtual bool onKeyPressed( int key, int flags = 0 );
        /// set accelerator table for window
        virtual void setAccelerators( CRGUIAcceleratorTableRef table ) { _acceleratorTable = table; }
        /// get window accelerator table
        virtual CRGUIAcceleratorTableRef getAccelerators() { return _acceleratorTable; }
        /// returns window width
        inline int getWidth() { return getRect().width(); }
        /// returns window height
        inline int getHeight() { return getRect().height(); }
        /// sets dirty flag
        virtual void setDirty() { _dirty = true; }
        /// returns true if window is changed but now drawn
        virtual bool isDirty() { return _dirty; }
        /// shows or hides window
        virtual void setVisible( bool visible ) { _visible = visible; setDirty(); }
        virtual bool isVisible() const { return _visible; }
        virtual const lvRect & getRect() { return _rect; }
        virtual void setRect( const lvRect & rc ) { _rect = rc; setDirty(); }
        virtual void flush() { draw(); _dirty = false; }
        /// returns true if window is fullscreen
        virtual bool isFullscreen() { return _fullscreen; }
        /// set fullscreen state for window
        virtual void setFullscreen( bool fullscreen ) { _fullscreen = fullscreen; }
        virtual CRGUIWindowManager * getWindowManager() { return _wm; }
        CRGUIWindowBase( CRGUIWindowManager * wm )
        : _wm(wm), _visible(true), _fullscreen(true), _dirty(true), _passKeysToParent(false), _passCommandsToParent(false)
        , _page(0), _pages(0)
        {
            // fullscreen visible by default
            _rect = _wm->getScreen()->getRect();
            //_statusText = "Sample status text";
        }
        virtual ~CRGUIWindowBase() { }
};

/// Base Screen class implementation
class CRGUIScreenBase : public CRGUIScreen
{
    protected:
        int _width;
        int _height;
        lvRect _updateRect;
        LVRef<LVDrawBuf> _canvas;
        LVRef<LVDrawBuf> _front;
        int _fullUpdateInterval;
        int _fullUpdateCounter;
        /// override in ancessor to transfer image to device
        virtual void update( const lvRect & rc, bool full ) = 0;
    public:
        /// fast update feature parameter setting
        virtual void setFullUpdateInterval( int pagesBeforeFullupdate=1 )
        {
            _fullUpdateInterval = pagesBeforeFullupdate;
            if ( _fullUpdateInterval>0 )
                _fullUpdateCounter = _fullUpdateInterval;
        }
        virtual bool checkFullUpdateCounter()
        {
            if ( _fullUpdateInterval<=0 )
                return false; // always partial update
            if ( _fullUpdateInterval==1 )
                return true;  // always full update
            _fullUpdateCounter--;
            if ( _fullUpdateCounter<=0 ) {
                _fullUpdateCounter = _fullUpdateInterval;
                return true; // full update
            }
            return false; // partial update
        }

        /// creates compatible canvas of specified size
        virtual LVDrawBuf * createCanvas( int dx, int dy )
        {
#if (COLOR_BACKBUFFER==1)
            LVDrawBuf * buf = new LVColorDrawBuf( dx, dy );
#else
            LVDrawBuf * buf = new LVGrayDrawBuf( dx, dy, GRAY_BACKBUFFER_BITS );
#endif
            return buf;
        }
        /// sets new screen size
        virtual bool setSize( int dx, int dy )
        {
            if ( _width!=dx || _height != dy ) {
                _width = dx;
                _height = dy;
                _canvas = LVRef<LVDrawBuf>( createCanvas( dx, dy ) );
                if ( !_front.isNull() )
                    _front = LVRef<LVDrawBuf>( createCanvas( dx, dy ) );
                return true;
            }
            return false;
        }

        /// returns screen width
        virtual int getWidth() { return _width; }
        /// returns screen height
        virtual int getHeight() { return _height; }
        /// return pointer to screen canvas
        virtual LVRef<LVDrawBuf> getCanvas() { return _canvas; }
        /// draw image on screen canvas
        virtual void draw( LVDrawBuf * img, int x = 0, int y = 0)
        {
            img->DrawTo( _canvas.get(), x, y, 0, NULL );
        }
        /// transfers contents of buffer to device, if full==true, redraws whole screen, otherwise only changed area
        virtual void flush( bool full );
        /// invalidates rectangle: add it to bounding box of next partial update
        virtual void invalidateRect( const lvRect & rc )
        {
            _updateRect.extend( rc );
        }
        CRGUIScreenBase( int width, int height, bool doublebuffer  )
        : _width( width ), _height( height ), _canvas(NULL), _front(NULL)
        , _fullUpdateInterval(1)
        , _fullUpdateCounter(1)
        {
            if ( width && height ) {
                _canvas = LVRef<LVDrawBuf>( createCanvas( width, height ) );
                if ( doublebuffer )
                    _front = LVRef<LVDrawBuf>( createCanvas( width, height ) );
            }
        }
        virtual ~CRGUIScreenBase()
        {
        }
};

#ifdef CR_WX_SUPPORT
/// WXWidget support: draw to wxImage
class CRWxScreen : public CRGUIScreenBase
{
    protected:
        wxBitmap _wxbitmap;
        virtual void update( const lvRect & rc, bool full )
        {
            wxImage img;
            int dyy = _canvas->GetHeight();
            int dxx = _canvas->GetWidth();
            int dx = dxx;
            int dy = dyy;
            img.Create(dx, dy, true);
            unsigned char * bits = img.GetData();
            for ( int y=0; y<dy && y<dyy; y++ ) {
                int bpp = _canvas->GetBitsPerPixel();
                if ( bpp==32 ) {
                    const lUInt32* src = (const lUInt32*) _canvas->GetScanLine( y );
                    unsigned char * dst = bits + y*dx*3;
                    for ( int x=0; x<dx && x<dxx; x++ )
                    {
                        lUInt32 c = *src++;
                        *dst++ = (c>>16) & 255;
                        *dst++ = (c>>8) & 255;
                        *dst++ = (c>>0) & 255;
                    }
                } else if ( bpp==2 ) {
                    //
                    static const unsigned char palette[4][3] = {
                        { 0xff, 0xff, 0xff },
                        { 0xaa, 0xaa, 0xaa },
                        { 0x55, 0x55, 0x55 },
                        { 0x00, 0x00, 0x00 },
                    };
                    const lUInt8* src = (const lUInt8*) _canvas->GetScanLine( y );
                    unsigned char * dst = bits + y*dx*3;
                    for ( int x=0; x<dx && x<dxx; x++ )
                    {
                        lUInt32 c = (( src[x>>2] >> ((3-(x&3))<<1) ))&3;
                        *dst++ = palette[c][0];
                        *dst++ = palette[c][1];
                        *dst++ = palette[c][2];
                    }
                } else if ( bpp==1 ) {
                    //
                    static const unsigned char palette[2][3] = {
                        { 0xff, 0xff, 0xff },
                        { 0x00, 0x00, 0x00 },
                    };
                    const lUInt8* src = (const lUInt8*) _canvas->GetScanLine( y );
                    unsigned char * dst = bits + y*dx*3;
                    for ( int x=0; x<dx && x<dxx; x++ )
                    {
                        lUInt32 c = (( src[x>>3] >> ((7-(x&7))) ))&1;
                        *dst++ = palette[c][0];
                        *dst++ = palette[c][1];
                        *dst++ = palette[c][2];
                    }
                }
            }

            // copy to bitmap
            wxBitmap bmp( img );
            _wxbitmap = bmp;
        }
    public:
        CRWxScreen( int width, int height )
        :  CRGUIScreenBase( width, height, true ) { }
        wxBitmap getWxBitmap() { return _wxbitmap; }
};
#endif

/// Window to show LVDocView contents
class CRDocViewWindow : public CRGUIWindowBase
{
    protected:
        LVDocView * _docview;
	    CRWindowSkinRef _skin;
        virtual void draw();
    public:
        LVDocView * getDocView()
        {
            return _docview;
        }
        CRDocViewWindow( CRGUIWindowManager * wm )
        : CRGUIWindowBase( wm )
        {
            CRLog::trace("CRDocViewWindow()");
            _docview = new LVDocView( wm->getScreen()->getCanvas()->GetBitsPerPixel() );
            CRLog::trace("resizing...");
            _docview->Resize( getWidth(), getHeight() );
            _docview->setPageMargins( lvRect(10, 10, 10, 10) );
            CRLog::trace("CRDocViewWindow() finished");
        }
        virtual ~CRDocViewWindow()
        {
            delete _docview;
            _docview = NULL;
        }

        virtual void setRect( const lvRect & rc );

        /// returns true if command is processed
        virtual bool onCommand( int command, int params );

		/// returns true if window is changed but now drawn
        virtual bool isDirty()
        {
            return _dirty || !_docview->isPageImageReady( 0 );
        }
};





//===========================================================================================
// MENU SUPPORT

enum CRMenuControlCmd {
    MCMD_CANCEL=500,
    MCMD_OK,
    MCMD_SCROLL_FORWARD,
    MCMD_SCROLL_BACK,
    MCMD_SELECT_0,
    MCMD_SELECT_1,
    MCMD_SELECT_2,
    MCMD_SELECT_3,
    MCMD_SELECT_4,
    MCMD_SELECT_5,
    MCMD_SELECT_6,
    MCMD_SELECT_7,
    MCMD_SELECT_8,
    MCMD_SELECT_9,
    MCMD_SELECT_0_LONG,
    MCMD_SELECT_1_LONG,
    MCMD_SELECT_2_LONG,
    MCMD_SELECT_3_LONG,
    MCMD_SELECT_4_LONG,
    MCMD_SELECT_5_LONG,
    MCMD_SELECT_6_LONG,
    MCMD_SELECT_7_LONG,
    MCMD_SELECT_8_LONG,
    MCMD_SELECT_9_LONG,
    MCMD_SCROLL_FORWARD_LONG,
    MCMD_SCROLL_BACK_LONG,
    MCMD_CLEAR,
    MCMD_NEXT_ITEM,
    MCMD_PREV_ITEM,
    MCMD_NEXT_PAGE,
    MCMD_PREV_PAGE,
    MCMD_SELECT,
    MCMD_SELECT_LONG
};

enum CRGUICmd {
    GCMD_PASS_TO_PARENT = 550
};

class CRMenu;

/// CRGUI menu item base class
class CRMenuItem
{
    protected:
        CRMenu * _menu;
        int _id;
        lString16 _label;
        LVImageSourceRef _image;
        LVFontRef _defFont;
        lString16 _propValue;
        bool _itemDirty;
    public:
        /// id of item
        int getId() { return _id; }
        /// set id of item
        void setId( int id ) { _id = id; }
        /// item label
        lString16 getLabel() { return _label; }
        void setLabel(lString16 label) { _label = label; setItemDirty(); }
        /// item icon
        LVImageSourceRef getImage() { return _image; }
        /// item label font
        virtual LVFontRef getFont() { return _defFont; }
        /// constructor
        CRMenuItem( CRMenu * menu, int id, lString16 label, LVImageSourceRef image, LVFontRef defFont, const lChar16 * propValue=NULL  )
    : _menu(menu), _id(id), _label(label), _image(image), _defFont(defFont), _propValue(propValue) { }
        /// constructor
        CRMenuItem( CRMenu * menu, int id, const char * label, LVImageSourceRef image, LVFontRef defFont, const lChar16 * propValue=NULL  )
    : _menu(menu), _id(id), _label(label), _image(image), _defFont(defFont), _propValue(propValue) { }
        /// measures item size
        virtual lvPoint getItemSize( CRRectSkinRef skin );
        /// draws item
        virtual void Draw( LVDrawBuf & buf, lvRect & rc, CRRectSkinRef skin, CRRectSkinRef valueSkin, bool selected );
        /// returns true if submenu
        virtual bool isSubmenu() const { return false; }
        /// called on item selection
        virtual int onSelect() { return 0; }
        virtual ~CRMenuItem() { }
        /// submenu for options dialog support
        virtual lString16 getSubmenuValue() { return lString16::empty_str; }
        /// property value, for options editor support
        virtual lString16 getPropValue() { return _propValue; }
        virtual bool isItemDirty() { return _itemDirty; }
        virtual void setItemDirty() { _itemDirty = true; }
        virtual void onLeave() { CRLog::trace("Menu item %d leave", _id); setItemDirty(); }
        virtual void onEnter() { CRLog::trace("Menu item %d enter", _id); setItemDirty(); }
};

/// CRGUI menu base class
class CRMenu : public CRGUIWindowBase, public CRMenuItem {
	private:
		void doCloseMenu(int command, bool highlight = false, int param = 0);
    protected:
        LVPtrVector<CRMenuItem> _items;
        CRPropRef _props;
        lString16 _propName;
        LVFontRef _valueFont;
        int _topItem;
        int _pageItems;
        int _helpHeight;
        int _cmdToHighlight;
        int _selectedItem;
        bool _pageUpdate;
        CRMenuSkinRef _skin;// = _wm->getSkin()->getMenuSkin( path.c_str() );
        // override for CRGUIWindow method
        virtual void draw();
        virtual void Draw( LVDrawBuf & buf, lvRect & rc, CRRectSkinRef skin, CRRectSkinRef valueSkin, bool selected );
        //virtual void Draw( LVDrawBuf & buf, int x, int y );
        virtual void highlightCommandItem( int cmd );
		virtual bool onItemSelect(int command, int params = 0 );
		int getLastOnPage();
    public:
        /// returns index of selected item, -1 if no item selected
        virtual int getSelectedItemIndex();
        virtual int getDefaultSelectionIndex() 
        {
#ifdef CR_POCKETBOOK
			if (getProps().isNull())
				return 0;
#endif
			return -1; 
        }
        virtual void activated();
        virtual void drawClient();
        virtual int getScrollHeight();
        CRMenuSkinRef getSkin();
        CRMenu( CRGUIWindowManager * wm, CRMenu * parentMenu, int id, lString16 label, LVImageSourceRef image, LVFontRef defFont, LVFontRef valueFont, CRPropRef props=CRPropRef(), const char * propName=NULL, int pageItems=8 )
        : CRGUIWindowBase( wm ), CRMenuItem( parentMenu, id, label, image, defFont ), _props(props), _propName(Utf8ToUnicode(lString8(propName))), _valueFont(valueFont), _topItem(0), _pageItems(pageItems),
          _cmdToHighlight(-1), _selectedItem(-1), _pageUpdate(true)
        { _fullscreen = false; _helpHeight=0; }
        CRMenu( CRGUIWindowManager * wm, CRMenu * parentMenu, int id, const char * label, LVImageSourceRef image, LVFontRef defFont, LVFontRef valueFont, CRPropRef props=CRPropRef(), const char * propName=NULL, int pageItems=8 )
        : CRGUIWindowBase( wm ), CRMenuItem( parentMenu, id, label, image, defFont ), _props(props), _propName(Utf8ToUnicode(lString8(propName))), _valueFont(valueFont), _topItem(0), _pageItems(pageItems),
        _cmdToHighlight(-1)
        { _fullscreen = false; _helpHeight=0; }
        virtual bool isSubmenu() const { return true; }
        LVPtrVector<CRMenuItem> & getItems() { return _items; }
        CRPropRef getProps() const { return _props; }
        lString16 getPropName() const { return _propName; }
        LVFontRef getValueFont() const { return _valueFont; }
        void setValueFont( LVFontRef font ) { _valueFont = font; }
        void addItem( CRMenuItem * item ) { _items.add( item ); }
        CRMenuItem * findItem( int id ) {
            for ( int i=0; i<_items.length(); i++ )
                if ( _items[i]->getId()==id )
                    return _items[i];
            return NULL;
        }
        CRMenu * findSubmenu( int id ) {
            for ( int i=0; i<_items.length(); i++ )
                if ( _items[i]->getId()==id && _items[i]->isSubmenu() )
                    return (CRMenu*)_items[i];
            return NULL;
        }
        /// called on system configuration change: screen size and orientation
        virtual void reconfigure( int flags );
        virtual int getPageCount();
        /// returns true if current page is changed
        virtual bool setCurPage( int nPage );
		virtual void setCurItem(int nItem);
        virtual int getCurPage( );
        virtual int getTopItem();
        virtual lString16 getSubmenuValue();
        virtual void toggleSubmenuValue();
        virtual int getItemHeight();
        virtual lvPoint getMaxItemSize();
        virtual lvPoint getItemSize();
        virtual lvPoint getSize();
        virtual ~CRMenu() { }
        // CRGUIWindow
        virtual const lvRect & getRect();
        /// overriden to disable passing key to parent windows
        virtual bool onKeyPressed( int key, int flags );
        /// returns true if command is processed
        virtual bool onCommand( int command, int params = 0 );
        /// closes menu and its submenus, posts command
        virtual void closeMenu( int command, int params = 0 );
        /// closes top level menu and its submenus, posts command
        virtual void closeAllMenu( int command, int params = 0 );
        /// closes menu and its submenus
        virtual void destroyMenu();
        virtual void covered() { _pageUpdate = true; }
};

class CRGUIUpdateEvent : public CRGUIEvent
{
public:
    CRGUIUpdateEvent( bool fullScreen=false )
    : CRGUIEvent( CREV_UPDATE )
    {
        _param1 = fullScreen ? 1 : 0;
    }
    virtual bool handle( CRGUIWindow * window )
    {
        return false;
    }
    virtual bool handle( CRGUIWindowManager * wm )
    {
        wm->update( _param1!=0, false );
        return true;
    }
};

class CRGUIResizeEvent : public CRGUIEvent
{
    cr_rotate_angle_t _angle;
public:
    CRGUIResizeEvent( int dx, int dy, cr_rotate_angle_t angle )
    : CRGUIEvent( CREV_RESIZE )
    {
        _param1 = dx;
        _param2 = dy;
        _angle = angle;
    }
    virtual bool handle( CRGUIWindow * window )
    {
        return false;
    }
    virtual bool handle( CRGUIWindowManager * wm )
    {
        wm->reconfigure(_param1, _param2, _angle);
        return true;
    }
};

class CRGUIKeyDownEvent : public CRGUIEvent
{
public:
    virtual bool isForVisibleOnly() { return true; }
    virtual bool isForModalOnly() { return true; }
    CRGUIKeyDownEvent( int key, int params )
    : CRGUIEvent( CREV_KEYDOWN )
    {
        _param1 = key;
        _param2 = params;
    }
    virtual bool handle( CRGUIWindow * window );
    virtual bool handle( CRGUIWindowManager * wm ) { return false; }
};

class CRGUICommandEvent : public CRGUIEvent
{
public:
    CRGUICommandEvent( int cmd, int params )
    : CRGUIEvent( CREV_COMMAND )
    {
        _param1 = cmd;
        _param2 = params;
    }
    virtual bool handle( CRGUIWindow * window );
    virtual bool handle( CRGUIWindowManager * wm ) { return false; }
    virtual bool isForModalOnly() { return false; }
    virtual bool isForVisibleOnly() { return true; }
};


#endif// CR_GUI_INCLUDED
