// 識別子が長すぎるワーニングを無効
#pragma warning (disable : 4786)

#include "ats_boid.h"
#include "ats_BoidGroup.h"
#include <cstdlib>
#include <iostream>
#include <limits>

using namespace std;
using namespace ats;


// Boid クラスメンバ ---------------------------------------------------------------------
//double g_one = 1.5;
#define g_one 1.5
double Boid::m_box_right  =  g_one;
double Boid::m_box_left   = -g_one;
double Boid::m_box_top    =  g_one;
double Boid::m_box_bottom = -g_one;
double Boid::m_box_front  =  g_one;
double Boid::m_box_back   = -g_one;
double Boid::m_box_max    =  g_one;
double Boid::m_box_min    = -g_one;

// コンストラクタ・デストラクタ ----------------------------------------------------------

inline double drand(int max, double dmax){ return static_cast<double>(rand() % max) / 100 - dmax; };

Boid::Boid(int max, int max2, int tail_num, double speed)
: m_parent(0),
m_tail_num(tail_num),
m_tail(m_tail_num),
m_tail_itr(m_tail.begin()),
m_speed((10 - rand() % 4) * 0.1 * speed) // 10〜7
{
	// 位置と速度の最大値
	double dmax  = max;
	double dmax2 = max2;
	dmax  /= 200;
	dmax2 /= 200;
	m_center.SetXYZ(drand(max,dmax), drand(max,dmax), drand(max,dmax));
	m_muki.SetXYZ  (drand(max,dmax), drand(max,dmax), drand(max,dmax));
}

// コピーコンストラクタ -----------------------------------------------------------------

Boid::Boid(const Boid& boid)
{

}


Boid::~Boid()
{
}

// UpdateTimeの実装サンプル
void Boid::UpdateTime(double time)
{
	bool crush_flag = false;
	// そのまま
	//if(m_parent != 0 && m_near != 0){
	// Nearはしょる
	if(m_parent != 0){
		// 次の方向 = 中央への方向 + 現在の方向 + 群れの方向
		// 向き = 群への協調 + 自分の慣性 + ニアミスを避ける。
		//Point3D go_parent(m_parent->Center() - m_center);
		//Point3D not_crush(m_center - m_near->m_center);

		// 中心へ ＋ 群への協調 ＋ 慣性 ＋ 衝突回避
		// 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.23 + m_parent->Muki()*0.023 + m_muki*1.5 + m_near->GetMuki()*0.01);
		// Nearをはしょったやつ

		m_muki = ((m_parent->Center()-m_center) * 0.05
		         + m_parent->Muki() * 0.08
		         + m_muki * 1.1);
		m_muki.Normalize() *= m_speed;

		// ボックスの外側にあって
		if(m_center.X() > m_box_max || m_center.X() < m_box_min ||
		   m_center.Y() > m_box_max || m_center.Y() < m_box_min ||
		   m_center.Z() > m_box_max || m_center.Z() < m_box_min)
		{
			m_center *= 0.98; // ちょっと中心に寄せてやってから
			m_muki *= -0.5; // 方向反転
		}
		else {
			m_muki += (Point<double>(rand()%10-5,rand()%10-5,rand()%10-5)).Normalize()*0.057;
			//m_muki.Normalize();
		}

		// 前の位置を保存
		*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--;
		}
		m_center += m_muki * 0.03;
	}
}


void Boid::SetNear(Boid* a)
{
	m_near = a;
}


Boid* Boid::GetNear() const
{
	return m_near;
}


bool Boid::IsNear() const
{
	if(m_near == 0)
		return false;
	else
		return true;
}


void Boid::SetParent(BoidGroup* a)
{
	m_parent = a;
}


BoidGroup* Boid::GetParent() const
{
	return m_parent;
}


bool Boid::IsParent() const
{
	if(m_parent == 0)
		return false;
	else
		return true;
}



Point3D Boid::GetCenter() const
{
	return m_center;
}


void Boid::SetCenter(const Point3D& a)
{
	m_center = a;
}


Point3D Boid::GetMuki() const
{
	return m_muki;
}


void Boid::SetMuki(const Point3D& a)
{
	m_muki = a;
}


void Boid::SetTailNumber(const int a)
{
	m_tail_num = a;
	m_tail.resize(a);
}


int Boid::GetTailNumber() const
{
	return m_tail_num;
}


void Boid::SetSpeed(const double a)
{
	m_speed = a;
}


double Boid::GetSpeed() const
{
	return m_speed;
}



Point3D Boid::GetTailPoint(int a) const // 頭のほうから
{
	// イテレータは途中から始まるため、一周分だけ余裕を持たせる
	if(a >= m_tail_num){ a -= m_tail_num; }
	return *(m_tail_itr + a);
}


