/*
	AramakiOnline
	Copyright (C) 2008 superbacker

	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 3 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, see <http://www.gnu.org/licenses/>.
 */

#include "MyCharacter.h"
#include "Utility.h"
#include "Point2d.h"
#include "MainFrame.h"
#include "World.h"

MyCharacter *MyCharacter::instance;

BEGIN_EVENT_TABLE(MyCharacter,wxEvtHandler)
	EVT_TIMER(ID_SEND_POSITION_TIMER,MyCharacter::onSendPositionTimer)
END_EVENT_TABLE()

MyCharacter::MyCharacter(Map &map,P2PNetwork &network) : direction(0),map(map),network(network),sendPositionTimer(this,ID_SEND_POSITION_TIMER),changedPosition(false) {
	network.sendMap(map);

	//位置を送信する回数を減らしネットワーク負荷を緩和
	sendPositionTimer.Start(100,false);
}

void MyCharacter::turnLeft() {
	direction=(direction-5)%360;
}

void MyCharacter::turnRight() {
	direction=(direction+5)%360;
}

void MyCharacter::goAhead() {
	Point3d oldPosition=position;

	position.x+=sin(toRadian(direction))*0.1;
	position.z-=cos(toRadian(direction))*0.1;

	correctPosition(position,oldPosition);

	changedPosition=true;
}

void MyCharacter::goBack() {
	Point3d oldPosition=position;

	position.x-=sin(toRadian(direction))*0.1;
	position.z+=cos(toRadian(direction))*0.1;

	correctPosition(position,oldPosition);

	changedPosition=true;
}

void MyCharacter::setPosition(const Point3d &_value) {
	Point3d lineStart,lineEnd;

	Character::setPosition(_value);

	lineStart=lineEnd=position;
	lineStart.y-=0.3;
	lineEnd.y+=0.3;

	//currentFaceを更新
	if (!map.collide(lineStart,lineEnd,position,currentFace)) wxLogFatalError(wxT("不正な位置です"));

	changedPosition=true;
}

void MyCharacter::correctPosition(Point3d &position,const Point3d &oldPosition) {
	Point3d lineStart,lineEnd;

	lineStart=lineEnd=position;
	lineStart.y-=0.3;
	lineEnd.y+=0.3;

	if (!currentFace.collide(lineStart,lineEnd,position)) {
		//面の外に出た
		if (!map.collide(lineStart,lineEnd,position,currentFace)) {
			//どの面にも当たっていない
			//壁ずり処理
			Point2d oldPosition2d(oldPosition.x,oldPosition.z),position2d(position.x,position.z);

			for (size_t i=0;i<currentFace.vertexes.size();++i) {
				//現在乗っている面の辺と移動元→移動先の線分で当たり判定
				Point2d edgeStart(currentFace.vertexes[i].x,currentFace.vertexes[i].z),edgeEnd(currentFace.vertexes[(i+1)%currentFace.vertexes.size()].x,currentFace.vertexes[(i+1)%currentFace.vertexes.size()].z);

				if (collideLine(oldPosition2d,position2d,edgeStart,edgeEnd)) {
					double t; //媒介変数
					Point2d v1,v2,v3,crossPoint,nv;

					nv.normalVector(edgeStart,edgeEnd);
					nv*=0.01;

					v1=edgeEnd-edgeStart;
					v2=position2d-edgeStart;
					t=v1.dot(v2)/v1.dot(v1);
					v3=v1*t;
					//交点を求める
					crossPoint=edgeStart+v3;

					//法線方向に押し戻す
					lineStart.x=lineEnd.x=crossPoint.x-nv.x;
					lineStart.z=lineEnd.z=crossPoint.y-nv.y;

					//高さを設定
					if (t<0.0||t>1.0) {
						//面の外
						if (!map.collide(lineStart,lineEnd,position,currentFace)) break;
					} else {
						if (!currentFace.collide(lineStart,lineEnd,position)) break;
					}

					return;
				}
			}

			//どこにも当たっていない
			position=oldPosition;
		}
	}
}
void MyCharacter::setMessage(const wxString &_value) {
	if (balloon.getText()==_value) return;
	Character::setMessage(_value);
	network.sendMessage(_value);
}

void MyCharacter::setName(const wxString &_value) {
	if (name.getText()==_value) return;
	Character::setName(_value);
	network.sendName(_value);
}

void MyCharacter::onSendPositionTimer(wxTimerEvent &event) {
	if (changedPosition) {
		network.sendPosition(position);
		changedPosition=false;
	}
}
