/***************************************************************************
 *   Copyright (C) 2004 by ian reinhart geiser                             *
 *   ian@geiseri.com                                                       *
 *                                                                         *
 *   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.             *
 ***************************************************************************/
#include "boardmodel.h"
#include <QPixmap>
#include <QBrush>
#include <math.h>
#include "levelgenerator.h"
#include "bandit.h"

int boardXSize = 1;
int boardYSize = 1;

BoardModel::BoardModel(QObject *parent)
 : QAbstractTableModel(parent), m_player(0)
{
	m_board.resize(boardXSize*boardYSize);
}


BoardModel::~BoardModel()
{
	qDeleteAll( m_monsters );
	delete m_player;
}

int BoardModel::rowCount(const QModelIndex & parent) const
{
	return boardYSize;
}

int BoardModel::columnCount(const QModelIndex & parent) const
{
	return boardXSize;
}

QVariant BoardModel::data(const QModelIndex & index, int role) const
{
	int x = index.column();
	int y = index.row();

// 	if( !m_board[x + (y * boardXSize)].isVisible() )
// 		return QVariant();

	if( role == Qt::DecorationRole )
	{
		return m_board[x + (y * boardXSize)].pixmap();
	}
	else if( role == Qt::BackgroundRole )
		return m_board[x + (y * boardXSize)].background();
	else
		return QVariant();
}

bool BoardModel::loadMap(const QString & mapName)
{
	QImage map;
	QColor wall("#000000");
	QColor path("#ffffff");
	QColor water("#0000ff");

	if( map.load( mapName, "XPM" ) )
	{
		for( int xIndex = 0; xIndex < map.width(); ++xIndex )
			for( int yIndex = 0; yIndex < map.height(); ++yIndex )
			{
				if( QColor( map.pixel( xIndex, yIndex ) ) == wall )
					m_board[  boardIndex( xIndex, yIndex) ].setTileBackground(Cell::WallType);
				else if( QColor( map.pixel( xIndex, yIndex ) ) == path )
					m_board[  boardIndex( xIndex, yIndex) ].setTileBackground(Cell::PathType);
				else if( QColor( map.pixel( xIndex, yIndex ) ) == water )
					m_board[ boardIndex( xIndex, yIndex) ].setTileBackground(Cell::WaterType);
				else
					m_board[ boardIndex( xIndex, yIndex) ].setTileBackground(Cell::PathType);
				dataChanged ( index(yIndex,xIndex), index(yIndex,xIndex) );
			}
		return true;
	}
	else
		return false;

}

int BoardModel::boardIndex(int X, int Y) const
{
	return X + ( boardXSize * Y );
}

void BoardModel::updatePosition(int oldX, int oldY, int newX, int newY )
{
	updateMonsters();
	if( m_board.at( boardIndex( newX, newY ) ).tileBackground() == Cell::WallType ||
	    m_board.at( boardIndex( newX, newY ) ).tileBackground() == Cell::WaterType )
	{
		return;
	}
	else if( m_board.at( boardIndex( newX, newY ) ).tileForground() == Cell::MonsterType )
	{
	// if monster attack restore old XY
		Monster *monster = m_monsters[monsterAtPoint(newX,newY)];
		if( m_player->melee(monster, CUT) )
			emit status( tr("%1 hits...").arg(m_player->name()));
		else
			emit status( tr("%1 misses...").arg(m_player->name()));
		if( monster->hitpoints() <= 0 )
		{
			emit status( tr("%1 killed %2...").arg(m_player->name()).arg(monster->name()));
			m_monsters.removeAll( monster );
			m_board[ boardIndex( newX, newY ) ].clearMonster();
			m_board[ boardIndex( newX, newY ) ].setTileBackground( Cell::PathType );
			delete monster;
			// Drop items
			// Drop gold
			// get experience
			return;
		}
		else if( m_player->hitpoints() <= 0  )
		{
			playerDead();
			return;
		}
		else
			return;
	}
	else if( m_board.at( boardIndex( newX, newY ) ).tileForground() == Cell::ItemType )
	{

	}

	m_player->setXPosition( newX );
	m_player->setYPosition( newY );
	m_board[ boardIndex( oldX, oldY ) ].clearPlayer();
	m_board[ boardIndex( newX, newY ) ].setPlayer( m_player );
	updateView( newX, newY );

	dataChanged ( index(newY,newX), index(oldY,oldX) );

}

void BoardModel::movePlayerUp()
{
	int oldX = m_player->xPosition();
	int oldY = m_player->yPosition();
	int newX = oldX;
	int newY = oldY - 1;
	updatePosition( oldX, oldY, newX, newY );
}

void BoardModel::movePlayerDown()
{
	int oldX = m_player->xPosition();
	int oldY = m_player->yPosition();
	int newX = oldX;
	int newY = oldY + 1;
	updatePosition( oldX, oldY, newX, newY );
}

void BoardModel::movePlayerLeft()
{
	int oldX = m_player->xPosition();
	int oldY = m_player->yPosition();
	int newX = oldX - 1;
	int newY = oldY;
	updatePosition( oldX, oldY, newX, newY );
}

void BoardModel::movePlayerRight()
{
	int oldX = m_player->xPosition();
	int oldY = m_player->yPosition();
	int newX = oldX + 1;
	int newY = oldY;
	updatePosition( oldX, oldY, newX, newY );
}

void BoardModel::addPlayer( Player *player, int Xpos, int Ypos )
{
	m_player = player;
	m_player->setXPosition( Xpos );
	m_player->setYPosition( Ypos );
	m_board[ boardIndex( Xpos, Ypos ) ].setPlayer( m_player );
	updateView( Xpos, Ypos );
	dataChanged ( index(Ypos,Xpos), index(Ypos,Xpos) );
}

void BoardModel::addMonster( Monster *monster, int Xpos, int Ypos )
{
	monster->setXPosition( Xpos );
	monster->setYPosition( Ypos );
	m_board[ boardIndex( Xpos, Ypos ) ].setMonster( monster );
	m_monsters << monster;
}

int BoardModel::monsterAtPoint( int Xpos, int Ypos ) const
{
	Monster *monster = m_board[ boardIndex( Xpos, Ypos ) ].monster();
	if( monster )
	{
		return m_monsters.indexOf( monster );
	}
	else
		return -1;
}

void BoardModel::generateMap(  int width, int height )
{
	boardXSize = width;
	boardYSize = height;
	int density = rowCount() * columnCount();
	m_board.resize(boardXSize*boardYSize);
	resetModel();
	LevelGenerator level(boardXSize, boardYSize, density );
	level.MazeGen( 1 );
	for( int xIndex = 0; xIndex < level.width(); ++xIndex )
		for( int yIndex = 0; yIndex < level.height(); ++yIndex )
		{
			if( level.cell( xIndex, yIndex) == WALL )
				m_board[  boardIndex( xIndex, yIndex) ].setTileBackground(Cell::WallType);
			else if( level.cell( xIndex, yIndex) == FLOOR )
				m_board[  boardIndex( xIndex, yIndex) ].setTileBackground(Cell::PathType);
			else if( level.cell( xIndex, yIndex) == MONSTER)
			{
				Monster *mon = Monster::monsterFactory("Bandit");
				addMonster( mon, xIndex, yIndex);
			}
		}

	if( m_player )
	{
		m_player->setXPosition( boardXSize/2 );
		m_player->setYPosition( boardYSize/2 );
		m_board[ boardIndex( boardXSize/2, boardYSize/2 ) ].setPlayer( m_player );
		updateView( m_player->xPosition(), m_player->yPosition());
	}
	reset();
}

void BoardModel::updateView( int newX, int newY )
{
	int visionSize = qMax( boardXSize -1, boardYSize -1);
	m_board[ boardIndex(newX,newY)].setIsVisible(true);

	for( int idxX = -visionSize; idxX < visionSize; ++idxX )
		for( int idxY = -visionSize; idxY < visionSize; ++idxY )
		{
			int xpos = qBound(0, newX + idxX, boardXSize -1);
			int ypos = qBound(0, newY + idxY, boardYSize -1);
			if(m_board[ boardIndex(xpos,ypos)].tileBackground() != Cell::WallType)
				m_board[ boardIndex(xpos, ypos)].setIsVisible(false);
		}

	for( int idx = 0; idx <= 360; idx += 1 )
	{
		qreal rad = idx * 3.14 / 180.0;
		for( int hyp = 1; hyp < visionSize; ++hyp )
		{
			int xpos = qBound(0, newX + (int)(cos( rad ) * hyp), boardXSize -1);
			int ypos = qBound(0, newY + (int)(sin( rad ) * hyp), boardYSize -1);

			m_board[ boardIndex(xpos, ypos)].setIsVisible(true);
			if(m_board[ boardIndex(xpos,ypos)].tileBackground() == Cell::WallType)
						break;
		}
	}

	dataChanged ( index(qMax( newY - visionSize * 2, 0),qMax( newX - visionSize, 0)),
	              index(qMin(newY + visionSize * 2, boardYSize),qMin(newX + visionSize, boardXSize)) );

}

QVariant BoardModel::headerData( int section, Qt::Orientation orientation, int role ) const
{
     if (role == Qt::SizeHintRole)
         return QSize(1, 1);
     return QVariant();
}

void BoardModel::updateMonsters( )
{
	foreach( Monster *monster, m_monsters )
	{

		int Xpos = monster->xPosition( );
		int Ypos = monster->yPosition( );
		m_board[ boardIndex( Xpos, Ypos ) ].clearMonster( );
		int newXpos = Xpos;
		int newYpos = Ypos;

		if( m_player->xPosition() > newXpos ) ++newXpos;
		if( m_player->xPosition() < newXpos ) --newXpos;
		if( m_player->yPosition() > newYpos ) ++newYpos;
		if( m_player->yPosition() < newYpos ) --newYpos;

		if(m_board[ boardIndex(newXpos,newYpos)].tileForground() == Cell::PlayerType)
		{
			if( monster->melee(m_player, monster->attackType() ) )
				emit status( tr("%1 hits...").arg(monster->name()));
			else
				emit status( tr("%1 misses...").arg(monster->name()));
			newXpos = Xpos;
			newYpos = Ypos;
		}

		if(m_board[ boardIndex(newXpos,newYpos)].tileBackground() != Cell::PathType)
			newXpos = Xpos;
		if(m_board[ boardIndex(newXpos,newYpos)].tileBackground() != Cell::PathType)
			newYpos = Ypos;

		monster->setXPosition( newXpos );
		monster->setYPosition( newYpos );
		m_board[ boardIndex( newXpos, newYpos ) ].setMonster( monster );
		dataChanged ( index(Ypos-1,Xpos-1), index(Ypos+1,Xpos+1) );
	}
}

void BoardModel::resetModel( )
{
	for( int idxX = 0; idxX < boardXSize; ++idxX)
		for( int idxY = 0; idxY < boardYSize; ++idxY)
		{
			m_board[ boardIndex( idxX, idxY ) ] = Cell();
			m_board[ boardIndex( idxX, idxY ) ].setTileBackground( Cell::WallType );
			m_board[ boardIndex( idxX, idxY ) ].setIsVisible( false );
		}
	qDeleteAll( m_monsters );
	m_monsters.clear();
	qDeleteAll( m_items );
	m_items.clear();

	if( m_player )
	{
		m_player->setXPosition( boardXSize/2 );
		m_player->setYPosition( boardYSize/2 );
		m_board[ boardIndex( boardXSize/2, boardYSize/2 ) ].setPlayer( m_player );
	}
	reset();
}

