#include "ats_MayaBoid.h"
#include "ats_Point.h"
#include "ats_Matrix3D.h"
#include "ats_MBoidGlobal.h"
#include <time.h>
#include <sstream>
#include <maya/MSelectionList.h>
#include <maya/MItSelectionList.h>

// 識別子が長すぎるワーニングを無効
#pragma warning (disable : 4786)

using namespace ats;
using namespace std;

MayaBoid::MayaBoid(MDagPath& path, int tail_num, double speed,
                   double max, double weight1, double weight2, double weight3)
: Boid(max, max, tail_num, speed), m_acFnSet(6),
  mo_group_weight(weight1), mo_group_muki_weight(weight2), mo_kansei_weight(weight3),
m_crazy(rand())
{
	MStatus status;
	MFnDagNode dag(path, &status);
		if ( MS::kSuccess != status )
			throw MException("Failure Creating MFnDagNode in MayaBoid Constructor \n", status);

	// これではコネクションまで再現されない
	/*m_maya_obj = dg.duplicate(true, false, &status);
		if ( MS::kSuccess != status )
			throw MException("Failure duplicate in MayaBoid Constructor\n", status);
	*/
	MString original_name(dag.name());
	MString command("instance ");
	MStringArray result;

	// MEL instance 発行
	status = MGlobal::executeCommand(command + original_name, result, false);
		if ( MS::kSuccess != status ){
			throw MException("Failure instance in MayaBoid Constructor\n", status);
		}

	MSelectionList s;
	status = s.add(result[0]);
		if ( MS::kSuccess != status )
			throw MException("Failure selection list add in MayaBoid Constructor\n", status);

	//status = s.getDagPath(0, m_dag_path, m_maya_obj);
	// getDagPathでは常にkNullObjが返る、なぜ？

	status = s.getDependNode (0, m_maya_obj);
		if(MS::kSuccess != status)
			throw MException("Failure selection list getDagPath in MayaBoid Constructor\n", status);
		if(MObject::kNullObj == m_maya_obj)
			throw MException("Failure selection list getDagPath returned null obj in MayaBoid Constructor\n", status);

	// DAGNodeFnSetとObjectの関連付け
	status = m_fn_dag_node.setObject(m_maya_obj);
		if ( MS::kSuccess != status )
			throw MException("Failure MFnDagNode::setObject in MayaBoid Constructor\n", status);

	status = m_fn_dag_node.getPath(m_dag_path);
		if(MS::kSuccess != status)
			throw MException("Failure GetDagPath in MayaBoid Constructor\n", status);

	MakeFnSet();
}

MayaBoid::~MayaBoid()
{

}

void MayaBoid::MakeFnSet()
{
	int attr_num = 6;
	vector<MObject> attr(attr_num);
	vector<MString> attrName(attr_num);
	attrName[0] = "translateX";
	attrName[1] = "translateY";
	attrName[2] = "translateZ";
	attrName[3] = "rotateX";
	attrName[4] = "rotateY";
	attrName[5] = "rotateZ";
	//MFnAnimCurve::AnimCurveType curve_type = MFnAnimCurve::kAnimCurveTL;
	MStatus status;

	for(int i=0; i < 6; i++){
		// DagNodeFnSetからアトリビュートのハンドルを取得
		attr[i] = m_fn_dag_node.attribute( attrName[i], &status );
		if ( MS::kSuccess != status ) {
			throw MException("Failure Crating attribute in MayaBoid::MakeFnSet\n", status);
		}
		MObject trans = m_dag_path.transform(&status);
		if ( MS::kSuccess != status ) {
			throw MException("Failure get dagpath transfrom in MayaBoid::MakeFnSet\n", status);
		}
		// AnimCurve関数セットからオブジェクトの作成
		//if(i > 2){ curve_type = MFnAnimCurve::kAnimCurveTA; }
		m_acFnSet[i].create(trans, attr[i], NULL, &status);
		if(MS::kSuccess != status){
			stringstream buf;
			buf << "Failure creat MFnAnimCurve in MayaBoid::MakeFnSet" << endl
		        << "bad attribute : " << attrName[i].asChar() << endl;
			    //<< "error code : " << error << endl;
			throw MException(buf.str(), status);
		}
	}
}

// メモ : Nearをはしょってないやつ
//if(m_parent != 0 && m_near != 0){
// OK m_muki = ((m_parent->Center()-m_center)*0.03 + m_parent->Muki()*0.03 + m_muki*0.95 + m_near->GetMuki()*0.01);
// メモ : Nearをはしょったやつ
// m_muki = ((m_parent->Center()-m_center)*0.05 + m_parent->Muki()*0.08 + m_muki*1.1);

void MayaBoid::UpdateTime(double time)
{
	m_time = time;

	if(m_parent != 0){
		// 中心へ ＋ 群への協調 ＋ 慣性 (＋ 衝突回避は重いのでなし)
		//m_muki = ((m_parent->Center()-m_center)*0.23 + m_parent->Muki()*0.023 + m_muki*1.5);
		//m_muki.Normalize() *= m_speed;

		//0.05 0.08 1.1

		m_muki = ((m_parent->Center()-m_center) * mo_group_weight
				 + m_parent->Muki() * mo_group_muki_weight
				 + m_muki * mo_kansei_weight);
		// 200回に一回狂う(笑)
		if(!(m_crazy++ % 200)){
			const BoundingFrame& b = m_parent->GetBoundingFrame();
			Point3D tmp(m_center - b.GetCenter());
			m_muki = -tmp.GetNormal(); // 中心向きのベクトルを加算
		}
		m_muki.Normalize() *= m_speed;

		IsCrash(); // ボックスから飛び出てれば反転
		UpdateTail(); // 尾の更新

		m_center += m_muki * 0.03;
	}
}

void MayaBoid::Draw()
{
	const double PI = 3.1415926535897932384626433832795;

	// 時間取得
	MTime tm( (double)m_time, MTime::kFilm );

	// 移動
	if((MS::kSuccess != m_acFnSet[0].addKeyframe(tm, m_center.X()*20)) ||
	   (MS::kSuccess != m_acFnSet[1].addKeyframe(tm, m_center.Y()*20)) ||
	   (MS::kSuccess != m_acFnSet[2].addKeyframe(tm, m_center.Z()*20))) {
		throw  MException("Error setting the keyframe 1\n", MStatus(MS::kFailure));
	}

	Point3D xnorm(1,0,0), ynorm(0,1,0), znorm(0,0,1);
	Point3D vec(m_muki);

	Point3D y_vec(vec); y_vec.Y(0); // Y軸回転
	double y_ang = y_vec.Angle(znorm);

	Matrix3DD tmp; // 回転計算
	if(vec.X() < 0){
		tmp.RotateY(-y_ang);
	} else {
		tmp.RotateY(y_ang);
	}

	Point3D x_vec(vec*tmp); // vec*tmp : 回転を逆に辿る
	x_vec.X(0);
	double x_ang = x_vec.Angle(znorm); // X軸回転
	x_ang *= -1;

	if(vec.X() < 0){
		y_ang = PI + PI - y_ang; // ZX平面の単位Zと垂直な方向で+-のどっちか
	}
	if(vec.Y() < 0){
		x_ang = PI + PI - x_ang;
	} 

	// 3,4,5 = rotate x, y, z
	if((MS::kSuccess != m_acFnSet[3].addKeyframe(tm, x_ang)) ||
	   (MS::kSuccess != m_acFnSet[4].addKeyframe(tm, y_ang)))// ||
	   //(MS::kSuccess != m_acFnSet[5].addKeyframe(tm, 0)))
	{
		throw  MException("Error setting the keyframe 2\n", MStatus(MS::kFailure));
	}
}

bool MayaBoid::IsCrash()
{
	// ボックスの外側にあって
	const BoundingFrame& b = m_parent->GetBoundingFrame();
	double out = b.IsOut(m_center * 20.0);
	if(out > 0){ // キーフレームをつけるときに20倍してるから
		//m_center = ((m_center - b.GetCenter()) * 0.98) + b.GetCenter(); // ちょっと中心に寄せてやってから
		//m_muki *= -1; // 方向反転

		Point3D tmp(m_center - b.GetCenter());
		m_muki += -tmp.GetNormal() * 0.3; // 中心向きのベクトルを加算
		return true;
	}
	else {
		m_muki += (Point<double>(rand()%10-5,rand()%10-5,rand()%10-5)).Normalize()*0.057;
		//m_muki.Normalize();
		return false;
	}
}

void MayaBoid::UpdateTail()
{
	*m_tail_itr = m_center;

	if(m_tail_itr == m_tail.begin()){ // イテレータを逆向きに移動
		m_tail_itr = m_tail.end();    // 一個ずらすもすべてコピーするも同じ
		m_tail_itr--;
	} else {
		m_tail_itr--;
	}
}

