//****************************************************************************
//Copyright (C) 2005-2006 Beijing BlueDJ Technology Co.,Ltd. All rights reserved.

//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 (in the file LICENSE.GPL); if not, write to the Free Software
//Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

//Please visit http://www.bluedj.com for more infomation about us.
//Contact us at ggwizard@gmail.com or darkdong@gmail.com.
//****************************************************************************/

#include "DJCanvasView.h"
#include "DJGameUser.h"
#include "DJGameController.h"
#include "DJGamePanel.h"
#include "DJCanvasItem.h"

static	int ZORDER_FOCUS	= 90;
static	int ZORDER_SEAT		= 100;
static	int ZORDER_TABLE	= 150;
static	int ZORDER_AVATAR	= 250;
static	int ZORDER_PLAYING	= 300;
static	int ZORDER_READY	= 350;
static	int ZORDER_NAME		= 400;

static qreal ZOOM_STEP	= 0.2;
static qreal MAX_ZOOM	= 1.99;
static qreal MIN_ZOOM	= 0.21;

static	int	CANVASVIEW_MARGIN_Y		= 30;

DJCell::DJCell( quint8 tableId, const QPixmap& tablePixmap, const QFont& tableFont, const QColor& tableColor,
		 const QList<QPixmap>& seatPixmaps, const QPixmap& playingPixmap, DJCanvasView* canvasView )
		 : m_gamePanel(0),m_canvasView(canvasView)
{
	//djDebug() << "DJCell constructor";
	m_playingItem	= new DJCanvasItem( m_canvasView->canvas(), playingPixmap, DJCanvasItem::Playing );
	m_playingItem->setZ( ZORDER_PLAYING );
	
	m_tableItem	= new DJCanvasTable( tablePixmap, tableId, tableFont, m_canvasView->canvas() );
	m_tableItem->setPen( tableColor );
	m_tableItem->setZ( ZORDER_TABLE );
	
	for ( quint8 seatId = 1; seatId <= seatPixmaps.size(); seatId++ ) {
		DJCanvasItem	*seatItem	= new DJCanvasItem( canvasView->canvas(), seatPixmaps.at(seatId-1), DJCanvasItem::Seat, tableId << 8 | seatId );
		seatItem->setZ( ZORDER_SEAT );
		m_seatItems << seatItem;
	}
}
DJCell::~DJCell()
{
	//djDebug() << "DJCell destructor";
	delete m_tableItem;
	delete m_playingItem;
	qDeleteAll( m_seatItems );
	deleteGamePanel();
}
void DJCell::draw()
{
	if ( m_canvasView->cellOrigins().isEmpty() )
		return;
		
	quint8 tableIndex	= tableId() - 1;
	
	int originx	= m_canvasView->cellOrigins().at(tableIndex).x();
	int originy	= m_canvasView->cellOrigins().at(tableIndex).y();
	
	int centerx	= m_canvasView->cellSize().width()/2;
	int centery	= m_canvasView->cellSize().height()/2;
			
	int x	= originx + centerx - m_tableItem->width()/2;
	int y	= originy + centery - m_tableItem->height()/2;
	
	m_tableItem->move( x, y );
	m_tableItem->show();
	
	x	= originx + centerx - m_playingItem->width()/2;
	y	= originy + centery - m_playingItem->height()/2;
	m_playingItem->move( x, y );
	
	for ( quint8 seatIndex = 0; seatIndex < numberOfSeats(); seatIndex++ ) {
		DJCanvasItem *seatItem	= m_seatItems.at(seatIndex);
		x	= originx + centerx - seatItem->width()/2 + m_canvasView->seatOffsets().at(seatIndex).x();
		y	= originy + centery - seatItem->height()/2 + m_canvasView->seatOffsets().at(seatIndex).y();
		seatItem->move( x, y );
		seatItem->show();
	}
	m_canvasView->canvas()->update();
	m_canvasView->updateContents();
}
void DJCell::synchronize( quint8 tableStatus, const QList<DJGameUser*>& tableMapUsers )
{
	djDebug() << "synchronize" ;
	//set table status
	setStatus( tableStatus );
	//remove user who not in tableMapUsers
	QList<DJGameUser*>::iterator it1 = m_orderedUsers.begin();
	while ( it1 != m_orderedUsers.end() ) {
        DJGameUser*	user	= *it1;
        djDebug() << "m_orderedUsers id = " << user->userId();
        if ( !tableMapUsers.contains( user ) ) {
        	djDebug() << "remove user who not in tableMapUsers" << user->userName();
        	it1	= m_orderedUsers.erase( it1 );
        	//m_canvasView->requestUserInfo( user->userId() );
        	//just set user status to idle?
        	user->setTrinity( 0, 0, GAME_STATUS_IDLE );
        }else
        	++it1;
    }
    //add user who not in m_orderedUsers
    QList<DJGameUser*>::const_iterator it2 = tableMapUsers.begin();
	while ( it2 != tableMapUsers.end() ) {
        DJGameUser*	user	= *it2;
        djDebug() << "tableMapUsers id = " << user->userId();
        if ( !m_orderedUsers.contains( user ) ) {
        	djDebug() << "add user who not in m_orderedUsers" << user->userName();
        	m_orderedUsers << user;
        }
        ++it2;
    }
    draw();
}
bool DJCell::isPlaying() const
{
	return m_playingItem->isVisible();
}
bool DJCell::isIdle() const
{
	return !m_playingItem->isVisible();
}
quint8 DJCell::tableId() const
{
	return m_tableItem->id();
}
quint8 DJCell::numberOfSeats() const
{
	return m_seatItems.size();
}
void DJCell::addUser( DJGameUser* user )
{
	if ( !m_orderedUsers.contains( user ) )
		m_orderedUsers << user;
}
void DJCell::removeUser( DJGameUser* user )
{
	int index	= m_orderedUsers.indexOf( user );
	if ( -1 != index ) {
		m_orderedUsers.removeAt( index );
    }
}
DJGameUser* DJCell::userAtSeat( quint8 seatId )
{
	foreach( DJGameUser* user, m_orderedUsers ) {
		if ( seatId == user->seatId() ) {
			return user;
		}
	}
	return 0;
}
void DJCell::setStatus( quint8 tableStatus )
{
	if ( DJGAME_TABLE_STATUS_IDLE == tableStatus )
		m_playingItem->hide();
	else {
		m_playingItem->show();
		foreach( DJGameUser* user, m_orderedUsers ) {
			if ( !user->isPlaying() ) {
				user->setTrinity( user->tableId(), user->seatId(), GAME_STATUS_PLAYING );
			}
		}
	}
}
void DJCell::setGamePanel( DJGamePanel* gamePanel )
{
	deleteGamePanel();
	m_gamePanel	= gamePanel;
}
void DJCell::deleteGamePanel()
{
	if ( m_gamePanel ) {
		m_gamePanel->closeChildDialog();
		m_gamePanel->removePendingAcl();
		m_gamePanel->deleteLater();
		m_gamePanel	= 0;
	}
}
QList<quint32> DJCell::panelUserIds()
{
	//NOTE:userIds = numberOfSeats + 1 ,userIds[0] is always selfid
	QList<quint32> userIds;
	int i;
	for ( i = 0; i <= numberOfSeats(); i++ ) {
		userIds << 0;
	}
	userIds[0]	= m_canvasView->gameController()->selfUserId();
	foreach( DJGameUser* user, m_orderedUsers ) {
		quint8 seat	= user->seatId();
		if ( seat )
			userIds[seat]	= user->userId();
	}
	return userIds;
}
/*
QList<quint8> DJCell::playingSeatIds()
{
	QList<quint8> seatIds;
	foreach( DJGameUser* user, m_orderedUsers ) {
		if ( user->isPlaying() )
			seatIds << user->seatId();
	}
	return seatIds;
}
*/
bool DJCell::precede( quint32 firstUserId, quint32 secondUserId )
{
	djDebug() << "DJCell::precede" << firstUserId << secondUserId;
	int firstIndex	= -1;
	int secondIndex	= -1;
	for( int i = 0; i < m_orderedUsers.size(); i++ ) {
		DJGameUser* user	= m_orderedUsers.at(i);
		djDebug() << "userid = " << user->userId() << user->userName();
		if ( firstUserId == user->userId() )
			firstIndex	= i;
		else if ( secondUserId == user->userId() )
			secondIndex	= i;
	}
	if ( -1 == firstIndex || -1 == secondIndex ) {
		return false;
	}else {
		return firstIndex < secondIndex;
	}
}
void DJCell::description() const
{
	djDebug() << "m_orderedUsers size = " << m_orderedUsers.size();
    foreach( DJGameUser* user, m_orderedUsers ) {
		djDebug() << user->userId() << user->userName() << user->tableId() << user->seatId() << user->gameStatus();
	}
}

//---------------------------------------------------------------------------------
DJPlayer::DJPlayer( DJGameUser* user, const QPixmap& readyPixmap,
		const QFont& nameFont, const QColor& nameColor, DJCanvasView* canvasView )
	:m_user(user),m_canvasView(canvasView)
{
	djDebug() << "DJPlayer constructor";
	QPixmap	avatarPixmap	= user->avatar();
	
	m_avatarItem	= new DJCanvasAvatar( avatarPixmap, user, readyPixmap, m_canvasView->canvas() );
	m_avatarItem->setZ( ZORDER_AVATAR );
	
	m_nameItem	= new DJCanvasText( user->userName(), m_canvasView->canvas() );
	m_nameItem->setFont( nameFont );
	m_nameItem->setColor( nameColor );
	m_nameItem->setZ( ZORDER_NAME );
}
DJPlayer::~DJPlayer()
{
	djDebug() << "DJPlayer destructor";
	delete m_avatarItem;
	delete m_nameItem;
}

void DJPlayer::draw()
{
	if ( m_canvasView->cellOrigins().isEmpty() )
		return;
	djDebug() << "DJPlayer::draw" << m_user->userName() << m_user->tableId() << m_user->seatId() << m_user->gameStatus();
	DJCanvasItem* selfReadyItem	= m_canvasView->selfReadyItem();
	quint8 tableId	= m_user->tableId();
	quint8 seatId	= m_user->seatId();
	
	if ( 0 == tableId || 0 == seatId ) {
		//hide player
		m_avatarItem->hide();
		m_nameItem->hide();
		if ( m_user->isSelf() ) {
			selfReadyItem->hide();
			m_canvasView->setFocusTable( 0, false );
		}
	}else {
		//show player
		quint8 tableIndex	= tableId - 1;
		quint8 seatIndex	= seatId - 1;
		
		int originx	= m_canvasView->cellOrigins().at(tableIndex).x();
    	int originy	= m_canvasView->cellOrigins().at(tableIndex).y();
    	
    	int centerx	= m_canvasView->cellSize().width()/2;
    	int centery	= m_canvasView->cellSize().height()/2;
    	
    	int x	= originx + centerx - m_avatarItem->width()/2 + m_canvasView->avatarOffsets().at( seatIndex ).x();
    	int y	= originy + centery - m_avatarItem->height()/2 + m_canvasView->avatarOffsets().at( seatIndex ).y();
    	
    	m_avatarItem->move( x, y );
    	m_avatarItem->show();
    	
    	QRect	rect	= m_nameItem->boundingRect();
    	m_nameItem->move( x, y - rect.height() );
    	m_nameItem->show();
    	
    	if ( m_user->isSelf() ) {
    		if ( m_user->isDown() ) {
    			x	= originx + centerx - selfReadyItem->width()/2;
				y	= originy + centery - selfReadyItem->height()/2;
				selfReadyItem->move( x, y );
				selfReadyItem->show();
			}else {
				selfReadyItem->hide();
			}
			m_canvasView->setFocusTable( m_user->tableId(), false );
    	}
	}
	m_canvasView->canvas()->update();
	m_canvasView->updateContents();
}
void DJPlayer::reload()
{
	QSize oldSize	= m_avatarItem->pixmap().size();
	QPixmap	avatarPixmap	= m_user->avatar();
	avatarPixmap	= avatarPixmap.scaled( oldSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation );
	m_avatarItem->setPixmap( avatarPixmap );
	
	m_nameItem->setText( m_user->userName() );
}
//---------------------------------------------------------------------------------
static 	const char*	KEY_GAME_CANVASVIEW_SCALE		= "GameCanvasViewScale";

DJCanvasView::DJCanvasView( DJGameController* gameController, QWidget* parent )
	:Q3CanvasView(parent),m_gameController(gameController)
{
	djDebug("DJCanvasView constructor");
	
	m_selfReadyItem	= 0;
	m_focusRectItem	= 0;
	Q3Canvas* canvas	= new Q3Canvas( this );
	//canvas->setUpdatePeriod( 500 );
	//canvas->setAdvancePeriod( 500 );
	setCanvas( canvas );
	canvas->setBackgroundPixmap( QPixmap( m_gameController->gameResDir() + "/image/base/floor.png" ) );
	
	setHScrollBarMode(Q3ScrollView::AlwaysOff);
	setVScrollBarMode(Q3ScrollView::AlwaysOn);
	m_canvasContextMenu	= new QMenu( this );
	
	m_actZoomIn	= new QAction( tr("Zoom in"), this );
	m_actZoomIn->setShortcut( QKeySequence(Qt::Key_Plus) );
	new QShortcut(QKeySequence(Qt::Key_Plus),this,SLOT(zoomIn()));
	connect( m_actZoomIn, SIGNAL(triggered()), SLOT(zoomIn()));
	m_canvasContextMenu->addAction(m_actZoomIn);
	
	m_actRevert	= new QAction( tr("Revert"), this );
	connect( m_actRevert, SIGNAL(triggered()), SLOT(revert()));
	m_canvasContextMenu->addAction(m_actRevert);
	
	m_actZoomOut	= new QAction( tr("Zoom out"), this );
	m_actZoomOut->setShortcut( QKeySequence(Qt::Key_Minus) );
	new QShortcut(QKeySequence(Qt::Key_Minus),this,SLOT(zoomOut()));
	connect( m_actZoomOut, SIGNAL(triggered()), SLOT(zoomOut()));
	m_canvasContextMenu->addAction(m_actZoomOut);
}

DJCanvasView::~DJCanvasView()
{
	//all items on canvas should have been deleted
	djDebug() << "DJCanvasView destructor" << canvas()->allItems().size();
	clearAllItems();
}
void DJCanvasView::clearAllItems()
{
	djDebug() << "DJCanvasView::clearAllItems";
	qDeleteAll( m_cells );
	m_cells.clear();
	qDeleteAll( m_players );
	m_players.clear();
	delete m_selfReadyItem;
	m_selfReadyItem	= 0;
	delete m_focusRectItem ;
	m_focusRectItem 	= 0;
}
DJCell* DJCanvasView::cell( quint8 tableIndex ) const
{
	return m_cells.at( tableIndex );
}
QList<DJGamePanel*> DJCanvasView::allPanels() const
{
	QList<DJGamePanel*>	panels;
	foreach( DJCell* cell, m_cells ) {
		DJGamePanel	*panel	= cell->gamePanel();
		if ( panel ) {
			panels << panel;
		}
	}
	return panels;
}
QList<quint8> DJCanvasView::allPanelTables() const
{
	QList<quint8>	tableIds;
	foreach( DJCell* cell, m_cells ) {
		if ( cell->gamePanel() ) {
			tableIds << cell->tableId();
		}
	}
	return tableIds;
}
void DJCanvasView::initialize( quint8 numberOfTables, const QPixmap& tablePixmap, const QFont& tableFont, const QColor& tableColor,
			const QList<QPixmap>& seatPixmaps, const QPixmap& selfReadyPixmap, const QPixmap& playingPixmap,
			const QSize& cellSize, const QList<QPoint>& seatOffsets, const QList<QPoint>& avatarOffsets )
{
	djDebug() << "initialize begin" << numberOfTables;
	
	clearAllItems();
	m_cellOrigins.clear();
	
	m_cellSize		= cellSize;
	m_seatOffsets	= seatOffsets;
	m_avatarOffsets	= avatarOffsets;
	
	for ( quint8 tableId = 1; tableId <= numberOfTables; tableId++  ) {
		DJCell	*cell	= new DJCell( tableId, tablePixmap, tableFont, tableColor, seatPixmaps, playingPixmap, this );
		m_cells << cell;
	}
	m_selfReadyItem	= new DJCanvasItem( canvas(), selfReadyPixmap, DJCanvasItem::Ready  );
	m_selfReadyItem->setZ( ZORDER_READY );
	
	m_focusRectItem	= new Q3CanvasRectangle( canvas() );
	m_focusRectItem->setPen( QPen(Qt::green) );
	m_focusRectItem->setSize( m_cellSize.width(), m_cellSize.height() );
	m_focusRectItem->setZ( ZORDER_FOCUS );
	m_focusRectItem->hide();
	
	QSettings settings;
    settings.beginGroup( m_gameController->gameName() );
    qreal	scale	= settings.value( KEY_GAME_CANVASVIEW_SCALE, 1 ).toDouble();
    settings.endGroup();
    
	QMatrix m;
	if ( scale != m.m11() ) {
		m.setMatrix( scale, m.m12(), m.m21(), scale, m.dx(), m.dy() );
		setWorldMatrix( m );
		emit viewScaleChanged( scale );
	}
	
	resizeEvent( 0 );
	
	djDebug() << "initialize end";
}
void DJCanvasView::addUser( DJGameUser* user, const QPixmap& readyPixmap,
				const QFont& nameFont, const QColor& nameColor )
{
	if ( m_players.contains( user ) )
		return;
	DJPlayer	*player	= new DJPlayer( user, readyPixmap, nameFont, nameColor, this );
	m_players.insert( user, player );
	
	connect( user, SIGNAL(trinityChanged(DJGameUser*,quint8,quint8,quint8)), player, SLOT(draw()) );
	connect( user, SIGNAL(trinityChanged(DJGameUser*,quint8,quint8,quint8)),SLOT(changeContents(DJGameUser*,quint8,quint8,quint8)) );
	connect( user, SIGNAL(reloaded(DJGameUser*)), SLOT(reloadUser(DJGameUser*)) );
	
	djDebug() << "adduser";
	changeContents( user, 0, 0, GAME_STATUS_IDLE );
	player->draw();
	djDebug() << "adduser end";
}
void DJCanvasView::reloadUser( DJGameUser* user )
{
	djDebug() << "DJCanvasView::reloadUser" << user->userName();
	if ( !m_players.contains( user ) )
		return;
	DJPlayer	*player	= m_players.value( user );
	player->reload();
	player->draw();
}
void DJCanvasView::removeUser( DJGameUser* user )
{
	if ( !m_players.contains( user ) )
		return;
	user->setTrinity( 0, 0, GAME_STATUS_IDLE );
	DJPlayer	*player	= m_players.take( user );
	delete player;
}
/*
void DJCanvasView::requestTableMap( quint8 minTableId, quint8 maxTableId )
{
	m_gameController->requestTableMap( minTableId, maxTableId );
}
void DJCanvasView::requestUserInfo( quint32 userId )
{
	m_gameController->requestUserInfo( userId );
}
*/
void DJCanvasView::resizeEvent( QResizeEvent * event )
{	
	djDebug() << "DJCanvasView::resizeEvent" << visibleWidth() << visibleHeight() << width() << height();
	if ( event )//system call
		Q3CanvasView::resizeEvent(event);
	int width	= static_cast<int>( visibleWidth() * inverseWorldMatrix().m11() );
	//int height	= visibleHeight();
	
	if ( m_cells.isEmpty() ) {
		return;
	}
	
	int	intervalx;
	int intervaly	= 20;
	
	if ( width <= 0 || visibleHeight() <= 0 )
		return;
	
	int columns	= width / m_cellSize.width();
	int space			= width % m_cellSize.width();
	if ( 0 == columns )  {
		columns = 1;
		intervalx	= 0;
	}else {
		intervalx	= space / columns;
	}
	int rows	= ( m_cells.size() + columns - 1) / columns;
	djDebug("columns=%d,rows=%d",columns,rows);
	
	int height	= (m_cellSize.height() + intervaly) * rows + CANVASVIEW_MARGIN_Y;
	
	if ( height * worldMatrix().m11() < visibleHeight() ) {
		height	= static_cast<int>(visibleHeight() * inverseWorldMatrix().m11());
	}
	djDebug()<<"canvas resize to"<<width<<height;
	canvas()->resize( width, height );
	
	int i,j;
	
	m_cellOrigins.clear();
	
	int x	= intervalx/2;
	int y	= CANVASVIEW_MARGIN_Y;
    for ( i = 0; i < rows; i++ ) {
    	for ( j = 0; j < columns; j++ ) {
    		m_cellOrigins << QPoint( x, y );
    		x	+= m_cellSize.width() + intervalx;
    	}
    	x	= intervalx/2;
    	y	+= m_cellSize.height() + intervaly;
	}
	
	foreach( DJCell *cell, m_cells ) {
		cell->draw();
	}
	foreach( DJPlayer *player, m_players ) {
		player->draw();
	}
}
void DJCanvasView::contentsMousePressEvent( QMouseEvent * e )
{
	//djDebug("DJCanvasView::contentsMousePressEvent x = %d,y = %d",e->x(),e->y());
	
	Q3CanvasView::contentsMousePressEvent(e);
	QPoint	point	= inverseWorldMatrix().map( e->pos() );
	Q3CanvasItemList list = canvas()->collisions( point );
	for( Q3CanvasItemList::Iterator it	= list.begin(); it != list.end(); ++it ) {
		if ( DJCanvasItem::RTTI != (*it)->rtti() )
			continue;
		DJCanvasItem	*item	= static_cast<DJCanvasItem *>(*it);
    	//djDebug() << "type = " << item->type();
    	switch ( item->type() ) {
    	case DJCanvasItem::Table:
    		if ( item->hit( point ) )
    			return;
    		break;
    	case DJCanvasItem::Seat: {
    		if ( Qt::LeftButton == e->button() ) {
        		quint16 seatId	= item->id();
        		//djDebug()<<"click seat id = "<< (seatId>>8) << (seatId&0xff);
        		emit sitDown( seatId>>8, seatId&0xff );
        	}
    		return;
    	}
    	case DJCanvasItem::Ready:
    		if ( Qt::LeftButton == e->button() ) {
        		emit ready();
        	}
    		return;
    	case DJCanvasItem::Avatar: {
    		DJCanvasAvatar*	avatarItem	= static_cast<DJCanvasAvatar *>(item);
    		DJGameUser*	user	= avatarItem->user();
    		if ( Qt::LeftButton == e->button() ) {
        		if ( user->isSelf() ) {
            		emit standUp();
            	}else {
            		emit leftClicked( user );
            	}
    		}else {
    			emit rightClicked( user );
    		}
    		return;
    	}
    	default:
    		break;
    	}
	}
	if ( Qt::RightButton == e->button() ) {
    	popupCanvasContextMenu();
    }
}
void DJCanvasView::contentsMouseDoubleClickEvent( QMouseEvent * e )
{
	djDebug() << "DJCanvasView::contentsMouseDoubleClickEvent";
	Q3CanvasView::contentsMouseDoubleClickEvent(e);
	QPoint	point	= inverseWorldMatrix().map( e->pos() );
	Q3CanvasItemList list = canvas()->collisions( point );
	for( Q3CanvasItemList::Iterator it	= list.begin(); it != list.end(); ++it ) {
		if ( DJCanvasItem::RTTI != (*it)->rtti() )
			continue;
		DJCanvasItem	*item	= static_cast<DJCanvasItem *>(*it);
    	djDebug() << "type = " << item->type();
    	switch ( item->type() ) {
    	case DJCanvasItem::Avatar: {
    		DJCanvasAvatar*	avatarItem	= static_cast<DJCanvasAvatar *>(item);
    		DJGameUser*	user	= avatarItem->user();
    		if ( Qt::LeftButton == e->button() ) {
            	emit leftDoubleClicked( user );
    		}
    		return;
    	}
    	default:
    		break;
    	}
	}
}
void DJCanvasView::changeContents( DJGameUser* user, quint8 oldTableId, quint8 oldSeatId, quint8 oldGameStatus )
{
	djDebug() << m_gameController->gameName() << "changeContents" << user->userName()
						<< oldTableId << oldSeatId << oldGameStatus << "-->"
						<< user->tableId() << user->seatId() << user->gameStatus();
	quint8 tableId	= user->tableId();
	quint8 seatId	= user->seatId();
	//quint8 gameStatus	= user->gameStatus();
	DJCell* oldCell	= 0;
	DJCell*	newCell	= 0;
	
	if ( oldTableId ) {
		oldCell	= cell(oldTableId - 1);
	}
	if ( tableId ) {
		newCell	= cell(tableId - 1);
	}
	
	if ( oldTableId != tableId || oldSeatId != seatId ) {
		//table seat changed
    	if ( oldCell ) {
    		oldCell->removeUser( user );
    	}
    	if ( newCell ) {
    		newCell->addUser( user );
    	}
	}
	djDebug() << "end changeContents";
}
void DJCanvasView::zoomIn()
{
	djDebug() << "zoomIn";
	QMatrix m	= worldMatrix();
	qreal scale	= m.m11();
	djDebug() << "scale =" << scale;
	if ( scale > MAX_ZOOM )
		return;
	scale	+= ZOOM_STEP;
	setScale( scale );
	emit viewScaleChanged( scale );
}
void DJCanvasView::zoomOut()
{
	djDebug() << "zoomOut";
	QMatrix m	= worldMatrix();
	qreal scale	= m.m11();
	djDebug() << "scale =" << scale;
	if ( scale < MIN_ZOOM ) {
		return;
	}
	scale	-= ZOOM_STEP;
	setScale( scale );
	emit viewScaleChanged( scale );
}
void DJCanvasView::revert()
{
	qreal scale	= 1;
	setScale( scale );
	emit viewScaleChanged( scale );
}
void DJCanvasView::setScale( qreal scale )
{
	djDebug() << "setScale" << scale;
	QMatrix m;
	m.setMatrix( scale, m.m12(), m.m21(), scale, m.dx(), m.dy() );
	setWorldMatrix( m );
	resizeEvent( 0 );
	
	QSettings settings;
    settings.beginGroup( m_gameController->gameName() );
    settings.setValue( KEY_GAME_CANVASVIEW_SCALE, scale );
    settings.endGroup();
}
void DJCanvasView::setFocusTable( quint8 tableId, bool drawRect )
{
	if ( 0 == tableId ) {
		m_focusRectItem->hide();
	}else {
    	//djDebug()<<"canvas size"<<canvas()->width()<<canvas()->height();
    	//djDebug()<<"visible size"<<visibleWidth()<<visibleHeight();
    	//djDebug()<<"cell size"<<m_cellSize;
    	//djDebug()<<"contents size"<<contentsWidth()<<contentsHeight();
    	
    	QRect	viewContentsRect	= QRect( contentsX(), contentsY(), visibleWidth(), visibleHeight() );
    	//djDebug()<<"viewContentsRect"<<viewContentsRect;
    	
    	QPoint	canvasPoint	= m_cellOrigins.value( tableId - 1, QPoint() );
    	QPoint	contentsPoint	= worldMatrix().map( canvasPoint );
    	//djDebug()<<"canvasPoint"<<canvasPoint;
    	//djDebug()<<"contentsPoint"<<contentsPoint;
    	
    	QRect 	tableCanvasRect	= QRect( canvasPoint, m_cellSize );
    	QRect	tableContentsRect	= worldMatrix().map( tableCanvasRect );
    	//djDebug()<<"tableCanvasRect"<<tableCanvasRect;
    	//djDebug()<<"tableContentsRect"<<tableContentsRect;
    	
    	if ( !viewContentsRect.contains( tableContentsRect ) ) {
    		setContentsPos( 0, contentsPoint.y() - CANVASVIEW_MARGIN_Y );
    	}
    	if ( drawRect ) {
    		m_focusRectItem->move( canvasPoint.x(), canvasPoint.y() );
    		m_focusRectItem->show();
    	}else
    		m_focusRectItem->hide();
	}
	canvas()->update();
}
void DJCanvasView::popupCanvasContextMenu()
{
	qreal scale	= worldMatrix().m11();
	m_actZoomIn->setEnabled( !(scale > MAX_ZOOM) );
	m_actRevert->setEnabled( !(1 == scale) );
	m_actZoomOut->setEnabled( !(scale < MIN_ZOOM) );
	m_canvasContextMenu->exec( QCursor::pos() );
}
/*
void DJCanvasView::contentsMouseMoveEvent ( QMouseEvent * e )
{
	djDebug() << "DJCanvasView::contentsMouseMoveEvent";
}
void DJCanvasView::contentsMouseReleaseEvent( QMouseEvent * e )
{
	djDebug() << "DJCanvasView::contentsMouseReleaseEvent";
}
*/
