/**
 * Add extra joints to the SIMM model to represent the joint location/orientation
 * in the parent body and in the child body.
 *
 * @param aJoint The Simbody joint.
 * @param rParentName The name of the parent body for the "real" joint.
 * @param rChildName The name of the child body for the "real" joint.
 * @return Whether or not a 'parent' joint was added.
 */
bool SimbodySimmModel::addExtraJoints(const OpenSim::Joint& aJoint, string& rParentName, string& rChildName)
{
   SimTK::Vec3 location;
   SimTK::Vec3 orientation;
	bool parentJointAdded = false;

   if (isParentJointNeeded(aJoint)) {
	   location = aJoint.getLocationInParent();
	   orientation = aJoint.getOrientationInParent();
      string bodyName = aJoint.getChildBodyName() + "_pjc";
      SimbodySimmBody* b = new SimbodySimmBody(NULL, bodyName);
      _simmBody.append(b);
      makeSimmJoint(aJoint.getName() + "_pjc", aJoint.getParentBodyName(), bodyName, location, orientation);
      rParentName = bodyName;
		parentJointAdded = true;
   } else {
      rParentName = aJoint.getParentBodyName();
		parentJointAdded = false;
   }

   if (isChildJointNeeded(aJoint)) {
	   location = aJoint.getLocationInChild();
	   orientation = aJoint.getOrientationInChild();
	   string bodyName = aJoint.getChildBodyName() + "_jcc";
      SimbodySimmBody* b = new SimbodySimmBody(NULL, bodyName);
      _simmBody.append(b);
      // This joint is specified in the reverse direction.
      makeSimmJoint(aJoint.getName() + "_jcc", aJoint.getChildBodyName(), bodyName, location, orientation);
      rChildName = bodyName;
   } else {
	   rChildName = aJoint.getChildBodyName();
   }

	return parentJointAdded;
}
/**
 * Check to see if the location (in parent) and orientation (in parent) of a
 * Simbody joint can be merged with the joint's "real" DOFs and still be
 * represented with a single SIMM joint.
 *
 * @param aJoint The Simbody joint to check.
 * @return Whether or not a separate SIMM joint is needed to represent the location
 *         and orientation in parent.
 */
bool SimbodySimmModel::isParentJointNeeded(const OpenSim::Joint& aJoint)
{
	// If the joint has no "real" DOFs (e.g., WeldJoints), then the parent joint is not needed.
	if (aJoint.getConcreteClassName()=="WeldJoint")
		return false;

	SimTK::Vec3 location;
	SimTK::Vec3 orientation;

	location = aJoint.getLocationInParent();
	orientation = aJoint.getOrientationInParent();

	bool translationsUsed[] = {false, false, false}, translationsDone = false;
	int numTranslations = 0, numRotations = 0;

	// First see which components are needed for the location and orientation.
	for (int i=0; i<3; i++) {
		if (NOT_EQUAL_WITHIN_ERROR(location[i], 0.0)) {
			translationsUsed[i] = true;
			numTranslations++;
		}
	}
	for (int i=0; i<3; i++) {
		if (NOT_EQUAL_WITHIN_ERROR(orientation[i], 0.0)) {
			numRotations++;
			if (numTranslations > 0)
				translationsDone = true;
		}
	}

	if (aJoint.getConcreteClassName()==("PinJoint")) {
		if (numRotations >= 3) // have already defined three rotations (can't add more)
			return true;
	} else if (aJoint.getConcreteClassName()==("SliderJoint")) {
		if (translationsUsed[0]) // slider joint translations are always along the X axis
			return true;
	} else if (aJoint.getConcreteClassName()==("EllipsoidJoint")) {
		return true; // NOTE: ellipsoid joints cannot be converted to SIMM joints
	} else if (aJoint.getConcreteClassName()==("FreeJoint")) {
		return true;
	} else if (aJoint.getConcreteClassName()==("CustomJoint")) {
		const CustomJoint* cj = (CustomJoint*)(&aJoint);
		const SpatialTransform& dofs = cj->getSpatialTransform();

		// Now see if the joint's "real" DOFs can be added.
		for (int i=0; i<6; i++) {
			const TransformAxis* ta = &dofs[i];
			if (i >= 3) {
				double axis[3];
				ta->getAxis(axis);
				for (int j=0; j<3; j++) {
					if (EQUAL_WITHIN_ERROR(axis[j], 1.0)) {
						if (ta->getCoordinateNames().size() > 0) { // transform axis is unused if it has no coordinate names
							if (translationsUsed[i-3]) // this translation component already defined
								return true;
							if (translationsDone) // have already defined translations then rotation (can't add more translations)
								return true;
							translationsUsed[i-3] = true;
							numTranslations++;
						}
						break;
					}
				}
			} else {
				if (ta->getCoordinateNames().size() > 0) { // transform axis is unused if it has no coordinate names
					if (numRotations >= 3) // have already defined three rotations (can't add more)
						return true;
					numRotations++;
					if (numTranslations > 0)
						translationsDone = true;
				}
			}
		}
	}

   return false;
}