/* --------------------------------------------------------
					thread_path_tracking

	Kalman-Filter tracking of the current robot state
   -------------------------------------------------------- */
void CPRRTNavigator::thread_path_tracking()
{
	// Set highest priority
	mrpt::system::changeCurrentProcessPriority(ppVeryHigh);
	mrpt::system::changeThreadPriority( mrpt::system::getCurrentThreadHandle(),tpHighest );

	cout << "[CPRRTNavigator:thread_path_tracking] Thread alive.\n";

	const double DESIRED_RATE = 15.0;
	const double DESIRED_PERIOD = 1.0/DESIRED_RATE;

	TTimeStamp  tim_last_iter = INVALID_TIMESTAMP;

	// Buffered data:
	TPathData	next_planned_point;  // target
	TTimeStamp	planned_path_time = INVALID_TIMESTAMP;

	while (!m_closingThreads)
	{
		if ( !m_initialized )  // Do nothing until we're loaded and ready.
		{
			mrpt::system::sleep(100);
			continue;
		}

		// Acquire the latest path plan --------------
		{
			CCriticalSectionLocker lock(& m_planned_path_cs );
			if (m_planned_path_time==INVALID_TIMESTAMP || m_planned_path.empty())
			{
				// There's no plan: Stop the robot:
				planned_path_time = INVALID_TIMESTAMP;
			}
			else  // Only update our copy if needed:
			{
				planned_path_time = m_planned_path_time;
				next_planned_point = m_planned_path.front();
			}
		} // end of CS

		// Path following ------------------------------
		if (planned_path_time == INVALID_TIMESTAMP)
		{
			// Stop the robot, now.
			onMotionCommand(0,0);
		}
		else
		{
			const bool ignore_trg_heading = INVALID_HEADING==next_planned_point.p.phi;

			// Acquire the current estimate of the robot state:
			// ----------------------------------------------------
			TPose2D  robotPose;
			float    robot_v,robot_w;
			if (!m_robotStateFilter.getCurrentEstimate(robotPose,robot_v,robot_w)) // Thread-safe call
			{
				// Error: Stop the robot, now.
				onMotionCommand(0,0);
			}
			else
			{	// we have proper localization

				// ==============================================================================
				// We want to approach to "next_planned_point"
				//   - Apply a sequence of tests to discover the shortests & easiest movement
				//      to that target.
				// ==============================================================================
				// Compute target in coordinates relative to the robot just now:
				const CPose2D trg_rel = CPose2D(next_planned_point.p) - CPose2D(robotPose);

//				if (next_planned_point.p.y==-1)	{
//					int a=0;
//				}

				// Current distances to target:
				const double trg_dist_lin = trg_rel.norm();
				const double trg_dist_ang = std::abs(trg_rel.phi());

				// Remaining Estimated Time for Arrival
				double ETA_target;
				if (std::abs(robot_v)>1e-4)
						ETA_target = std::max(0.05, (trg_dist_lin-params.pathtrack.radius_checkpoints)/std::abs(robot_v) );
				else if (std::abs(robot_w)>1e-4)
						ETA_target = trg_dist_ang/std::abs(robot_w);
				else	ETA_target = 1000.0;

				//cout << format("ETA: %f\n",ETA_target);
				//cout << format("d_lin=%f d_ang=%f\n",trg_dist_lin,RAD2DEG(trg_dist_ang));

				// If we have reached one point, remove it from the front of the list:
				if ((trg_dist_lin<params.pathtrack.radius_checkpoints || trg_dist_lin<std::abs(robot_v)*3*DESIRED_PERIOD )&&
					(ignore_trg_heading || trg_dist_ang<DEG2RAD(10) || trg_dist_ang<std::abs(robot_w)*3*DESIRED_PERIOD )
					)
				{
					CCriticalSectionLocker lock(& m_planned_path_cs );
					if (m_planned_path_time==planned_path_time)
						m_planned_path.pop_front();
				}
				else
				{
					// ------------------------------------------------------------------------------------
					//   CASE 1: Direct arc.
					//     (tx,ty,ta) all are compatible with a direct arc from our current pose
					// ------------------------------------------------------------------------------------
					bool   good_cmd_found = false;
					double good_cmd_v=0,good_cmd_w=0;

					{
						// predicted heading at (tx,ty):
						double  predict_phi;
						double cmd_v=0,cmd_w=0;

						if (trg_rel.y()==0)
						{	// In real-world conditions this might never happen, but just in case:
							predict_phi = 0;
							cmd_v = next_planned_point.max_v * sign(trg_rel.x());
							cmd_w = 0;
						}
						else
						{
							const double R = square(trg_dist_lin)/(2*trg_rel.y());
							// predicted heading at (tx,ty):
							predict_phi = atan2(trg_rel.x(),R-trg_rel.y());

							cmd_v = next_planned_point.max_v * sign(trg_rel.x());
							cmd_w = cmd_v/R;
						}

						// Good enough?
						if (ignore_trg_heading ||
							std::abs( wrapToPi(trg_rel.phi()-predict_phi))<DEG2RAD(7) )
						{
							good_cmd_found = true;
							good_cmd_v=cmd_v;
							good_cmd_w=cmd_w;
						}
					} // end case 1


					// ------------------------------------------
					// Scale velocities to the actual ranges:
					// ------------------------------------------
					if (good_cmd_found)
					{
						// SCALE: For maximum segment speeds:
						// ----------------------------------------
						if (std::abs(good_cmd_v)>next_planned_point.max_v)
						{
							const double K = next_planned_point.max_v / std::abs(good_cmd_v);
							good_cmd_v *= K;
							good_cmd_w *= K;
						}
						if (std::abs(good_cmd_w)>next_planned_point.max_w)
						{
							const double K = next_planned_point.max_w / std::abs(good_cmd_w);
							good_cmd_v *= K;
							good_cmd_w *= K;
						}

						// SCALE: Take into account the desired velocities when arriving at the target.
						// ------------------------------------------------------------------------------
						const double Av_to_trg = next_planned_point.trg_v - robot_v;
						const double min_At_to_change_to_trg_v = std::abs(Av_to_trg)/params.max_accel_v;

						if (good_cmd_v!=0 && ETA_target<=min_At_to_change_to_trg_v)
						{
							// We have to adapt velocity so we can get to the target right:
							const double new_v = next_planned_point.trg_v - (ETA_target/min_At_to_change_to_trg_v)*Av_to_trg;

							const double K = new_v / std::abs(good_cmd_v);
							good_cmd_v *= K;
							good_cmd_w *= K;
						}


						// SCALE: Is the command compatible with the maximum accelerations, wrt the current speed??
						// ----------------------------------------
						const double max_Av = params.max_accel_v * 0.2 /*sec*/;
						const double max_Aw = params.max_accel_w * 0.2 /*sec*/;
						if ( std::abs( good_cmd_v - robot_v )>max_Av )
						{
							if (good_cmd_v!=0)
							{
								const double rho = good_cmd_w/good_cmd_v;
								good_cmd_v = (good_cmd_v>robot_v) ? robot_v+max_Av :  robot_v-max_Av;
								good_cmd_w = rho*good_cmd_v;
							}
						}
						if ( std::abs( good_cmd_w - robot_w )>max_Aw )
						{
							if (good_cmd_w!=0)
							{
								const double R = good_cmd_v/good_cmd_w;
								good_cmd_w = (good_cmd_w>robot_w) ? robot_w+max_Aw :  robot_w-max_Aw;
								good_cmd_v = R*good_cmd_w;
							}
						}




						// Ok, send the command:
						// ----------------------------------------
						onMotionCommand(good_cmd_v,good_cmd_w);
					}
					else
					{
						// No valid motion found??!!
						// TODO: Raise some error.
						onMotionCommand(0,0);
					}

				} // end we haven't reached the target
			} // end we have proper localization
		} // end do path following


		// Run at XX Hz -------------------------------
		const TTimeStamp tim_now = now();
		int delay_ms;
		if (tim_last_iter != INVALID_TIMESTAMP)
		{
			const double tim_since_last = mrpt::system::timeDifference(tim_last_iter,tim_now);
			delay_ms = std::max(1, round( 1000.0*(DESIRED_PERIOD-tim_since_last) ) );
		}
		else delay_ms = 20;

		tim_last_iter = tim_now; // for the next iter.
		mrpt::system::sleep(delay_ms); // do the delay
	};
	cout << "[CPRRTNavigator:thread_path_tracking] Exit.\n";
}