/*
 * ***** BEGIN GPL LICENSE BLOCK *****
 *
 * 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 2
 * 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, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
 * All rights reserved.
 *
 * Contributor(s): Blender Foundation, 2002-2009 full recode.
 *
 * ***** END GPL LICENSE BLOCK *****
 */

/** \file blender/editors/armature/editarmature.c
 *  \ingroup edarmature
 */


#include <ctype.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <math.h> 
#include <float.h> 
#include <assert.h> 


#include "DNA_anim_types.h"
#include "DNA_mesh_types.h"
#include "DNA_armature_types.h"
#include "DNA_constraint_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"

#include "MEM_guardedalloc.h"

#include "BLI_blenlib.h"
#include "BLI_math.h"
#include "BLI_utildefines.h"
#include "BLI_ghash.h"

#include "BKE_animsys.h"
#include "BKE_action.h"
#include "BKE_armature.h"
#include "BKE_constraint.h"
#include "BKE_context.h"
#include "BKE_deform.h"
#include "BKE_depsgraph.h"
#include "BKE_DerivedMesh.h"
#include "BKE_global.h"
#include "BKE_idprop.h"
#include "BKE_main.h"
#include "BKE_object.h"
#include "BKE_report.h"
#include "BKE_subsurf.h"
#include "BKE_modifier.h"
#include "DNA_object_types.h"

#include "BIF_gl.h"

#include "RNA_access.h"
#include "RNA_define.h"

#include "WM_api.h"
#include "WM_types.h"

#include "ED_armature.h"
#include "ED_keyframing.h"
#include "ED_mesh.h"
#include "ED_object.h"
#include "ED_screen.h"
#include "ED_util.h"
#include "ED_view3d.h"

#include "UI_interface.h"
#include "UI_resources.h"

#include "armature_intern.h"
#include "meshlaplacian.h"

#if 0
#include "reeb.h"
#endif

/* **************** tools on Editmode Armature **************** */

/* Sync selection to parent for connected children */
void ED_armature_sync_selection(ListBase *edbo)
{
	EditBone *ebo;
	
	for (ebo = edbo->first; ebo; ebo = ebo->next) {
		/* if bone is not selectable, we shouldn't alter this setting... */
		if ((ebo->flag & BONE_UNSELECTABLE) == 0) {
			if ((ebo->flag & BONE_CONNECTED) && (ebo->parent)) {
				if (ebo->parent->flag & BONE_TIPSEL)
					ebo->flag |= BONE_ROOTSEL;
				else
					ebo->flag &= ~BONE_ROOTSEL;
			}
			
			if ((ebo->flag & BONE_TIPSEL) && (ebo->flag & BONE_ROOTSEL))
				ebo->flag |= BONE_SELECTED;
			else
				ebo->flag &= ~BONE_SELECTED;
		}
	}
}

void ED_armature_validate_active(struct bArmature *arm)
{
	EditBone *ebone = arm->act_edbone;

	if (ebone) {
		if (ebone->flag & BONE_HIDDEN_A)
			arm->act_edbone = NULL;
	}
}

static void bone_free(bArmature *arm, EditBone *bone)
{
	if (arm->act_edbone == bone)
		arm->act_edbone = NULL;

	if (bone->prop) {
		IDP_FreeProperty(bone->prop);
		MEM_freeN(bone->prop);
	}

	BLI_freelinkN(arm->edbo, bone);
}

void ED_armature_edit_bone_remove(bArmature *arm, EditBone *exBone)
{
	EditBone *curBone;

	/* Find any bones that refer to this bone */
	for (curBone = arm->edbo->first; curBone; curBone = curBone->next) {
		if (curBone->parent == exBone) {
			curBone->parent = exBone->parent;
			curBone->flag &= ~BONE_CONNECTED;
		}
	}

	bone_free(arm, exBone);
}

/* context: editmode armature */
EditBone *ED_armature_bone_get_mirrored(ListBase *edbo, EditBone *ebo)
{
	EditBone *eboflip = NULL;
	char name[MAXBONENAME];
	
	if (ebo == NULL)
		return NULL;
	
	flip_side_name(name, ebo->name, FALSE);
	
	for (eboflip = edbo->first; eboflip; eboflip = eboflip->next) {
		if (ebo != eboflip) {
			if (!strcmp(name, eboflip->name))
				break;
		}
	}
	
	return eboflip;
}

/* helper function for tools to work on mirrored parts.
 * it leaves mirrored bones selected then too, which is a good indication of what happened */
static void armature_select_mirrored(bArmature *arm)
{
	/* Select mirrored bones */
	if (arm->flag & ARM_MIRROR_EDIT) {
		EditBone *curBone, *ebone_mirr;
		
		for (curBone = arm->edbo->first; curBone; curBone = curBone->next) {
			if (arm->layer & curBone->layer) {
				if (curBone->flag & BONE_SELECTED) {
					ebone_mirr = ED_armature_bone_get_mirrored(arm->edbo, curBone);
					if (ebone_mirr)
						ebone_mirr->flag |= BONE_SELECTED;
				}
			}
		}
	}
	
}

static void armature_tag_select_mirrored(bArmature *arm)
{
	EditBone *curBone;

	/* always untag */
	for (curBone = arm->edbo->first; curBone; curBone = curBone->next) {
		curBone->flag &= ~BONE_DONE;
	}

	/* Select mirrored bones */
	if (arm->flag & ARM_MIRROR_EDIT) {
		for (curBone = arm->edbo->first; curBone; curBone = curBone->next) {
			if (arm->layer & curBone->layer) {
				if (curBone->flag & (BONE_SELECTED | BONE_ROOTSEL | BONE_TIPSEL)) {
					EditBone *ebone_mirr = ED_armature_bone_get_mirrored(arm->edbo, curBone);
					if (ebone_mirr && (ebone_mirr->flag & BONE_SELECTED) == 0) {
						ebone_mirr->flag |= BONE_DONE;
					}
				}
			}
		}

		for (curBone = arm->edbo->first; curBone; curBone = curBone->next) {
			if (curBone->flag & BONE_DONE) {
				EditBone *ebone_mirr = ED_armature_bone_get_mirrored(arm->edbo, curBone);
				curBone->flag |= ebone_mirr->flag & (BONE_SELECTED | BONE_ROOTSEL | BONE_TIPSEL);
			}
		}
	}
}


/* only works when tagged */
static void armature_tag_unselect(bArmature *arm)
{
	EditBone *curBone;

	for (curBone = arm->edbo->first; curBone; curBone = curBone->next) {
		if (curBone->flag & BONE_DONE) {
			curBone->flag &= ~(BONE_SELECTED | BONE_ROOTSEL | BONE_TIPSEL | BONE_DONE);
		}
	}
}

/* converts Bones to EditBone list, used for tools as well */
EditBone *make_boneList(ListBase *edbo, ListBase *bones, EditBone *parent, Bone *actBone)
{
	EditBone    *eBone;
	EditBone    *eBoneAct = NULL;
	EditBone    *eBoneTest = NULL;
	Bone        *curBone;
		
	for (curBone = bones->first; curBone; curBone = curBone->next) {
		eBone = MEM_callocN(sizeof(EditBone), "make_editbone");
		
		/*	Copy relevant data from bone to eBone */
		eBone->parent = parent;
		BLI_strncpy(eBone->name, curBone->name, sizeof(eBone->name));
		eBone->flag = curBone->flag;
		
		/* fix selection flags */

		if (eBone->flag & BONE_SELECTED) {
			/* if the bone is selected the copy its root selection to the parents tip */
			eBone->flag |= BONE_TIPSEL;
			if (eBone->parent && (eBone->flag & BONE_CONNECTED)) {
				eBone->parent->flag |= BONE_TIPSEL;
				eBone->flag &= ~BONE_ROOTSEL; /* this is ignored when there is a connected parent, so unset it */
			}
			else {
				eBone->flag |= BONE_ROOTSEL;
			}
		}
		else {
			/* if the bone is not selected, but connected to its parent
			 *  copy the parents tip selection state */
			if (eBone->parent && (eBone->flag & BONE_CONNECTED)) {
				/* selecting with the mouse gives this behavior */
				if (eBone->parent->flag & BONE_TIPSEL) {
					eBone->flag |= BONE_ROOTSEL;
				}
				else {
					eBone->flag &= ~BONE_ROOTSEL;
				}

				/* probably not selected but just in case */
				eBone->flag &= ~BONE_TIPSEL;
			}
		}

		copy_v3_v3(eBone->head, curBone->arm_head);
		copy_v3_v3(eBone->tail, curBone->arm_tail);
		eBone->roll = curBone->arm_roll;
		
		/* rest of stuff copy */
		eBone->length = curBone->length;
		eBone->dist = curBone->dist;
		eBone->weight = curBone->weight;
		eBone->xwidth = curBone->xwidth;
		eBone->zwidth = curBone->zwidth;
		eBone->ease1 = curBone->ease1;
		eBone->ease2 = curBone->ease2;
		eBone->rad_head = curBone->rad_head;
		eBone->rad_tail = curBone->rad_tail;
		eBone->segments = curBone->segments;
		eBone->layer = curBone->layer;

		if (curBone->prop)
			eBone->prop = IDP_CopyProperty(curBone->prop);
		
		BLI_addtail(edbo, eBone);
		
		/*	Add children if necessary */
		if (curBone->childbase.first) {
			eBoneTest = make_boneList(edbo, &curBone->childbase, eBone, actBone);
			if (eBoneTest)
				eBoneAct = eBoneTest;
		}

		if (curBone == actBone)
			eBoneAct = eBone;
	}

	return eBoneAct;
}

/* nasty stuff for converting roll in editbones into bones */
/* also sets restposition in armature (arm_mat) */
static void fix_bonelist_roll(ListBase *bonelist, ListBase *editbonelist)
{
	Bone *curBone;
	EditBone *ebone;
	float premat[3][3];
	float postmat[3][3];
	float difmat[3][3];
	float imat[3][3];
	float delta[3];
	
	for (curBone = bonelist->first; curBone; curBone = curBone->next) {
		/* sets local matrix and arm_mat (restpos) */
		BKE_armature_where_is_bone(curBone, curBone->parent);
		
		/* Find the associated editbone */
		for (ebone = editbonelist->first; ebone; ebone = ebone->next)
			if ((Bone *)ebone->temp == curBone)
				break;
		
		if (ebone) {
			/* Get the ebone premat */
			sub_v3_v3v3(delta, ebone->tail, ebone->head);
			vec_roll_to_mat3(delta, ebone->roll, premat);
			
			/* Get the bone postmat */
			copy_m3_m4(postmat, curBone->arm_mat);
			
			invert_m3_m3(imat, premat);
			mul_m3_m3m3(difmat, imat, postmat);
#if 0
			printf("Bone %s\n", curBone->name);
			print_m4("premat", premat);
			print_m4("postmat", postmat);
			print_m4("difmat", difmat);
			printf("Roll = %f\n",  RAD2DEGF(-atan2(difmat[2][0], difmat[2][2])));
#endif
			curBone->roll = (float)-atan2(difmat[2][0], difmat[2][2]);
			
			/* and set restposition again */
			BKE_armature_where_is_bone(curBone, curBone->parent);
		}
		fix_bonelist_roll(&curBone->childbase, editbonelist);
	}
}

/* put EditMode back in Object */
void ED_armature_from_edit(Object *obedit)
{
	bArmature *arm = obedit->data;
	EditBone *eBone, *neBone;
	Bone *newBone;
	Object *obt;
	
	/* armature bones */
	BKE_armature_bonelist_free(&arm->bonebase);
	
	/* remove zero sized bones, this gives instable restposes */
	for (eBone = arm->edbo->first; eBone; eBone = neBone) {
		float len = len_v3v3(eBone->head, eBone->tail);
		neBone = eBone->next;
		if (len <= 0.000001f) {  /* FLT_EPSILON is too large? */
			EditBone *fBone;
			
			/*	Find any bones that refer to this bone	*/
			for (fBone = arm->edbo->first; fBone; fBone = fBone->next) {
				if (fBone->parent == eBone)
					fBone->parent = eBone->parent;
			}
			if (G.debug & G_DEBUG)
				printf("Warning: removed zero sized bone: %s\n", eBone->name);
			bone_free(arm, eBone);
		}
	}
	
	/*	Copy the bones from the editData into the armature */
	for (eBone = arm->edbo->first; eBone; eBone = eBone->next) {
		newBone = MEM_callocN(sizeof(Bone), "bone");
		eBone->temp = newBone;   /* Associate the real Bones with the EditBones */
		
		BLI_strncpy(newBone->name, eBone->name, sizeof(newBone->name));
		copy_v3_v3(newBone->arm_head, eBone->head);
		copy_v3_v3(newBone->arm_tail, eBone->tail);
		newBone->arm_roll = eBone->roll;
		
		newBone->flag = eBone->flag;
		
		if (eBone == arm->act_edbone) {
			/* don't change active selection, this messes up separate which uses
			 * editmode toggle and can separate active bone which is de-selected originally */
			/* newBone->flag |= BONE_SELECTED; */ /* important, editbones can be active with only 1 point selected */
			arm->act_edbone = NULL;
			arm->act_bone = newBone;
		}
		newBone->roll = 0.0f;
		
		newBone->weight = eBone->weight;
		newBone->dist = eBone->dist;
		
		newBone->xwidth = eBone->xwidth;
		newBone->zwidth = eBone->zwidth;
		newBone->ease1 = eBone->ease1;
		newBone->ease2 = eBone->ease2;
		newBone->rad_head = eBone->rad_head;
		newBone->rad_tail = eBone->rad_tail;
		newBone->segments = eBone->segments;
		newBone->layer = eBone->layer;
		
		if (eBone->prop)
			newBone->prop = IDP_CopyProperty(eBone->prop);
	}
	
	/* Fix parenting in a separate pass to ensure ebone->bone connections
	 * are valid at this point */
	for (eBone = arm->edbo->first; eBone; eBone = eBone->next) {
		newBone = (Bone *)eBone->temp;
		if (eBone->parent) {
			newBone->parent = (Bone *)eBone->parent->temp;
			BLI_addtail(&newBone->parent->childbase, newBone);
			
			{
				float M_parentRest[3][3];
				float iM_parentRest[3][3];
				float delta[3];
				
				/* Get the parent's  matrix (rotation only) */
				sub_v3_v3v3(delta, eBone->parent->tail, eBone->parent->head);
				vec_roll_to_mat3(delta, eBone->parent->roll, M_parentRest);
				
				/* Invert the parent matrix */
				invert_m3_m3(iM_parentRest, M_parentRest);
				
				/* Get the new head and tail */
				sub_v3_v3v3(newBone->head, eBone->head, eBone->parent->tail);
				sub_v3_v3v3(newBone->tail, eBone->tail, eBone->parent->tail);
				
				mul_m3_v3(iM_parentRest, newBone->head);
				mul_m3_v3(iM_parentRest, newBone->tail);
			}
		}
		/*	...otherwise add this bone to the armature's bonebase */
		else {
			copy_v3_v3(newBone->head, eBone->head);
			copy_v3_v3(newBone->tail, eBone->tail);
			BLI_addtail(&arm->bonebase, newBone);
		}
	}
	
	/* Make a pass through the new armature to fix rolling */
	/* also builds restposition again (like BKE_armature_where_is) */
	fix_bonelist_roll(&arm->bonebase, arm->edbo);
	
	/* so all users of this armature should get rebuilt */
	for (obt = G.main->object.first; obt; obt = obt->id.next) {
		if (obt->data == arm)
			BKE_pose_rebuild(obt, arm);
	}
	
	DAG_id_tag_update(&obedit->id, OB_RECALC_DATA);
}

void ED_armature_apply_transform(Object *ob, float mat[4][4])
{
	EditBone *ebone;
	bArmature *arm = ob->data;
	float scale = mat4_to_scale(mat);   /* store the scale of the matrix here to use on envelopes */
	float mat3[3][3];

	copy_m3_m4(mat3, mat);
	normalize_m3(mat3);

	/* Put the armature into editmode */
	ED_armature_to_edit(ob);

	/* Do the rotations */
	for (ebone = arm->edbo->first; ebone; ebone = ebone->next) {
		float delta[3], tmat[3][3];

		/* find the current bone's roll matrix */
		sub_v3_v3v3(delta, ebone->tail, ebone->head);
		vec_roll_to_mat3(delta, ebone->roll, tmat);

		/* transform the roll matrix */
		mul_m3_m3m3(tmat, mat3, tmat);

		/* transform the bone */
		mul_m4_v3(mat, ebone->head);
		mul_m4_v3(mat, ebone->tail);

		/* apply the transfiormed roll back */
		mat3_to_vec_roll(tmat, NULL, &ebone->roll);

		ebone->rad_head *= scale;
		ebone->rad_tail *= scale;
		ebone->dist     *= scale;

		/* we could be smarter and scale by the matrix along the x & z axis */
		ebone->xwidth   *= scale;
		ebone->zwidth   *= scale;
	}
	
	/* Turn the list into an armature */
	ED_armature_from_edit(ob);
	ED_armature_edit_free(ob);
}

/* exported for use in editors/object/ */
/* 0 == do center, 1 == center new, 2 == center cursor */
void docenter_armature(Scene *scene, Object *ob, float cursor[3], int centermode, int around)
{
	Object *obedit = scene->obedit; // XXX get from context
	EditBone *ebone;
	bArmature *arm = ob->data;
	float cent[3];

	/* Put the armature into editmode */
	if (ob != obedit) {
		ED_armature_to_edit(ob);
		obedit = NULL; /* we cant use this so behave as if there is no obedit */
	}

	/* Find the centerpoint */
	if (centermode == 2) {
		copy_v3_v3(cent, cursor);
		invert_m4_m4(ob->imat, ob->obmat);
		mul_m4_v3(ob->imat, cent);
	}
	else {
		if (around == V3D_CENTROID) {
			int total = 0;
			zero_v3(cent);
			for (ebone = arm->edbo->first; ebone; ebone = ebone->next) {
				total += 2;
				add_v3_v3(cent, ebone->head);
				add_v3_v3(cent, ebone->tail);
			}
			if (total) {
				mul_v3_fl(cent, 1.0f / (float)total);
			}
		}
		else {
			float min[3], max[3];
			INIT_MINMAX(min, max);
			for (ebone = arm->edbo->first; ebone; ebone = ebone->next) {
				minmax_v3v3_v3(min, max, ebone->head);
				minmax_v3v3_v3(min, max, ebone->tail);
			}
			mid_v3_v3v3(cent, min, max);
		}
	}
	
	/* Do the adjustments */
	for (ebone = arm->edbo->first; ebone; ebone = ebone->next) {
		sub_v3_v3(ebone->head, cent);
		sub_v3_v3(ebone->tail, cent);
	}
	
	/* Turn the list into an armature */
	if (obedit == NULL) {
		ED_armature_from_edit(ob);
		ED_armature_edit_free(ob);
	}

	/* Adjust object location for new centerpoint */
	if (centermode && obedit == NULL) {
		mul_mat3_m4_v3(ob->obmat, cent); /* ommit translation part */
		add_v3_v3(ob->loc, cent);
	}
}

/* ---------------------- */

/* checks if an EditBone with a matching name already, returning the matching bone if it exists */
static EditBone *editbone_name_exists(ListBase *edbo, const char *name)
{
	return BLI_findstring(edbo, name, offsetof(EditBone, name));
}

/* note: there's a unique_bone_name() too! */
static int editbone_unique_check(void *arg, const char *name)
{
	struct {ListBase *lb; void *bone; } *data = arg;
	EditBone *dupli = editbone_name_exists(data->lb, name);
	return dupli && dupli != data->bone;
}

void unique_editbone_name(ListBase *edbo, char *name, EditBone *bone)
{
	struct {ListBase *lb; void *bone; } data;
	data.lb = edbo;
	data.bone = bone;

	BLI_uniquename_cb(editbone_unique_check, &data, "Bone", '.', name, sizeof(bone->name));
}

/* helper for apply_armature_pose2bones - fixes parenting of objects that are bone-parented to armature */
static void applyarmature_fix_boneparents(Scene *scene, Object *armob)
{
	Object workob, *ob;
	
	/* go through all objects in database */
	for (ob = G.main->object.first; ob; ob = ob->id.next) {
		/* if parent is bone in this armature, apply corrections */
		if ((ob->parent == armob) && (ob->partype == PARBONE)) {
			/* apply current transform from parent (not yet destroyed), 
			 * then calculate new parent inverse matrix
			 */
			BKE_object_apply_mat4(ob, ob->obmat, FALSE, FALSE);
			
			BKE_object_workob_calc_parent(scene, ob, &workob);
			invert_m4_m4(ob->parentinv, workob.obmat);
		}
	}
}

/* set the current pose as the restpose */
static int apply_armature_pose2bones_exec(bContext *C, wmOperator *op)
{
	Scene *scene = CTX_data_scene(C);
	Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C)); // must be active object, not edit-object
	bArmature *arm = BKE_armature_from_object(ob);
	bPose *pose;
	bPoseChannel *pchan;
	EditBone *curbone;
	
	/* don't check if editmode (should be done by caller) */
	if (ob->type != OB_ARMATURE)
		return OPERATOR_CANCELLED;
	if (BKE_object_obdata_is_libdata(ob)) {
		BKE_report(op->reports, RPT_ERROR, "Cannot apply pose to lib-linked armature"); /* error_libdata(); */
		return OPERATOR_CANCELLED;
	}

	/* helpful warnings... */
	/* TODO: add warnings to be careful about actions, applying deforms first, etc. */
	if (ob->adt && ob->adt->action)
		BKE_report(op->reports, RPT_WARNING,
		           "Actions on this armature will be destroyed by this new rest pose as the "
		           "transforms stored are relative to the old rest pose");

	/* Get editbones of active armature to alter */
	ED_armature_to_edit(ob);
	
	/* get pose of active object and move it out of posemode */
	pose = ob->pose;
	
	for (pchan = pose->chanbase.first; pchan; pchan = pchan->next) {
		curbone = editbone_name_exists(arm->edbo, pchan->name);
		
		/* simply copy the head/tail values from pchan over to curbone */
		copy_v3_v3(curbone->head, pchan->pose_head);
		copy_v3_v3(curbone->tail, pchan->pose_tail);
		
		/* fix roll:
		 *	1. find auto-calculated roll value for this bone now
		 *	2. remove this from the 'visual' y-rotation
		 */
		{
			float premat[3][3], imat[3][3], pmat[3][3], tmat[3][3];
			float delta[3], eul[3];
			
			/* obtain new auto y-rotation */
			sub_v3_v3v3(delta, curbone->tail, curbone->head);
			vec_roll_to_mat3(delta, 0.0f, premat);
			invert_m3_m3(imat, premat);
			
			/* get pchan 'visual' matrix */
			copy_m3_m4(pmat, pchan->pose_mat);
			
			/* remove auto from visual and get euler rotation */
			mul_m3_m3m3(tmat, imat, pmat);
			mat3_to_eul(eul, tmat);
			
			/* just use this euler-y as new roll value */
			curbone->roll = eul[1];
		}
		
		/* clear transform values for pchan */
		zero_v3(pchan->loc);
		zero_v3(pchan->eul);
		unit_qt(pchan->quat);
		unit_axis_angle(pchan->rotAxis, &pchan->rotAngle);
		pchan->size[0] = pchan->size[1] = pchan->size[2] = 1.0f;
		
		/* set anim lock */
		curbone->flag |= BONE_UNKEYED;
	}
	
	/* convert editbones back to bones, and then free the edit-data */
	ED_armature_from_edit(ob);
	ED_armature_edit_free(ob);
	
	/* flush positions of posebones */
	BKE_pose_where_is(scene, ob);
	
	/* fix parenting of objects which are bone-parented */
	applyarmature_fix_boneparents(scene, ob);
	
	/* note, notifier might evolve */
	WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob);
	
	return OPERATOR_FINISHED;
}

void POSE_OT_armature_apply(wmOperatorType *ot)
{
	/* identifiers */
	ot->name = "Apply Pose as Rest Pose";
	ot->idname = "POSE_OT_armature_apply";
	ot->description = "Apply the current pose as the new rest pose";
	
	/* callbacks */
	ot->exec = apply_armature_pose2bones_exec;
	ot->poll = ED_operator_posemode;
	
	/* flags */
	ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}


/* set the current pose as the restpose */
static int pose_visual_transform_apply_exec(bContext *C, wmOperator *UNUSED(op))
{
	Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C)); // must be active object, not edit-object

	/* don't check if editmode (should be done by caller) */
	if (ob->type != OB_ARMATURE)
		return OPERATOR_CANCELLED;

	/* loop over all selected pchans
	 *
	 * TODO, loop over children before parents if multiple bones
	 * at once are to be predictable*/
	CTX_DATA_BEGIN(C, bPoseChannel *, pchan, selected_pose_bones)
	{
		float delta_mat[4][4];
		
		/* chan_mat already contains the delta transform from rest pose to pose-mode pose
		 * as that is baked into there so that B-Bones will work. Once we've set this as the
		 * new raw-transform components, don't recalc the poses yet, otherwise IK result will 
		 * change, thus changing the result we may be trying to record.
		 */
		copy_m4_m4(delta_mat, pchan->chan_mat);
		BKE_pchan_apply_mat4(pchan, delta_mat, TRUE);
	}
	CTX_DATA_END;
	
	DAG_id_tag_update(&ob->id, OB_RECALC_DATA);

	/* note, notifier might evolve */
	WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob);

	return OPERATOR_FINISHED;
}

void POSE_OT_visual_transform_apply(wmOperatorType *ot)
{
	/* identifiers */
	ot->name = "Apply Visual Transform to Pose";
	ot->idname = "POSE_OT_visual_transform_apply";
	ot->description = "Apply final constrained position of pose bones to their transform";
	
	/* callbacks */
	ot->exec = pose_visual_transform_apply_exec;
	ot->poll = ED_operator_posemode;
	
	/* flags */
	ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}

/* ---------------------- */

/* Helper function for armature joining - link fixing */
static void joined_armature_fix_links(Object *tarArm, Object *srcArm, bPoseChannel *pchan, EditBone *curbone)
{
	Object *ob;
	bPose *pose;
	bPoseChannel *pchant;
	bConstraint *con;
	
	/* let's go through all objects in database */
	for (ob = G.main->object.first; ob; ob = ob->id.next) {
		/* do some object-type specific things */
		if (ob->type == OB_ARMATURE) {
			pose = ob->pose;
			for (pchant = pose->chanbase.first; pchant; pchant = pchant->next) {
				for (con = pchant->constraints.first; con; con = con->next) {
					bConstraintTypeInfo *cti = BKE_constraint_get_typeinfo(con);
					ListBase targets = {NULL, NULL};
					bConstraintTarget *ct;
					
					/* constraint targets */
					if (cti && cti->get_constraint_targets) {
						cti->get_constraint_targets(con, &targets);
						
						for (ct = targets.first; ct; ct = ct->next) {
							if (ct->tar == srcArm) {
								if (ct->subtarget[0] == '\0') {
									ct->tar = tarArm;
								}
								else if (strcmp(ct->subtarget, pchan->name) == 0) {
									ct->tar = tarArm;
									BLI_strncpy(ct->subtarget, curbone->name, sizeof(ct->subtarget));
								}
							}
						}
						
						if (cti->flush_constraint_targets)
							cti->flush_constraint_targets(con, &targets, 0);
					}
					
					/* action constraint? */
					if (con->type == CONSTRAINT_TYPE_ACTION) {
						bActionConstraint *data = con->data; // XXX old animation system
						bAction *act;
						bActionChannel *achan;
						
						if (data->act) {
							act = data->act;
							
							for (achan = act->chanbase.first; achan; achan = achan->next) {
								if (strcmp(achan->name, pchan->name) == 0)
									BLI_strncpy(achan->name, curbone->name, sizeof(achan->name));
							}
						}
					}
					
				}
			}
		}
			
		/* fix object-level constraints */
		if (ob != srcArm) {
			for (con = ob->constraints.first; con; con = con->next) {
				bConstraintTypeInfo *cti = BKE_constraint_get_typeinfo(con);
				ListBase targets = {NULL, NULL};
				bConstraintTarget *ct;
				
				/* constraint targets */
				if (cti && cti->get_constraint_targets) {
					cti->get_constraint_targets(con, &targets);
					
					for (ct = targets.first; ct; ct = ct->next) {
						if (ct->tar == srcArm) {
							if (ct->subtarget[0] == '\0') {
								ct->tar = tarArm;
							}
							else if (strcmp(ct->subtarget, pchan->name) == 0) {
								ct->tar = tarArm;
								BLI_strncpy(ct->subtarget, curbone->name, sizeof(ct->subtarget));
							}
						}
					}
					
					if (cti->flush_constraint_targets)
						cti->flush_constraint_targets(con, &targets, 0);
				}
			}
		}
		
		/* See if an object is parented to this armature */
		if (ob->parent && (ob->parent == srcArm)) {
			/* Is object parented to a bone of this src armature? */
			if (ob->partype == PARBONE) {
				/* bone name in object */
				if (!strcmp(ob->parsubstr, pchan->name))
					BLI_strncpy(ob->parsubstr, curbone->name, sizeof(ob->parsubstr));
			}
			
			/* make tar armature be new parent */
			ob->parent = tarArm;
		}
	}
}

/* join armature exec is exported for use in object->join objects operator... */
int join_armature_exec(bContext *C, wmOperator *UNUSED(op))
{
	Main *bmain = CTX_data_main(C);
	Scene *scene = CTX_data_scene(C);
	Object  *ob = CTX_data_active_object(C);
	bArmature *arm = (ob) ? ob->data : NULL;
	bPose *pose, *opose;
	bPoseChannel *pchan, *pchann;
	EditBone *curbone;
	float mat[4][4], oimat[4][4];
	
	/*	Ensure we're not in editmode and that the active object is an armature*/
	if (!ob || ob->type != OB_ARMATURE)
		return OPERATOR_CANCELLED;
	if (!arm || arm->edbo)
		return OPERATOR_CANCELLED;
	
	/* Get editbones of active armature to add editbones to */
	ED_armature_to_edit(ob);
	
	/* get pose of active object and move it out of posemode */
	pose = ob->pose;
	ob->mode &= ~OB_MODE_POSE;

	CTX_DATA_BEGIN(C, Base *, base, selected_editable_bases)
	{
		if ((base->object->type == OB_ARMATURE) && (base->object != ob)) {
			bArmature *curarm = base->object->data;
			
			/* Make a list of editbones in current armature */
			ED_armature_to_edit(base->object);
			
			/* Get Pose of current armature */
			opose = base->object->pose;
			base->object->mode &= ~OB_MODE_POSE;
			//BASACT->flag &= ~OB_MODE_POSE;
			
			/* Find the difference matrix */
			invert_m4_m4(oimat, ob->obmat);
			mult_m4_m4m4(mat, oimat, base->object->obmat);
			
			/* Copy bones and posechannels from the object to the edit armature */
			for (pchan = opose->chanbase.first; pchan; pchan = pchann) {
				pchann = pchan->next;
				curbone = editbone_name_exists(curarm->edbo, pchan->name);
				
				/* Get new name */
				unique_editbone_name(arm->edbo, curbone->name, NULL);
				
				/* Transform the bone */
				{
					float premat[4][4];
					float postmat[4][4];
					float difmat[4][4];
					float imat[4][4];
					float temp[3][3];
					float delta[3];
					
					/* Get the premat */
					sub_v3_v3v3(delta, curbone->tail, curbone->head);
					vec_roll_to_mat3(delta, curbone->roll, temp);
					
					unit_m4(premat); /* Mat4MulMat34 only sets 3x3 part */
					mul_m4_m3m4(premat, temp, mat);
					
					mul_m4_v3(mat, curbone->head);
					mul_m4_v3(mat, curbone->tail);
					
					/* Get the postmat */
					sub_v3_v3v3(delta, curbone->tail, curbone->head);
					vec_roll_to_mat3(delta, curbone->roll, temp);
					copy_m4_m3(postmat, temp);
					
					/* Find the roll */
					invert_m4_m4(imat, premat);
					mult_m4_m4m4(difmat, imat, postmat);
					
					curbone->roll -= (float)atan2(difmat[2][0], difmat[2][2]);
				}
				
				/* Fix Constraints and Other Links to this Bone and Armature */
				joined_armature_fix_links(ob, base->object, pchan, curbone);
				
				/* Rename pchan */
				BLI_strncpy(pchan->name, curbone->name, sizeof(pchan->name));
				
				/* Jump Ship! */
				BLI_remlink(curarm->edbo, curbone);
				BLI_addtail(arm->edbo, curbone);
				
				BLI_remlink(&opose->chanbase, pchan);
				BLI_addtail(&pose->chanbase, pchan);
				BKE_pose_channels_hash_free(opose);
				BKE_pose_channels_hash_free(pose);
			}
			
			ED_base_object_free_and_unlink(bmain, scene, base);
		}
	}
	CTX_DATA_END;
	
	DAG_scene_sort(bmain, scene);  /* because we removed object(s) */

	ED_armature_from_edit(ob);
	ED_armature_edit_free(ob);

	WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, scene);
	
	return OPERATOR_FINISHED;
}

/* ---------------------- */

/* Helper function for armature separating - link fixing */
static void separated_armature_fix_links(Object *origArm, Object *newArm)
{
	Object *ob;
	bPoseChannel *pchan;
	bConstraint *con;
	ListBase *opchans, *npchans;
	
	/* get reference to list of bones in original and new armatures  */
	opchans = &origArm->pose->chanbase;
	npchans = &newArm->pose->chanbase;
	
	/* let's go through all objects in database */
	for (ob = G.main->object.first; ob; ob = ob->id.next) {
		/* do some object-type specific things */
		if (ob->type == OB_ARMATURE) {
			for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) {
				for (con = pchan->constraints.first; con; con = con->next) {
					bConstraintTypeInfo *cti = BKE_constraint_get_typeinfo(con);
					ListBase targets = {NULL, NULL};
					bConstraintTarget *ct;
					
					/* constraint targets */
					if (cti && cti->get_constraint_targets) {
						cti->get_constraint_targets(con, &targets);
						
						for (ct = targets.first; ct; ct = ct->next) {
							/* any targets which point to original armature are redirected to the new one only if:
							 *	- the target isn't origArm/newArm itself
							 *	- the target is one that can be found in newArm/origArm
							 */
							if (ct->subtarget[0] != 0) {
								if (ct->tar == origArm) {
									if (BLI_findstring(npchans, ct->subtarget, offsetof(bPoseChannel, name))) {
										ct->tar = newArm;
									}
								}
								else if (ct->tar == newArm) {
									if (BLI_findstring(opchans, ct->subtarget, offsetof(bPoseChannel, name))) {
										ct->tar = origArm;
									}
								}
							}
						}

						if (cti->flush_constraint_targets) {
							cti->flush_constraint_targets(con, &targets, 0);
						}
					}
				}
			}
		}
			
		/* fix object-level constraints */
		if (ob != origArm) {
			for (con = ob->constraints.first; con; con = con->next) {
				bConstraintTypeInfo *cti = BKE_constraint_get_typeinfo(con);
				ListBase targets = {NULL, NULL};
				bConstraintTarget *ct;
				
				/* constraint targets */
				if (cti && cti->get_constraint_targets) {
					cti->get_constraint_targets(con, &targets);
					
					for (ct = targets.first; ct; ct = ct->next) {
						/* any targets which point to original armature are redirected to the new one only if:
						 *	- the target isn't origArm/newArm itself
						 *	- the target is one that can be found in newArm/origArm
						 */
						if (ct->subtarget[0] != '\0') {
							if (ct->tar == origArm) {
								if (BLI_findstring(npchans, ct->subtarget, offsetof(bPoseChannel, name))) {
									ct->tar = newArm;
								}
							}
							else if (ct->tar == newArm) {
								if (BLI_findstring(opchans, ct->subtarget, offsetof(bPoseChannel, name))) {
									ct->tar = origArm;
								}
							}
						}
					}

					if (cti->flush_constraint_targets) {
						cti->flush_constraint_targets(con, &targets, 0);
					}
				}
			}
		}
		
		/* See if an object is parented to this armature */
		if (ob->parent && (ob->parent == origArm)) {
			/* Is object parented to a bone of this src armature? */
			if ((ob->partype == PARBONE) && (ob->parsubstr[0] != '\0')) {
				if (BLI_findstring(npchans, ob->parsubstr, offsetof(bPoseChannel, name))) {
					ob->parent = newArm;
				}
			}
		}
	}
}

/* Helper function for armature separating - remove certain bones from the given armature 
 *	sel: remove selected bones from the armature, otherwise the unselected bones are removed
 *  (ob is not in editmode)
 */
static void separate_armature_bones(Object *ob, short sel) 
{
	bArmature *arm = (bArmature *)ob->data;
	bPoseChannel *pchan, *pchann;
	EditBone *curbone;
	
	/* make local set of editbones to manipulate here */
	ED_armature_to_edit(ob);
	
	/* go through pose-channels, checking if a bone should be removed */
	for (pchan = ob->pose->chanbase.first; pchan; pchan = pchann) {
		pchann = pchan->next;
		curbone = editbone_name_exists(arm->edbo, pchan->name);
		
		/* check if bone needs to be removed */
		if ( (sel && (curbone->flag & BONE_SELECTED)) ||
		     (!sel && !(curbone->flag & BONE_SELECTED)) )
		{
			EditBone *ebo;
			bPoseChannel *pchn;
			
			/* clear the bone->parent var of any bone that had this as its parent  */
			for (ebo = arm->edbo->first; ebo; ebo = ebo->next) {
				if (ebo->parent == curbone) {
					ebo->parent = NULL;
					ebo->temp = NULL; /* this is needed to prevent random crashes with in ED_armature_from_edit */
					ebo->flag &= ~BONE_CONNECTED;
				}
			}
			
			/* clear the pchan->parent var of any pchan that had this as its parent */
			for (pchn = ob->pose->chanbase.first; pchn; pchn = pchn->next) {
				if (pchn->parent == pchan)
					pchn->parent = NULL;
			}
			
			/* free any of the extra-data this pchan might have */
			BKE_pose_channel_free(pchan);
			BKE_pose_channels_hash_free(ob->pose);
			
			/* get rid of unneeded bone */
			bone_free(arm, curbone);
			BLI_freelinkN(&ob->pose->chanbase, pchan);
		}
	}
	
	/* exit editmode (recalculates pchans too) */
	ED_armature_from_edit(ob);
	ED_armature_edit_free(ob);
}

/* separate selected bones into their armature */
static int separate_armature_exec(bContext *C, wmOperator *UNUSED(op))
{
	Main *bmain = CTX_data_main(C);
	Scene *scene = CTX_data_scene(C);
	Object *obedit = CTX_data_edit_object(C);
	Object *oldob, *newob;
	Base *oldbase, *newbase;
	
	/* sanity checks */
	if (obedit == NULL)
		return OPERATOR_CANCELLED;
	
	/* set wait cursor in case this takes a while */
	WM_cursor_wait(1);
	
	/* we are going to do this as follows (unlike every other instance of separate):
	 *	1. exit editmode +posemode for active armature/base. Take note of what this is.
	 *	2. duplicate base - BASACT is the new one now
	 *	3. for each of the two armatures, enter editmode -> remove appropriate bones -> exit editmode + recalc
	 *	4. fix constraint links
	 *	5. make original armature active and enter editmode
	 */

	/* 1) only edit-base selected */
	/* TODO: use context iterators for this? */
	CTX_DATA_BEGIN(C, Base *, base, visible_bases)
	{
		if (base->object == obedit) base->flag |= 1;
		else base->flag &= ~1;
	}
	CTX_DATA_END;
	
	/* 1) store starting settings and exit editmode */
	oldob = obedit;
	oldbase = BASACT;
	oldob->mode &= ~OB_MODE_POSE;
	//oldbase->flag &= ~OB_POSEMODE;
	
	ED_armature_from_edit(obedit);
	ED_armature_edit_free(obedit);
	
	/* 2) duplicate base */
	newbase = ED_object_add_duplicate(bmain, scene, oldbase, USER_DUP_ARM); /* only duplicate linked armature */
	DAG_scene_sort(bmain, scene);

	newob = newbase->object;
	newbase->flag &= ~SELECT;
	
	
	/* 3) remove bones that shouldn't still be around on both armatures */
	separate_armature_bones(oldob, 1);
	separate_armature_bones(newob, 0);
	
	
	/* 4) fix links before depsgraph flushes */ // err... or after?
	separated_armature_fix_links(oldob, newob);
	
	DAG_id_tag_update(&oldob->id, OB_RECALC_DATA);  /* this is the original one */
	DAG_id_tag_update(&newob->id, OB_RECALC_DATA);  /* this is the separated one */
	
	
	/* 5) restore original conditions */
	obedit = oldob;
	
	ED_armature_to_edit(obedit);
	
	/* note, notifier might evolve */
	WM_event_add_notifier(C, NC_OBJECT | ND_POSE, obedit);
	
	/* recalc/redraw + cleanup */
	WM_cursor_wait(0);
	
	return OPERATOR_FINISHED;
}

void ARMATURE_OT_separate(wmOperatorType *ot)
{
	/* identifiers */
	ot->name = "Separate Bones";
	ot->idname = "ARMATURE_OT_separate";
	ot->description = "Isolate selected bones into a separate armature";
	
	/* callbacks */
	ot->invoke = WM_operator_confirm;
	ot->exec = separate_armature_exec;
	ot->poll = ED_operator_editarmature;
	
	/* flags */
	ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}

/* **************** END tools on Editmode Armature **************** */
/* **************** PoseMode & EditMode *************************** */

/* only for opengl selection indices */
Bone *get_indexed_bone(Object *ob, int index)
{
	bPoseChannel *pchan;
	if (ob->pose == NULL) return NULL;
	index >>= 16;     // bone selection codes use left 2 bytes
	
	pchan = BLI_findlink(&ob->pose->chanbase, index);
	return pchan ? pchan->bone : NULL;
}

/* See if there are any selected bones in this buffer */
/* only bones from base are checked on */
static void *get_bone_from_selectbuffer(Scene *scene, Base *base, unsigned int *buffer, short hits, short findunsel)
{
	Object *obedit = scene->obedit; // XXX get from context
	Bone *bone;
	EditBone *ebone;
	void *firstunSel = NULL, *firstSel = NULL, *data;
	unsigned int hitresult;
	short i, takeNext = 0, sel;
	
	for (i = 0; i < hits; i++) {
		hitresult = buffer[3 + (i * 4)];
		
		if (!(hitresult & BONESEL_NOSEL)) { // -1
			if (hitresult & BONESEL_ANY) {  // to avoid including objects in selection
				
				hitresult &= ~(BONESEL_ANY);
				/* Determine what the current bone is */
				if (obedit == NULL || base->object != obedit) {
					/* no singular posemode, so check for correct object */
					if (base->selcol == (hitresult & 0xFFFF)) {
						bone = get_indexed_bone(base->object, hitresult);
						
						if (findunsel)
							sel = (bone->flag & BONE_SELECTED);
						else
							sel = !(bone->flag & BONE_SELECTED);
						
						data = bone;
					}
					else {
						data = NULL;
						sel = 0;
					}
				}
				else {
					bArmature *arm = obedit->data;
					
					ebone = BLI_findlink(arm->edbo, hitresult);
					if (findunsel)
						sel = (ebone->flag & BONE_SELECTED);
					else
						sel = !(ebone->flag & BONE_SELECTED);
					
					data = ebone;
				}
				
				if (data) {
					if (sel) {
						if (!firstSel) firstSel = data;
						takeNext = 1;
					}
					else {
						if (!firstunSel)
							firstunSel = data;
						if (takeNext)
							return data;
					}
				}
			}
		}
	}
	
	if (firstunSel)
		return firstunSel;
	else 
		return firstSel;
}



/* used by posemode as well editmode */
/* only checks scene->basact! */
/* x and y are mouse coords (area space) */
static void *get_nearest_bone(bContext *C, short findunsel, int x, int y)
{
	ViewContext vc;
	rcti rect;
	unsigned int buffer[MAXPICKBUF];
	short hits;
	
	view3d_set_viewcontext(C, &vc);
	
	// rect.xmin = ... mouseco!
	rect.xmin = rect.xmax = x;
	rect.ymin = rect.ymax = y;
	
	glInitNames();
	hits = view3d_opengl_select(&vc, buffer, MAXPICKBUF, &rect);

	if (hits > 0)
		return get_bone_from_selectbuffer(vc.scene, vc.scene->basact, buffer, hits, findunsel);
	
	return NULL;
}

/* Get the first available child of an editbone */
static EditBone *editbone_get_child(bArmature *arm, EditBone *pabone, short use_visibility)
{
	EditBone *curbone, *chbone = NULL;
	
	for (curbone = arm->edbo->first; curbone; curbone = curbone->next) {
		if (curbone->parent == pabone) {
			if (use_visibility) {
				if ((arm->layer & curbone->layer) && !(pabone->flag & BONE_HIDDEN_A)) {
					chbone = curbone;
				}
			}
			else
				chbone = curbone;
		}
	}
	
	return chbone;
}

/* **************** END PoseMode & EditMode *************************** */
/* **************** Posemode stuff ********************** */


static void selectconnected_posebonechildren(Object *ob, Bone *bone, int extend)
{
	Bone *curBone;
	
	/* stop when unconnected child is encontered, or when unselectable bone is encountered */
	if (!(bone->flag & BONE_CONNECTED) || (bone->flag & BONE_UNSELECTABLE))
		return;
	
	/* XXX old cruft! use notifiers instead */
	//select_actionchannel_by_name (ob->action, bone->name, !(shift));
	
	if (extend)
		bone->flag &= ~BONE_SELECTED;
	else
		bone->flag |= BONE_SELECTED;
	
	for (curBone = bone->childbase.first; curBone; curBone = curBone->next)
		selectconnected_posebonechildren(ob, curBone, extend);
}

/* within active object context */
/* previously known as "selectconnected_posearmature" */
static int pose_select_connected_invoke(bContext *C, wmOperator *op, wmEvent *event)
{
	Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C));
	bArmature *arm = (bArmature *)ob->data;
	Bone *bone, *curBone, *next = NULL;
	int extend = RNA_boolean_get(op->ptr, "extend");

	view3d_operator_needs_opengl(C);
	
	if (extend)
		bone = get_nearest_bone(C, 0, event->mval[0], event->mval[1]);
	else
		bone = get_nearest_bone(C, 1, event->mval[0], event->mval[1]);
	
	if (!bone)
		return OPERATOR_CANCELLED;
	
	/* Select parents */
	for (curBone = bone; curBone; curBone = next) {
		/* ignore bone if cannot be selected */
		if ((curBone->flag & BONE_UNSELECTABLE) == 0) {
			// XXX old cruft! use notifiers instead
			//select_actionchannel_by_name (ob->action, curBone->name, !(shift));
			
			if (extend)
				curBone->flag &= ~BONE_SELECTED;
			else
				curBone->flag |= BONE_SELECTED;
			
			if (curBone->flag & BONE_CONNECTED)
				next = curBone->parent;
			else
				next = NULL;
		}
		else
			next = NULL;
	}
	
	/* Select children */
	for (curBone = bone->childbase.first; curBone; curBone = next)
		selectconnected_posebonechildren(ob, curBone, extend);
	
	/* updates */
	WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob);
	
	if (arm->flag & ARM_HAS_VIZ_DEPS) {
		/* mask modifier ('armature' mode), etc. */
		DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
	}

	return OPERATOR_FINISHED;
}

static int pose_select_linked_poll(bContext *C)
{
	return (ED_operator_view3d_active(C) && ED_operator_posemode(C));
}

void POSE_OT_select_linked(wmOperatorType *ot)
{
	/* identifiers */
	ot->name = "Select Connected";
	ot->idname = "POSE_OT_select_linked";
	ot->description = "Select bones related to selected ones by parent/child relationships";
	
	/* api callbacks */
	ot->exec = NULL;
	ot->invoke = pose_select_connected_invoke;
	ot->poll = pose_select_linked_poll;
	
	/* flags */
	ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
	
	/* props */
	RNA_def_boolean(ot->srna, "extend", FALSE, "Extend", "Extend selection instead of deselecting everything first");
}

/* **************** END Posemode stuff ********************** */
/* **************** EditMode stuff ********************** */

/* called in space.c */
/* previously "selectconnected_armature" */
static int armature_select_linked_invoke(bContext *C, wmOperator *op, wmEvent *event)
{
	bArmature *arm;
	EditBone *bone, *curBone, *next;
	int extend = RNA_boolean_get(op->ptr, "extend");
	Object *obedit = CTX_data_edit_object(C);
	arm = obedit->data;

	view3d_operator_needs_opengl(C);

	if (extend)
		bone = get_nearest_bone(C, 0, event->mval[0], event->mval[1]);
	else
		bone = get_nearest_bone(C, 1, event->mval[0], event->mval[1]);

	if (!bone)
		return OPERATOR_CANCELLED;

	/* Select parents */
	for (curBone = bone; curBone; curBone = next) {
		if ((curBone->flag & BONE_UNSELECTABLE) == 0) {
			if (extend) {
				curBone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
			}
			else {
				curBone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
			}
		}
		
		if (curBone->flag & BONE_CONNECTED)
			next = curBone->parent;
		else
			next = NULL;
	}

	/* Select children */
	while (bone) {
		for (curBone = arm->edbo->first; curBone; curBone = next) {
			next = curBone->next;
			if ((curBone->parent == bone) && (curBone->flag & BONE_UNSELECTABLE) == 0) {
				if (curBone->flag & BONE_CONNECTED) {
					if (extend)
						curBone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
					else
						curBone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
					bone = curBone;
					break;
				}
				else {
					bone = NULL;
					break;
				}
			}
		}
		if (!curBone)
			bone = NULL;
	}
	
	ED_armature_sync_selection(arm->edbo);
	
	WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, obedit);
	
	return OPERATOR_FINISHED;
}

static int armature_select_linked_poll(bContext *C)
{
	return (ED_operator_view3d_active(C) && ED_operator_editarmature(C) );
}

void ARMATURE_OT_select_linked(wmOperatorType *ot)
{
	/* identifiers */
	ot->name = "Select Connected";
	ot->idname = "ARMATURE_OT_select_linked";
	ot->description = "Select bones related to selected ones by parent/child relationships";
	
	/* api callbacks */
	ot->exec = NULL;
	ot->invoke = armature_select_linked_invoke;
	ot->poll = armature_select_linked_poll;
	
	/* flags */
	ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
	
	/* properties */
	RNA_def_boolean(ot->srna, "extend", FALSE, "Extend", "Extend selection instead of deselecting everything first");
}

/* does bones and points */
/* note that BONE ROOT only gets drawn for root bones (or without IK) */
static EditBone *get_nearest_editbonepoint(ViewContext *vc, const int mval[2],
                                           ListBase *edbo, int findunsel, int *selmask)
{
	bArmature *arm = (bArmature *)vc->obedit->data;
	EditBone *ebone_next_act = arm->act_edbone;

	EditBone *ebone;
	rcti rect;
	unsigned int buffer[MAXPICKBUF];
	unsigned int hitresult, besthitresult = BONESEL_NOSEL;
	int i, mindep = 4;
	short hits;

	glInitNames();

	/* find the bone after the current active bone, so as to bump up its chances in selection.
	 * this way overlapping bones will cycle selection state as with objects. */
	if (ebone_next_act &&
	    EBONE_VISIBLE(arm, ebone_next_act) &&
	    ebone_next_act->flag & (BONE_SELECTED | BONE_ROOTSEL | BONE_TIPSEL))
	{
		ebone_next_act = ebone_next_act->next ? ebone_next_act->next : arm->edbo->first;
	}
	else {
		ebone_next_act = NULL;
	}

	rect.xmin = mval[0] - 5;
	rect.xmax = mval[0] + 5;
	rect.ymin = mval[1] - 5;
	rect.ymax = mval[1] + 5;

	hits = view3d_opengl_select(vc, buffer, MAXPICKBUF, &rect);
	if (hits == 0) {
		rect.xmin = mval[0] - 12;
		rect.xmax = mval[0] + 12;
		rect.ymin = mval[1] - 12;
		rect.ymax = mval[1] + 12;
		hits = view3d_opengl_select(vc, buffer, MAXPICKBUF, &rect);
	}
	/* See if there are any selected bones in this group */
	if (hits > 0) {

		if (hits == 1) {
			if (!(buffer[3] & BONESEL_NOSEL))
				besthitresult = buffer[3];
		}
		else {
			for (i = 0; i < hits; i++) {
				hitresult = buffer[3 + (i * 4)];
				if (!(hitresult & BONESEL_NOSEL)) {
					int dep;

					ebone = BLI_findlink(edbo, hitresult & ~BONESEL_ANY);

					/* clicks on bone points get advantage */
					if (hitresult & (BONESEL_ROOT | BONESEL_TIP)) {
						/* but also the unselected one */
						if (findunsel) {
							if ( (hitresult & BONESEL_ROOT) && (ebone->flag & BONE_ROOTSEL) == 0)
								dep = 1;
							else if ( (hitresult & BONESEL_TIP) && (ebone->flag & BONE_TIPSEL) == 0)
								dep = 1;
							else
								dep = 2;
						}
						else dep = 2;
					}
					else {
						/* bone found */
						if (findunsel) {
							if ((ebone->flag & BONE_SELECTED) == 0)
								dep = 2;
							else
								dep = 3;
						}
						else dep = 3;
					}

					if (ebone == ebone_next_act) {
						dep -= 1;
					}

					if (dep < mindep) {
						mindep = dep;
						besthitresult = hitresult;
					}
				}
			}
		}

		if (!(besthitresult & BONESEL_NOSEL)) {

			ebone = BLI_findlink(edbo, besthitresult & ~BONESEL_ANY);

			*selmask = 0;
			if (besthitresult & BONESEL_ROOT)
				*selmask |= BONE_ROOTSEL;
			if (besthitresult & BONESEL_TIP)
				*selmask |= BONE_TIPSEL;
			if (besthitresult & BONESEL_BONE)
				*selmask |= BONE_SELECTED;
			return ebone;
		}
	}
	*selmask = 0;
	return NULL;
}

/* previously delete_armature */
/* only editmode! */
static int armature_delete_selected_exec(bContext *C, wmOperator *UNUSED(op))
{
	bArmature *arm;
	EditBone *curBone, *ebone_next;
	bConstraint *con;
	Object *obedit = CTX_data_edit_object(C); // XXX get from context
	arm = obedit->data;

	/* cancel if nothing selected */
	if (CTX_DATA_COUNT(C, selected_bones) == 0)
		return OPERATOR_CANCELLED;
	
	armature_select_mirrored(arm);
	
	/*  First erase any associated pose channel */
	if (obedit->pose) {
		bPoseChannel *pchan, *pchan_next;
		for (pchan = obedit->pose->chanbase.first; pchan; pchan = pchan_next) {
			pchan_next = pchan->next;
			curBone = editbone_name_exists(arm->edbo, pchan->name);
			
			if (curBone && (curBone->flag & BONE_SELECTED) && (arm->layer & curBone->layer)) {
				BKE_pose_channel_free(pchan);
				BKE_pose_channels_hash_free(obedit->pose);
				BLI_freelinkN(&obedit->pose->chanbase, pchan);
			}
			else {
				for (con = pchan->constraints.first; con; con = con->next) {
					bConstraintTypeInfo *cti = BKE_constraint_get_typeinfo(con);
					ListBase targets = {NULL, NULL};
					bConstraintTarget *ct;
					
					if (cti && cti->get_constraint_targets) {
						cti->get_constraint_targets(con, &targets);
						
						for (ct = targets.first; ct; ct = ct->next) {
							if (ct->tar == obedit) {
								if (ct->subtarget[0]) {
									curBone = editbone_name_exists(arm->edbo, ct->subtarget);
									if (curBone && (curBone->flag & BONE_SELECTED) && (arm->layer & curBone->layer)) {
										con->flag |= CONSTRAINT_DISABLE;
										ct->subtarget[0] = 0;
									}
								}
							}
						}
						
						if (cti->flush_constraint_targets)
							cti->flush_constraint_targets(con, &targets, 0);
					}
				}
			}
		}
	}
	
	
	for (curBone = arm->edbo->first; curBone; curBone = ebone_next) {
		ebone_next = curBone->next;
		if (arm->layer & curBone->layer) {
			if (curBone->flag & BONE_SELECTED) {
				if (curBone == arm->act_edbone) arm->act_edbone = NULL;
				ED_armature_edit_bone_remove(arm, curBone);
			}
		}
	}
	
	
	ED_armature_sync_selection(arm->edbo);

	WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, obedit);

	return OPERATOR_FINISHED;
}

void ARMATURE_OT_delete(wmOperatorType *ot)
{
	/* identifiers */
	ot->name = "Delete Selected Bone(s)";
	ot->idname = "ARMATURE_OT_delete";
	ot->description = "Remove selected bones from the armature";
	
	/* api callbacks */
	ot->invoke = WM_operator_confirm;
	ot->exec = armature_delete_selected_exec;
	ot->poll = ED_operator_editarmature;
	
	/* flags */
	ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}

/* toggle==0: deselect
 * toggle==1: swap (based on test)
 * toggle==2: swap (no test), CURRENTLY UNUSED
 */
void ED_armature_deselect_all(Object *obedit, int toggle)
{
	bArmature *arm = obedit->data;
	EditBone    *eBone;
	int sel = 1;
	
	if (toggle == 1) {
		/* Determine if there are any selected bones
		 * and therefore whether we are selecting or deselecting */
		for (eBone = arm->edbo->first; eBone; eBone = eBone->next) {
			//			if (arm->layer & eBone->layer) {
			if (eBone->flag & (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL)) {
				sel = 0;
				break;
			}
			//			}
		}
	}
	else sel = toggle;
	
	/*	Set the flags */
	for (eBone = arm->edbo->first; eBone; eBone = eBone->next) {
		if (sel == 2) {
			/* invert selection of bone */
			if (EBONE_VISIBLE(arm, eBone)) {
				eBone->flag ^= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
				if (arm->act_edbone == eBone)
					arm->act_edbone = NULL;
			}
		}
		else if (sel == 1) {
			/* select bone */
			if (EBONE_VISIBLE(arm, eBone)) {
				eBone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
				if (eBone->parent)
					eBone->parent->flag |= (BONE_TIPSEL);
			}
		}
		else {
			/* deselect bone */
			eBone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
			if (arm->act_edbone == eBone)
				arm->act_edbone = NULL;
		}
	}
	
	ED_armature_sync_selection(arm->edbo);
}

void ED_armature_deselect_all_visible(Object *obedit)
{
	bArmature *arm = obedit->data;
	EditBone    *ebone;

	for (ebone = arm->edbo->first; ebone; ebone = ebone->next) {
		/* first and foremost, bone must be visible and selected */
		if (EBONE_SELECTABLE(arm, ebone)) {
			ebone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
		}
	}

	ED_armature_sync_selection(arm->edbo);
}

/* accounts for connected parents */
static int ebone_select_flag(EditBone *ebone)
{
	if (ebone->parent && (ebone->flag & BONE_CONNECTED)) {
		return ((ebone->parent->flag & BONE_TIPSEL) ? BONE_ROOTSEL : 0) | (ebone->flag & (BONE_SELECTED | BONE_TIPSEL));
	}
	else {
		return ebone->flag & (BONE_SELECTED | BONE_ROOTSEL | BONE_TIPSEL);
	}
}

/* context: editmode armature in view3d */
int mouse_armature(bContext *C, const int mval[2], int extend, int deselect, int toggle)
{
	Object *obedit = CTX_data_edit_object(C);
	bArmature *arm = obedit->data;
	ViewContext vc;
	EditBone *nearBone = NULL;
	int selmask;

	view3d_set_viewcontext(C, &vc);
	
	BIF_sk_selectStroke(C, mval, extend);
	
	nearBone = get_nearest_editbonepoint(&vc, mval, arm->edbo, 1, &selmask);
	if (nearBone) {

		if (!extend && !deselect && !toggle)
			ED_armature_deselect_all(obedit, 0);
		
		/* by definition the non-root connected bones have no root point drawn,
		 * so a root selection needs to be delivered to the parent tip */
		
		if (selmask & BONE_SELECTED) {
			if (nearBone->parent && (nearBone->flag & BONE_CONNECTED)) {
				/* click in a chain */
				if (extend) {
					/* select this bone */
					nearBone->flag |= BONE_TIPSEL;
					nearBone->parent->flag |= BONE_TIPSEL;
				}
				else if (deselect) {
					/* deselect this bone */
					nearBone->flag &= ~(BONE_TIPSEL | BONE_SELECTED);
					/* only deselect parent tip if it is not selected */
					if (!(nearBone->parent->flag & BONE_SELECTED))
						nearBone->parent->flag &= ~BONE_TIPSEL;
				}
				else if (toggle) {
					/* hold shift inverts this bone's selection */
					if (nearBone->flag & BONE_SELECTED) {
						/* deselect this bone */
						nearBone->flag &= ~(BONE_TIPSEL | BONE_SELECTED);
						/* only deselect parent tip if it is not selected */
						if (!(nearBone->parent->flag & BONE_SELECTED))
							nearBone->parent->flag &= ~BONE_TIPSEL;
					}
					else {
						/* select this bone */
						nearBone->flag |= BONE_TIPSEL;
						nearBone->parent->flag |= BONE_TIPSEL;
					}
				}
				else {
					/* select this bone */
					nearBone->flag |= BONE_TIPSEL;
					nearBone->parent->flag |= BONE_TIPSEL;
				}
			}
			else {
				if (extend) {
					nearBone->flag |= (BONE_TIPSEL | BONE_ROOTSEL);
				}
				else if (deselect) {
					nearBone->flag &= ~(BONE_TIPSEL | BONE_ROOTSEL);
				}
				else if (toggle) {
					/* hold shift inverts this bone's selection */
					if (nearBone->flag & BONE_SELECTED)
						nearBone->flag &= ~(BONE_TIPSEL | BONE_ROOTSEL);
					else
						nearBone->flag |= (BONE_TIPSEL | BONE_ROOTSEL);
				}
				else
					nearBone->flag |= (BONE_TIPSEL | BONE_ROOTSEL);
			}
		}
		else {
			if (extend)
				nearBone->flag |= selmask;
			else if (deselect)
				nearBone->flag &= ~selmask;
			else if (toggle && (nearBone->flag & selmask))
				nearBone->flag &= ~selmask;
			else
				nearBone->flag |= selmask;
		}
		
		ED_armature_sync_selection(arm->edbo);
		
		if (nearBone) {
			/* then now check for active status */
			if (ebone_select_flag(nearBone)) {
				arm->act_edbone = nearBone;
			}
		}
		
		WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, vc.obedit);
		return 1;
	}

	return 0;
}

void ED_armature_edit_free(struct Object *ob)
{
	bArmature *arm = ob->data;
	EditBone *eBone;
	
	/*	Clear the editbones list */
	if (arm->edbo) {
		if (arm->edbo->first) {
			for (eBone = arm->edbo->first; eBone; eBone = eBone->next) {
				if (eBone->prop) {
					IDP_FreeProperty(eBone->prop);
					MEM_freeN(eBone->prop);
				}
			}

			BLI_freelistN(arm->edbo);
		}
		MEM_freeN(arm->edbo);
		arm->edbo = NULL;
	}
}

/* Put armature in EditMode */
void ED_armature_to_edit(Object *ob)
{
	bArmature *arm = ob->data;
	
	ED_armature_edit_free(ob);
	arm->edbo = MEM_callocN(sizeof(ListBase), "edbo armature");
	arm->act_edbone = make_boneList(arm->edbo, &arm->bonebase, NULL, arm->act_bone);
	arm->act_bone = NULL;

//	BIF_freeTemplates(); /* force template update when entering editmode */
}


/* adjust bone roll to align Z axis with vector
 * vec is in local space and is normalized
 */

float ED_rollBoneToVector(EditBone *bone, const float align_axis[3], const short axis_only)
{
	float mat[3][3], nor[3];

	sub_v3_v3v3(nor, bone->tail, bone->head);
	vec_roll_to_mat3(nor, 0.0f, mat);
	
	/* check the bone isn't aligned with the axis */
	if (!is_zero_v3(align_axis) && angle_v3v3(align_axis, mat[2]) > FLT_EPSILON) {
		float vec[3], align_axis_proj[3], roll;

		/* project the new_up_axis along the normal */
		project_v3_v3v3(vec, align_axis, nor);
		sub_v3_v3v3(align_axis_proj, align_axis, vec);
		
		if (axis_only) {
			if (angle_v3v3(align_axis_proj, mat[2]) > (float)(M_PI / 2.0)) {
				negate_v3(align_axis_proj);
			}
		}
		
		roll = angle_v3v3(align_axis_proj, mat[2]);
		
		cross_v3_v3v3(vec, mat[2], align_axis_proj);
		
		if (dot_v3v3(vec, nor) < 0) {
			roll = -roll;
		}

		return roll;
	}

	return 0.0f;
}


static EnumPropertyItem prop_calc_roll_types[] = {
	{0, "X", 0, "X Axis", ""},
	{1, "Y", 0, "Y Axis", ""},
	{2, "Z", 0, "Z Axis", ""},
	{5, "ACTIVE", 0, "Active Bone", ""},
	{6, "VIEW", 0, "View Axis", ""},
	{7, "CURSOR", 0, "Cursor", ""},
	{0, NULL, 0, NULL, NULL}
};


static int armature_calc_roll_exec(bContext *C, wmOperator *op) 
{
	Object *ob = CTX_data_edit_object(C);
	const short type = RNA_enum_get(op->ptr, "type");
	const short axis_only = RNA_boolean_get(op->ptr, "axis_only");
	const short axis_flip = RNA_boolean_get(op->ptr, "axis_flip");

	float imat[3][3];

	bArmature *arm = ob->data;
	EditBone *ebone;

	copy_m3_m4(imat, ob->obmat);
	invert_m3(imat);

	if (type == 7) { /* Cursor */
		Scene *scene = CTX_data_scene(C);
		View3D *v3d = CTX_wm_view3d(C); /* can be NULL */
		float cursor_local[3];
		const float   *cursor = give_cursor(scene, v3d);
	

		copy_v3_v3(cursor_local, cursor);
		mul_m3_v3(imat, cursor_local);

		/* cursor */
		for (ebone = arm->edbo->first; ebone; ebone = ebone->next) {
			if (EBONE_VISIBLE(arm, ebone) && EBONE_EDITABLE(ebone)) {
				float cursor_rel[3];
				sub_v3_v3v3(cursor_rel, cursor_local, ebone->head);
				if (axis_flip) negate_v3(cursor_rel);
				ebone->roll = ED_rollBoneToVector(ebone, cursor_rel, axis_only);
			}
		}
	}
	else {
		float vec[3] = {0.0f, 0.0f, 0.0f};
		if (type == 6) { /* View */
			RegionView3D *rv3d = CTX_wm_region_view3d(C);
			if (rv3d == NULL) {
				BKE_report(op->reports, RPT_ERROR, "No region view3d available");
				return OPERATOR_CANCELLED;
			}

			copy_v3_v3(vec, rv3d->viewinv[2]);
			mul_m3_v3(imat, vec);
		}
		else if (type == 5) {
			float mat[3][3], nor[3];
			ebone = (EditBone *)arm->act_edbone;
			if (ebone == NULL) {
				BKE_report(op->reports, RPT_ERROR, "No active bone set");
				return OPERATOR_CANCELLED;
			}

			sub_v3_v3v3(nor, ebone->tail, ebone->head);
			vec_roll_to_mat3(nor, ebone->roll, mat);
			copy_v3_v3(vec, mat[2]);
		}
		else { /* Axis */
			assert(type >= 0 && type <= 5);
			if (type < 3) vec[type] = 1.0f;
			else vec[type - 2] = -1.0f;
			mul_m3_v3(imat, vec);
		}

		if (axis_flip) negate_v3(vec);

		for (ebone = arm->edbo->first; ebone; ebone = ebone->next) {
			if (EBONE_VISIBLE(arm, ebone) && EBONE_EDITABLE(ebone)) {
				/* roll func is a callback which assumes that all is well */
				ebone->roll = ED_rollBoneToVector(ebone, vec, axis_only);
			}
		}
	}

	if (arm->flag & ARM_MIRROR_EDIT) {
		for (ebone = arm->edbo->first; ebone; ebone = ebone->next) {
			if ((EBONE_VISIBLE(arm, ebone) && EBONE_EDITABLE(ebone)) == 0) {
				EditBone *ebone_mirr = ED_armature_bone_get_mirrored(arm->edbo, ebone);
				if (ebone_mirr && (EBONE_VISIBLE(arm, ebone_mirr) && EBONE_EDITABLE(ebone_mirr))) {
					ebone->roll = -ebone_mirr->roll;
				}
			}
		}
	}

	/* note, notifier might evolve */
	WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob);
	
	return OPERATOR_FINISHED;
}

void ARMATURE_OT_calculate_roll(wmOperatorType *ot)
{
	/* identifiers */
	ot->name = "Recalculate Roll";
	ot->idname = "ARMATURE_OT_calculate_roll";
	ot->description = "Automatically fix alignment of select bones' axes";
	
	/* api callbacks */
	ot->invoke = WM_menu_invoke;
	ot->exec = armature_calc_roll_exec;
	ot->poll = ED_operator_editarmature;
	
	/* flags */
	ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;

	/* properties */
	ot->prop = RNA_def_enum(ot->srna, "type", prop_calc_roll_types, 0, "Type", "");
	RNA_def_boolean(ot->srna, "axis_flip", 0, "Flip Axis", "Negate the alignment axis");
	RNA_def_boolean(ot->srna, "axis_only", 0, "Shortest Rotation", "Ignore the axis direction, use the shortest rotation to align");
}

/* **************** undo for armatures ************** */

typedef struct UndoArmature {
	EditBone *act_edbone;
	ListBase lb;
} UndoArmature;

static void undoBones_to_editBones(void *uarmv, void *armv, void *UNUSED(data))
{
	UndoArmature *uarm = uarmv;
	bArmature *arm = armv;
	EditBone *ebo, *newebo;
	
	BLI_freelistN(arm->edbo);
	
	/* copy  */
	for (ebo = uarm->lb.first; ebo; ebo = ebo->next) {
		newebo = MEM_dupallocN(ebo);
		ebo->temp = newebo;
		BLI_addtail(arm->edbo, newebo);
	}
	
	/* active bone */
	if (uarm->act_edbone) {
		ebo = uarm->act_edbone;
		arm->act_edbone = ebo->temp;
	}
	else
		arm->act_edbone = NULL;

	/* set pointers */
	for (newebo = arm->edbo->first; newebo; newebo = newebo->next) {
		if (newebo->parent) newebo->parent = newebo->parent->temp;
	}
	/* be sure they don't hang ever */
	for (newebo = arm->edbo->first; newebo; newebo = newebo->next) {
		newebo->temp = NULL;
	}
}

static void *editBones_to_undoBones(void *armv, void *UNUSED(obdata))
{
	bArmature *arm = armv;
	UndoArmature *uarm;
	EditBone *ebo, *newebo;
	
	uarm = MEM_callocN(sizeof(UndoArmature), "listbase undo");
	
	/* copy */
	for (ebo = arm->edbo->first; ebo; ebo = ebo->next) {
		newebo = MEM_dupallocN(ebo);
		ebo->temp = newebo;
		BLI_addtail(&uarm->lb, newebo);
	}
	
	/* active bone */
	if (arm->act_edbone) {
		ebo = arm->act_edbone;
		uarm->act_edbone = ebo->temp;
	}

	/* set pointers */
	for (newebo = uarm->lb.first; newebo; newebo = newebo->next) {
		if (newebo->parent) newebo->parent = newebo->parent->temp;
	}
	
	return uarm;
}

static void free_undoBones(void *uarmv)
{
	UndoArmature *uarm = uarmv;
	
	BLI_freelistN(&uarm->lb);
	MEM_freeN(uarm);
}

static void *get_armature_edit(bContext *C)
{
	Object *obedit = CTX_data_edit_object(C);
	if (obedit && obedit->type == OB_ARMATURE) {
		return obedit->data;
	}
	return NULL;
}

/* and this is all the undo system needs to know */
void undo_push_armature(bContext *C, const char *name)
{
	// XXX solve getdata()
	undo_editmode_push(C, name, get_armature_edit, free_undoBones, undoBones_to_editBones, editBones_to_undoBones, NULL);
}



/* **************** END EditMode stuff ********************** */
/* *************** Adding stuff in editmode *************** */

/* default bone add, returns it selected, but without tail set */
/* XXX should be used everywhere, now it mallocs bones still locally in functions */
EditBone *ED_armature_edit_bone_add(bArmature *arm, const char *name)
{
	EditBone *bone = MEM_callocN(sizeof(EditBone), "eBone");
	
	BLI_strncpy(bone->name, name, sizeof(bone->name));
	unique_editbone_name(arm->edbo, bone->name, NULL);
	
	BLI_addtail(arm->edbo, bone);
	
	bone->flag |= BONE_TIPSEL;
	bone->weight = 1.0f;
	bone->dist = 0.25f;
	bone->xwidth = 0.1f;
	bone->zwidth = 0.1f;
	bone->ease1 = 1.0f;
	bone->ease2 = 1.0f;
	bone->rad_head = 0.10f;
	bone->rad_tail = 0.05f;
	bone->segments = 1;
	bone->layer = arm->layer;
	
	return bone;
}

/* v3d and rv3d are allowed to be NULL */
void add_primitive_bone(Scene *scene, View3D *v3d, RegionView3D *rv3d)
{
	Object *obedit = scene->obedit; // XXX get from context
	bArmature *arm = obedit->data;
	float obmat[3][3], curs[3], viewmat[3][3], totmat[3][3], imat[3][3];
	EditBone    *bone;

	/* Get inverse point for head and orientation for tail */
	invert_m4_m4(obedit->imat, obedit->obmat);
	mul_v3_m4v3(curs, obedit->imat, give_cursor(scene, v3d));

	if (rv3d && (U.flag & USER_ADD_VIEWALIGNED))
		copy_m3_m4(obmat, rv3d->viewmat);
	else unit_m3(obmat);
	
	copy_m3_m4(viewmat, obedit->obmat);
	mul_m3_m3m3(totmat, obmat, viewmat);
	invert_m3_m3(imat, totmat);
	
	ED_armature_deselect_all(obedit, 0);
	
	/* Create a bone */
	bone = ED_armature_edit_bone_add(arm, "Bone");

	arm->act_edbone = bone;

	copy_v3_v3(bone->head, curs);
	
	if (rv3d && (U.flag & USER_ADD_VIEWALIGNED))
		add_v3_v3v3(bone->tail, bone->head, imat[1]);   // bone with unit length 1
	else
		add_v3_v3v3(bone->tail, bone->head, imat[2]);   // bone with unit length 1, pointing up Z
}


/* previously addvert_armature */
/* the ctrl-click method */
static int armature_click_extrude_exec(bContext *C, wmOperator *UNUSED(op))
{
	View3D *v3d;
	bArmature *arm;
	EditBone *ebone, *newbone, *flipbone;
	float mat[3][3], imat[3][3];
	const float *curs;
	int a, to_root = 0;
	Object *obedit;
	Scene *scene;

	scene = CTX_data_scene(C);
	v3d = CTX_wm_view3d(C);
	obedit = CTX_data_edit_object(C);
	arm = obedit->data;
	
	/* find the active or selected bone */
	for (ebone = arm->edbo->first; ebone; ebone = ebone->next) {
		if (EBONE_VISIBLE(arm, ebone)) {
			if (ebone->flag & BONE_TIPSEL || arm->act_edbone == ebone)
				break;
		}
	}
	
	if (ebone == NULL) {
		for (ebone = arm->edbo->first; ebone; ebone = ebone->next) {
			if (EBONE_VISIBLE(arm, ebone)) {
				if (ebone->flag & BONE_ROOTSEL || arm->act_edbone == ebone)
					break;
			}
		}
		if (ebone == NULL) 
			return OPERATOR_CANCELLED;
		
		to_root = 1;
	}
	
	ED_armature_deselect_all(obedit, 0);
	
	/* we re-use code for mirror editing... */
	flipbone = NULL;
	if (arm->flag & ARM_MIRROR_EDIT)
		flipbone = ED_armature_bone_get_mirrored(arm->edbo, ebone);

	for (a = 0; a < 2; a++) {
		if (a == 1) {
			if (flipbone == NULL)
				break;
			else {
				SWAP(EditBone *, flipbone, ebone);
			}
		}
		
		newbone = ED_armature_edit_bone_add(arm, ebone->name);
		arm->act_edbone = newbone;
		
		if (to_root) {
			copy_v3_v3(newbone->head, ebone->head);
			newbone->rad_head = ebone->rad_tail;
			newbone->parent = ebone->parent;
		}
		else {
			copy_v3_v3(newbone->head, ebone->tail);
			newbone->rad_head = ebone->rad_tail;
			newbone->parent = ebone;
			newbone->flag |= BONE_CONNECTED;
		}
		
		curs = give_cursor(scene, v3d);
		copy_v3_v3(newbone->tail, curs);
		sub_v3_v3v3(newbone->tail, newbone->tail, obedit->obmat[3]);
		
		if (a == 1)
			newbone->tail[0] = -newbone->tail[0];
		
		copy_m3_m4(mat, obedit->obmat);
		invert_m3_m3(imat, mat);
		mul_m3_v3(imat, newbone->tail);
		
		newbone->length = len_v3v3(newbone->head, newbone->tail);
		newbone->rad_tail = newbone->length * 0.05f;
		newbone->dist = newbone->length * 0.25f;
		
	}
	
	ED_armature_sync_selection(arm->edbo);

	WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, obedit);
	
	return OPERATOR_FINISHED;
}

static int armature_click_extrude_invoke(bContext *C, wmOperator *op, wmEvent *event)
{
	/* TODO most of this code is copied from set3dcursor_invoke,
	 * it would be better to reuse code in set3dcursor_invoke */

	/* temporarily change 3d cursor position */
	Scene *scene;
	ARegion *ar;
	View3D *v3d;
	float *fp, tvec[3], oldcurs[3], mval_f[2];
	int retv;

	scene = CTX_data_scene(C);
	ar = CTX_wm_region(C);
	v3d = CTX_wm_view3d(C);
	
	fp = give_cursor(scene, v3d);
	
	copy_v3_v3(oldcurs, fp);

	VECCOPY2D(mval_f, event->mval);
	ED_view3d_win_to_3d(ar, fp, mval_f, tvec);
	copy_v3_v3(fp, tvec);

	/* extrude to the where new cursor is and store the operation result */
	retv = armature_click_extrude_exec(C, op);

	/* restore previous 3d cursor position */
	copy_v3_v3(fp, oldcurs);

	return retv;
}

void ARMATURE_OT_click_extrude(wmOperatorType *ot)
{
	/* identifiers */
	ot->name = "Click-Extrude";
	ot->idname = "ARMATURE_OT_click_extrude";
	ot->description = "Create a new bone going from the last selected joint to the mouse position";
	
	/* api callbacks */
	ot->invoke = armature_click_extrude_invoke;
	ot->exec = armature_click_extrude_exec;
	ot->poll = ED_operator_editarmature;
	
	/* flags */
	ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;

	/* props */
}

/* adds an EditBone between the nominated locations (should be in the right space) */
static EditBone *add_points_bone(Object *obedit, float head[], float tail[])
{
	EditBone *ebo;
	
	ebo = ED_armature_edit_bone_add(obedit->data, "Bone");
	
	copy_v3_v3(ebo->head, head);
	copy_v3_v3(ebo->tail, tail);
	
	return ebo;
}


static EditBone *get_named_editbone(ListBase *edbo, char *name)
{
	EditBone  *eBone;

	if (name) {
		for (eBone = edbo->first; eBone; eBone = eBone->next) {
			if (!strcmp(name, eBone->name))
				return eBone;
		}
	}

	return NULL;
}

/* Call this before doing any duplications
 * */
void preEditBoneDuplicate(ListBase *editbones)
{
	EditBone *eBone;
	
	/* clear temp */
	for (eBone = editbones->first; eBone; eBone = eBone->next) {
		eBone->temp = NULL;
	}
}

/*
 * Note: When duplicating cross objects, editbones here is the list of bones
 * from the SOURCE object but ob is the DESTINATION object
 * */
void updateDuplicateSubtargetObjects(EditBone *dupBone, ListBase *editbones, Object *src_ob, Object *dst_ob)
{
	/* If an edit bone has been duplicated, lets
	 * update it's constraints if the subtarget
	 * they point to has also been duplicated
	 */
	EditBone     *oldtarget, *newtarget;
	bPoseChannel *pchan;
	bConstraint  *curcon;
	ListBase     *conlist;
	
	if ( (pchan = BKE_pose_channel_verify(dst_ob->pose, dupBone->name)) ) {
		if ( (conlist = &pchan->constraints) ) {
			for (curcon = conlist->first; curcon; curcon = curcon->next) {
				/* does this constraint have a subtarget in
				 * this armature?
				 */
				bConstraintTypeInfo *cti = BKE_constraint_get_typeinfo(curcon);
				ListBase targets = {NULL, NULL};
				bConstraintTarget *ct;
				
				if (cti && cti->get_constraint_targets) {
					cti->get_constraint_targets(curcon, &targets);
					
					for (ct = targets.first; ct; ct = ct->next) {
						if ((ct->tar == src_ob) && (ct->subtarget[0])) {
							ct->tar = dst_ob; /* update target */ 
							oldtarget = get_named_editbone(editbones, ct->subtarget);
							if (oldtarget) {
								/* was the subtarget bone duplicated too? If
								 * so, update the constraint to point at the 
								 * duplicate of the old subtarget.
								 */
								if (oldtarget->temp) {
									newtarget = (EditBone *) oldtarget->temp;
									BLI_strncpy(ct->subtarget, newtarget->name, sizeof(ct->subtarget));
								}
							}
						}
					}
					
					if (cti->flush_constraint_targets)
						cti->flush_constraint_targets(curcon, &targets, 0);
				}
			}
		}
	}
}

void updateDuplicateSubtarget(EditBone *dupBone, ListBase *editbones, Object *ob)
{
	updateDuplicateSubtargetObjects(dupBone, editbones, ob, ob);
}


EditBone *duplicateEditBoneObjects(EditBone *curBone, const char *name, ListBase *editbones,
                                   Object *src_ob, Object *dst_ob)
{
	EditBone *eBone = MEM_mallocN(sizeof(EditBone), "addup_editbone");
	
	/*	Copy data from old bone to new bone */
	memcpy(eBone, curBone, sizeof(EditBone));
	
	curBone->temp = eBone;
	eBone->temp = curBone;
	
	if (name != NULL) {
		BLI_strncpy(eBone->name, name, sizeof(eBone->name));
	}

	unique_editbone_name(editbones, eBone->name, NULL);
	BLI_addtail(editbones, eBone);
	
	/* copy the ID property */
	if (curBone->prop)
		eBone->prop = IDP_CopyProperty(curBone->prop);

	/* Lets duplicate the list of constraints that the
	 * current bone has.
	 */
	if (src_ob->pose) {
		bPoseChannel *chanold, *channew;
		
		chanold = BKE_pose_channel_verify(src_ob->pose, curBone->name);
		if (chanold) {
			/* WARNING: this creates a new posechannel, but there will not be an attached bone
			 *		yet as the new bones created here are still 'EditBones' not 'Bones'.
			 */
			channew = BKE_pose_channel_verify(dst_ob->pose, eBone->name);

			if (channew) {
				BKE_pose_channel_copy_data(channew, chanold);
			}
		}
	}
	
	return eBone;
}

EditBone *duplicateEditBone(EditBone *curBone, const char *name, ListBase *editbones, Object *ob)
{
	return duplicateEditBoneObjects(curBone, name, editbones, ob, ob);
}

/* previously adduplicate_armature */
static int armature_duplicate_selected_exec(bContext *C, wmOperator *UNUSED(op))
{
	bArmature *arm;
	EditBone    *eBone = NULL;
	EditBone    *curBone;
	EditBone    *firstDup = NULL; /*	The beginning of the duplicated bones in the edbo list */

	Object *obedit = CTX_data_edit_object(C);
	arm = obedit->data;

	/* cancel if nothing selected */
	if (CTX_DATA_COUNT(C, selected_bones) == 0)
		return OPERATOR_CANCELLED;
	
	ED_armature_sync_selection(arm->edbo); // XXX why is this needed?

	preEditBoneDuplicate(arm->edbo);

	/* Select mirrored bones */
	if (arm->flag & ARM_MIRROR_EDIT) {
		for (curBone = arm->edbo->first; curBone; curBone = curBone->next) {
			if (EBONE_VISIBLE(arm, curBone)) {
				if (curBone->flag & BONE_SELECTED) {
					eBone = ED_armature_bone_get_mirrored(arm->edbo, curBone);
					if (eBone)
						eBone->flag |= BONE_SELECTED;
				}
			}
		}
	}

	
	/*	Find the selected bones and duplicate them as needed */
	for (curBone = arm->edbo->first; curBone && curBone != firstDup; curBone = curBone->next) {
		if (EBONE_VISIBLE(arm, curBone)) {
			if (curBone->flag & BONE_SELECTED) {
				
				eBone = duplicateEditBone(curBone, curBone->name, arm->edbo, obedit);
				
				if (!firstDup)
					firstDup = eBone;

			}
		}
	}

	/*	Run though the list and fix the pointers */
	for (curBone = arm->edbo->first; curBone && curBone != firstDup; curBone = curBone->next) {
		if (EBONE_VISIBLE(arm, curBone)) {
			if (curBone->flag & BONE_SELECTED) {
				eBone = (EditBone *) curBone->temp;
				
				if (!curBone->parent) {
					/* If this bone has no parent,
					 * Set the duplicate->parent to NULL
					 */
					eBone->parent = NULL;
				}
				else if (curBone->parent->temp) {
					/* If this bone has a parent that was duplicated,
					 * Set the duplicate->parent to the curBone->parent->temp
					 */
					eBone->parent = (EditBone *)curBone->parent->temp;
				}
				else {
					/* If this bone has a parent that IS not selected,
					 * Set the duplicate->parent to the curBone->parent
					 */
					eBone->parent = (EditBone *) curBone->parent;
					eBone->flag &= ~BONE_CONNECTED;
				}
				
				/* Lets try to fix any constraint subtargets that might
				 * have been duplicated 
				 */
				updateDuplicateSubtarget(eBone, arm->edbo, obedit);
			}
		}
	}
	
	/* correct the active bone */
	if (arm->act_edbone) {
		eBone = arm->act_edbone;
		if (eBone->temp)
			arm->act_edbone = eBone->temp;
	}

	/*	Deselect the old bones and select the new ones */
	for (curBone = arm->edbo->first; curBone && curBone != firstDup; curBone = curBone->next) {
		if (EBONE_VISIBLE(arm, curBone))
			curBone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
	}

	ED_armature_validate_active(arm);

	WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, obedit);
	
	return OPERATOR_FINISHED;
}


void ARMATURE_OT_duplicate(wmOperatorType *ot)
{
	/* identifiers */
	ot->name = "Duplicate Selected Bone(s)";
	ot->idname = "ARMATURE_OT_duplicate";
	ot->description = "Make copies of the selected bones within the same armature";
	
	/* api callbacks */
	ot->exec = armature_duplicate_selected_exec;
	ot->poll = ED_operator_editarmature;
	
	/* flags */
	ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}


/* *************** END Adding stuff in editmode *************** */
/* ************** Add/Remove stuff in editmode **************** */

/* temporary data-structure for merge/fill bones */
typedef struct EditBonePoint {
	struct EditBonePoint *next, *prev;
	
	EditBone *head_owner;       /* EditBone which uses this point as a 'head' point */
	EditBone *tail_owner;       /* EditBone which uses this point as a 'tail' point */
	
	float vec[3];               /* the actual location of the point in local/EditMode space */
} EditBonePoint;

/* find chain-tips (i.e. bones without children) */
static void chains_find_tips(ListBase *edbo, ListBase *list)
{
	EditBone *curBone, *ebo;
	LinkData *ld;
	
	/* note: this is potentially very slow ... there's got to be a better way */
	for (curBone = edbo->first; curBone; curBone = curBone->next) {
		short stop = 0;
		
		/* is this bone contained within any existing chain? (skip if so) */
		for (ld = list->first; ld; ld = ld->next) {
			for (ebo = ld->data; ebo; ebo = ebo->parent) {
				if (ebo == curBone) {
					stop = 1;
					break;
				}
			}
			
			if (stop) break;
		}
		/* skip current bone if it is part of an existing chain */
		if (stop) continue;
		
		/* is any existing chain part of the chain formed by this bone? */
		stop = 0;
		for (ebo = curBone->parent; ebo; ebo = ebo->parent) {
			for (ld = list->first; ld; ld = ld->next) {
				if (ld->data == ebo) {
					ld->data = curBone;
					stop = 1;
					break;
				}
			}
			
			if (stop) break;
		}
		/* current bone has already been added to a chain? */
		if (stop) continue;
		
		/* add current bone to a new chain */
		ld = MEM_callocN(sizeof(LinkData), "BoneChain");
		ld->data = curBone;
		BLI_addtail(list, ld);
	}
}

/* --------------------- */

static void fill_add_joint(EditBone *ebo, short eb_tail, ListBase *points)
{
	EditBonePoint *ebp;
	float vec[3];
	short found = 0;
	
	if (eb_tail) {
		copy_v3_v3(vec, ebo->tail);
	}
	else {
		copy_v3_v3(vec, ebo->head);
	}
	
	for (ebp = points->first; ebp; ebp = ebp->next) {
		if (equals_v3v3(ebp->vec, vec)) {
			if (eb_tail) {
				if ((ebp->head_owner) && (ebp->head_owner->parent == ebo)) {
					/* so this bone's tail owner is this bone */
					ebp->tail_owner = ebo;
					found = 1;
					break;
				}
			}
			else {
				if ((ebp->tail_owner) && (ebo->parent == ebp->tail_owner)) {
					/* so this bone's head owner is this bone */
					ebp->head_owner = ebo;
					found = 1;
					break;
				}
			}
		}
	}
	
	/* allocate a new point if no existing point was related */
	if (found == 0) {
		ebp = MEM_callocN(sizeof(EditBonePoint), "EditBonePoint");
		
		if (eb_tail) {
			copy_v3_v3(ebp->vec, ebo->tail);
			ebp->tail_owner = ebo;
		}
		else {
			copy_v3_v3(ebp->vec, ebo->head);
			ebp->head_owner = ebo;
		}
		
		BLI_addtail(points, ebp);
	}
}

/* bone adding between selected joints */
static int armature_fill_bones_exec(bContext *C, wmOperator *op)
{
	Object *obedit = CTX_data_edit_object(C);
	bArmature *arm = (obedit) ? obedit->data : NULL;
	Scene *scene = CTX_data_scene(C);
	View3D *v3d = CTX_wm_view3d(C);
	ListBase points = {NULL, NULL};
	int count;

	/* sanity checks */
	if (ELEM(NULL, obedit, arm))
		return OPERATOR_CANCELLED;

	/* loop over all bones, and only consider if visible */
	CTX_DATA_BEGIN(C, EditBone *, ebone, visible_bones)
	{
		if (!(ebone->flag & BONE_CONNECTED) && (ebone->flag & BONE_ROOTSEL))
			fill_add_joint(ebone, 0, &points);
		if (ebone->flag & BONE_TIPSEL) 
			fill_add_joint(ebone, 1, &points);
	}
	CTX_DATA_END;
	
	/* the number of joints determines how we fill:
	 *  1) between joint and cursor (joint=head, cursor=tail)
	 *  2) between the two joints (order is dependent on active-bone/hierachy)
	 *  3+) error (a smarter method involving finding chains needs to be worked out
	 */
	count = BLI_countlist(&points);
	
	if (count == 0) {
		BKE_report(op->reports, RPT_ERROR, "No joints selected");
		return OPERATOR_CANCELLED;
	}
	else if (count == 1) {
		EditBonePoint *ebp;
		float curs[3];
		
		/* Get Points - selected joint */
		ebp = (EditBonePoint *)points.first;
		
		/* Get points - cursor (tail) */
		invert_m4_m4(obedit->imat, obedit->obmat);
		mul_v3_m4v3(curs, obedit->imat, give_cursor(scene, v3d));
		
		/* Create a bone */
		/* newbone = */ add_points_bone(obedit, ebp->vec, curs);
	}
	else if (count == 2) {
		EditBonePoint *ebp, *ebp2;
		float head[3], tail[3];
		short headtail = 0;
		
		/* check that the points don't belong to the same bone */
		ebp = (EditBonePoint *)points.first;
		ebp2 = ebp->next;
		
		if ((ebp->head_owner == ebp2->tail_owner) && (ebp->head_owner != NULL)) {
			BKE_report(op->reports, RPT_ERROR, "Same bone selected...");
			BLI_freelistN(&points);
			return OPERATOR_CANCELLED;
		}
		if ((ebp->tail_owner == ebp2->head_owner) && (ebp->tail_owner != NULL)) {
			BKE_report(op->reports, RPT_ERROR, "Same bone selected...");
			BLI_freelistN(&points);
			return OPERATOR_CANCELLED;
		}
		
		/* find which one should be the 'head' */
		if ((ebp->head_owner && ebp2->head_owner) || (ebp->tail_owner && ebp2->tail_owner)) {
			/* rule: whichever one is closer to 3d-cursor */
			float curs[3];
			float vecA[3], vecB[3];
			float distA, distB;
			
			/* get cursor location */
			invert_m4_m4(obedit->imat, obedit->obmat);
			mul_v3_m4v3(curs, obedit->imat, give_cursor(scene, v3d));
			
			/* get distances */
			sub_v3_v3v3(vecA, ebp->vec, curs);
			sub_v3_v3v3(vecB, ebp2->vec, curs);
			distA = len_v3(vecA);
			distB = len_v3(vecB);
			
			/* compare distances - closer one therefore acts as direction for bone to go */
			headtail = (distA < distB) ? 2 : 1;
		}
		else if (ebp->head_owner) {
			headtail = 1;
		}
		else if (ebp2->head_owner) {
			headtail = 2;
		}
		
		/* assign head/tail combinations */
		if (headtail == 2) {
			copy_v3_v3(head, ebp->vec);
			copy_v3_v3(tail, ebp2->vec);
		}
		else if (headtail == 1) {
			copy_v3_v3(head, ebp2->vec);
			copy_v3_v3(tail, ebp->vec);
		}
		
		/* add new bone and parent it to the appropriate end */
		if (headtail) {
			EditBone *newbone = add_points_bone(obedit, head, tail);
			
			/* do parenting (will need to set connected flag too) */
			if (headtail == 2) {
				/* ebp tail or head - tail gets priority */
				if (ebp->tail_owner)
					newbone->parent = ebp->tail_owner;
				else
					newbone->parent = ebp->head_owner;
			}
			else {
				/* ebp2 tail or head - tail gets priority */
				if (ebp2->tail_owner)
					newbone->parent = ebp2->tail_owner;
				else
					newbone->parent = ebp2->head_owner;
			}
			
			newbone->flag |= BONE_CONNECTED;
		}
	}
	else {
		/* FIXME.. figure out a method for multiple bones */
		BKE_reportf(op->reports, RPT_ERROR, "Too many points selected: %d", count);
		BLI_freelistN(&points);
		return OPERATOR_CANCELLED;
	}
	
	/* updates */
	WM_event_add_notifier(C, NC_OBJECT | ND_POSE, obedit);
	
	/* free points */
	BLI_freelistN(&points);
	
	return OPERATOR_FINISHED;
}

void ARMATURE_OT_fill(wmOperatorType *ot)
{
	/* identifiers */
	ot->name = "Fill Between Joints";
	ot->idname = "ARMATURE_OT_fill";
	ot->description = "Add bone between selected joint(s) and/or 3D-Cursor";
	
	/* callbacks */
	ot->exec = armature_fill_bones_exec;
	ot->poll = ED_operator_editarmature;
	
	/* flags */
	ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}

/* --------------------- */

/* this function merges between two bones, removes them and those in-between, 
 * and adjusts the parent relationships for those in-between
 */
static void bones_merge(Object *obedit, EditBone *start, EditBone *end, EditBone *endchild, ListBase *chains)
{
	bArmature *arm = obedit->data;
	EditBone *ebo, *ebone, *newbone;
	LinkData *chain;
	float head[3], tail[3];
	
	/* check if same bone */
	if (start == end) {
		if (G.debug & G_DEBUG) {
			printf("Error: same bone!\n");
			printf("\tstart = %s, end = %s\n", start->name, end->name);
		}
	}
	
	/* step 1: add a new bone
	 *	- head = head/tail of start (default head)
	 *	- tail = head/tail of end (default tail)
	 *	- parent = parent of start
	 */
	if ((start->flag & BONE_TIPSEL) && (start->flag & BONE_SELECTED) == 0) {
		copy_v3_v3(head, start->tail);
	}
	else {
		copy_v3_v3(head, start->head);
	}
	if ((end->flag & BONE_ROOTSEL) && (end->flag & BONE_SELECTED) == 0) {
		copy_v3_v3(tail, end->head);
	}
	else {
		copy_v3_v3(tail, end->tail);
	}
	newbone = add_points_bone(obedit, head, tail);
	newbone->parent = start->parent;

	/* TODO, copy more things to the new bone */
	newbone->flag = start->flag & (BONE_HINGE | BONE_NO_DEFORM | BONE_NO_SCALE |
	                               BONE_NO_CYCLICOFFSET | BONE_NO_LOCAL_LOCATION | BONE_DONE);
	
	/* step 2a: reparent any side chains which may be parented to any bone in the chain of bones to merge 
	 *	- potentially several tips for side chains leading to some tree exist...
	 */
	for (chain = chains->first; chain; chain = chain->next) {
		/* traverse down chain until we hit the bottom or if we run into the tip of the chain of bones we're 
		 * merging (need to stop in this case to avoid corrupting this chain too!) 
		 */
		for (ebone = chain->data; (ebone) && (ebone != end); ebone = ebone->parent) {
			short found = 0;
			
			/* check if this bone is parented to one in the merging chain
			 * ! WATCHIT: must only go check until end of checking chain
			 */
			for (ebo = end; (ebo) && (ebo != start->parent); ebo = ebo->parent) {
				/* side-chain found? --> remap parent to new bone, then we're done with this chain :) */
				if (ebone->parent == ebo) {
					ebone->parent = newbone;
					found = 1;
					break;
				}
			}
			
			/* carry on to the next tip now  */
			if (found) 
				break;
		}
	}
	
	/* step 2b: parent child of end to newbone (child from this chain) */
	if (endchild)
		endchild->parent = newbone;
	
	/* step 3: delete all bones between and including start and end */
	for (ebo = end; ebo; ebo = ebone) {
		ebone = (ebo == start) ? (NULL) : (ebo->parent);
		bone_free(arm, ebo);
	}
	
	newbone->flag |= (BONE_ROOTSEL | BONE_TIPSEL | BONE_SELECTED);
	ED_armature_sync_selection(arm->edbo);
}


static int armature_merge_exec(bContext *C, wmOperator *op)
{
	Object *obedit = CTX_data_edit_object(C);
	bArmature *arm = (obedit) ? obedit->data : NULL;
	short type = RNA_enum_get(op->ptr, "type");
	
	/* sanity checks */
	if (ELEM(NULL, obedit, arm))
		return OPERATOR_CANCELLED;
	
	/* for now, there's only really one type of merging that's performed... */
	if (type == 1) {
		/* go down chains, merging bones */
		ListBase chains = {NULL, NULL};
		LinkData *chain, *nchain;
		EditBone *ebo;
		
		armature_tag_select_mirrored(arm);
		
		/* get chains (ends on chains) */
		chains_find_tips(arm->edbo, &chains);
		if (chains.first == NULL) return OPERATOR_CANCELLED;
		
		/* each 'chain' is the last bone in the chain (with no children) */
		for (chain = chains.first; chain; chain = nchain) {
			EditBone *bstart = NULL, *bend = NULL;
			EditBone *bchild = NULL, *child = NULL;
			
			/* temporarily remove chain from list of chains */
			nchain = chain->next;
			BLI_remlink(&chains, chain);
			
			/* only consider bones that are visible and selected */
			for (ebo = chain->data; ebo; child = ebo, ebo = ebo->parent) {
				/* check if visible + selected */
				if (EBONE_VISIBLE(arm, ebo) &&
				    ((ebo->flag & BONE_CONNECTED) || (ebo->parent == NULL)) &&
				    (ebo->flag & BONE_SELECTED) )
				{
					/* set either end or start (end gets priority, unless it is already set) */
					if (bend == NULL) {
						bend = ebo;
						bchild = child;
					}
					else 
						bstart = ebo;
				}
				else {
					/* chain is broken... merge any continous segments then clear */
					if (bstart && bend)
						bones_merge(obedit, bstart, bend, bchild, &chains);
					
					bstart = NULL;
					bend = NULL;
					bchild = NULL;
				}
			}
			
			/* merge from bstart to bend if something not merged */
			if (bstart && bend)
				bones_merge(obedit, bstart, bend, bchild, &chains);
			
			/* put back link */
			BLI_insertlinkbefore(&chains, nchain, chain);
		}
		
		armature_tag_unselect(arm);
		
		BLI_freelistN(&chains);
	}
	
	/* updates */
	ED_armature_sync_selection(arm->edbo);
	WM_event_add_notifier(C, NC_OBJECT | ND_POSE, obedit);
	
	return OPERATOR_FINISHED;
}

void ARMATURE_OT_merge(wmOperatorType *ot)
{
	static EnumPropertyItem merge_types[] = {
		{1, "WITHIN_CHAIN", 0, "Within Chains", ""},
		{0, NULL, 0, NULL, NULL}
	};

	/* identifiers */
	ot->name = "Merge Bones";
	ot->idname = "ARMATURE_OT_merge";
	ot->description = "Merge continuous chains of selected bones";
	
	/* callbacks */
	ot->invoke = WM_menu_invoke;
	ot->exec = armature_merge_exec;
	ot->poll = ED_operator_editarmature;
	
	/* flags */
	ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
	
	/* properties */
	ot->prop = RNA_def_enum(ot->srna, "type", merge_types, 0, "Type", "");
}

/* ************** END Add/Remove stuff in editmode ************ */
/* *************** Tools in editmode *********** */

static int armature_hide_exec(bContext *C, wmOperator *op)
{
	Object *obedit = CTX_data_edit_object(C);
	bArmature *arm = obedit->data;
	EditBone *ebone;
	const int invert = RNA_boolean_get(op->ptr, "unselected") ? BONE_SELECTED : 0;

	/* cancel if nothing selected */
	if (CTX_DATA_COUNT(C, selected_bones) == 0)
		return OPERATOR_CANCELLED;

	for (ebone = arm->edbo->first; ebone; ebone = ebone->next) {
		if (EBONE_VISIBLE(arm, ebone)) {
			if ((ebone->flag & BONE_SELECTED) != invert) {
				ebone->flag &= ~(BONE_TIPSEL | BONE_SELECTED | BONE_ROOTSEL);
				ebone->flag |= BONE_HIDDEN_A;
			}
		}
	}
	ED_armature_validate_active(arm);
	ED_armature_sync_selection(arm->edbo);

	WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, obedit);

	return OPERATOR_FINISHED;
}

void ARMATURE_OT_hide(wmOperatorType *ot)
{
	/* identifiers */
	ot->name = "Hide Selected Bones";
	ot->idname = "ARMATURE_OT_hide";
	ot->description = "Tag selected bones to not be visible in Edit Mode";
	
	/* api callbacks */
	ot->exec = armature_hide_exec;
	ot->poll = ED_operator_editarmature;
	
	/* flags */
	ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;

	/* props */
	RNA_def_boolean(ot->srna, "unselected", 0, "Unselected", "Hide unselected rather than selected");
}

static int armature_reveal_exec(bContext *C, wmOperator *UNUSED(op))
{
	Object *obedit = CTX_data_edit_object(C);
	bArmature *arm = obedit->data;
	EditBone *ebone;
	
	for (ebone = arm->edbo->first; ebone; ebone = ebone->next) {
		if (arm->layer & ebone->layer) {
			if (ebone->flag & BONE_HIDDEN_A) {
				ebone->flag |= (BONE_TIPSEL | BONE_SELECTED | BONE_ROOTSEL);
				ebone->flag &= ~BONE_HIDDEN_A;
			}
		}
	}
	ED_armature_validate_active(arm);
	ED_armature_sync_selection(arm->edbo);

	WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, obedit);

	return OPERATOR_FINISHED;
}

void ARMATURE_OT_reveal(wmOperatorType *ot)
{
	/* identifiers */
	ot->name = "Reveal Bones";
	ot->idname = "ARMATURE_OT_reveal";
	ot->description = "Unhide all bones that have been tagged to be hidden in Edit Mode";
	
	/* api callbacks */
	ot->exec = armature_reveal_exec;
	ot->poll = ED_operator_editarmature;
	
	/* flags */
	ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;

}
#if 0 // remove this?
static void hide_selected_armature_bones(Scene *scene)
{
	Object *obedit = scene->obedit; // XXX get from context
	bArmature *arm = obedit->data;
	EditBone *ebone;
	
	for (ebone = arm->edbo->first; ebone; ebone = ebone->next) {
		if (EBONE_VISIBLE(arm, ebone)) {
			if (ebone->flag & BONE_SELECTED) {
				ebone->flag &= ~(BONE_TIPSEL | BONE_SELECTED | BONE_ROOTSEL);
				ebone->flag |= BONE_HIDDEN_A;
			}
		}
	}
	ED_armature_validate_active(arm);
	ED_armature_sync_selection(arm->edbo);
}

static void hide_unselected_armature_bones(Scene *scene)
{
	Object *obedit = scene->obedit; // XXX get from context
	bArmature *arm = obedit->data;
	EditBone *ebone;
	
	for (ebone = arm->edbo->first; ebone; ebone = ebone->next) {
		bArmature *arm = obedit->data;
		if (EBONE_VISIBLE(arm, ebone)) {
			if (ebone->flag & (BONE_TIPSEL | BONE_SELECTED | BONE_ROOTSEL)) {
				/* pass */
			}
			else {
				ebone->flag |= BONE_HIDDEN_A;
			}
		}
	}

	ED_armature_validate_active(arm);
	ED_armature_sync_selection(arm->edbo);
}

void show_all_armature_bones(Scene *scene)
{
	Object *obedit = scene->obedit; // XXX get from context
	bArmature *arm = obedit->data;
	EditBone *ebone;
	
	for (ebone = arm->edbo->first; ebone; ebone = ebone->next) {
		if (arm->layer & ebone->layer) {
			if (ebone->flag & BONE_HIDDEN_A) {
				ebone->flag |= (BONE_TIPSEL | BONE_SELECTED | BONE_ROOTSEL);
				ebone->flag &= ~BONE_HIDDEN_A;
			}
		}
	}
	ED_armature_validate_active(arm);
	ED_armature_sync_selection(arm->edbo);
}
#endif

/* previously extrude_armature */
/* context; editmode armature */
/* if forked && mirror-edit: makes two bones with flipped names */
static int armature_extrude_exec(bContext *C, wmOperator *op)
{
	Object *obedit;
	bArmature *arm;
	EditBone *newbone, *ebone, *flipbone, *first = NULL;
	int a, totbone = 0, do_extrude;
	int forked = RNA_boolean_get(op->ptr, "forked");

	obedit = CTX_data_edit_object(C);
	arm = obedit->data;

	/* since we allow root extrude too, we have to make sure selection is OK */
	for (ebone = arm->edbo->first; ebone; ebone = ebone->next) {
		if (EBONE_VISIBLE(arm, ebone)) {
			if (ebone->flag & BONE_ROOTSEL) {
				if (ebone->parent && (ebone->flag & BONE_CONNECTED)) {
					if (ebone->parent->flag & BONE_TIPSEL)
						ebone->flag &= ~BONE_ROOTSEL;
				}
			}
		}
	}
	
	/* Duplicate the necessary bones */
	for (ebone = arm->edbo->first; ((ebone) && (ebone != first)); ebone = ebone->next) {
		if (EBONE_VISIBLE(arm, ebone)) {
			/* we extrude per definition the tip */
			do_extrude = FALSE;
			if (ebone->flag & (BONE_TIPSEL | BONE_SELECTED)) {
				do_extrude = TRUE;
			}
			else if (ebone->flag & BONE_ROOTSEL) {
				/* but, a bone with parent deselected we do the root... */
				if (ebone->parent && (ebone->parent->flag & BONE_TIPSEL)) {
					/* pass */
				}
				else {
					do_extrude = 2;
				}
			}
			
			if (do_extrude) {
				/* we re-use code for mirror editing... */
				flipbone = NULL;
				if (arm->flag & ARM_MIRROR_EDIT) {
					flipbone = ED_armature_bone_get_mirrored(arm->edbo, ebone);
					if (flipbone) {
						forked = 0;  // we extrude 2 different bones
						if (flipbone->flag & (BONE_TIPSEL | BONE_ROOTSEL | BONE_SELECTED))
							/* don't want this bone to be selected... */
							flipbone->flag &= ~(BONE_TIPSEL | BONE_SELECTED | BONE_ROOTSEL);
					}
					if ((flipbone == NULL) && (forked))
						flipbone = ebone;
				}
				
				for (a = 0; a < 2; a++) {
					if (a == 1) {
						if (flipbone == NULL)
							break;
						else {
							SWAP(EditBone *, flipbone, ebone);
						}
					}
					
					totbone++;
					newbone = MEM_callocN(sizeof(EditBone), "extrudebone");
					
					if (do_extrude == TRUE) {
						copy_v3_v3(newbone->head, ebone->tail);
						copy_v3_v3(newbone->tail, newbone->head);
						newbone->parent = ebone;
						
						newbone->flag = ebone->flag & (BONE_TIPSEL | BONE_RELATIVE_PARENTING);  // copies it, in case mirrored bone
						
						if (newbone->parent) newbone->flag |= BONE_CONNECTED;
					}
					else {
						copy_v3_v3(newbone->head, ebone->head);
						copy_v3_v3(newbone->tail, ebone->head);
						newbone->parent = ebone->parent;
						
						newbone->flag = BONE_TIPSEL;
						
						if (newbone->parent && (ebone->flag & BONE_CONNECTED)) {
							newbone->flag |= BONE_CONNECTED;
						}
					}
					
					newbone->weight = ebone->weight;
					newbone->dist = ebone->dist;
					newbone->xwidth = ebone->xwidth;
					newbone->zwidth = ebone->zwidth;
					newbone->ease1 = ebone->ease1;
					newbone->ease2 = ebone->ease2;
					newbone->rad_head = ebone->rad_tail; // don't copy entire bone...
					newbone->rad_tail = ebone->rad_tail;
					newbone->segments = 1;
					newbone->layer = ebone->layer;

					BLI_strncpy(newbone->name, ebone->name, sizeof(newbone->name));

					if (flipbone && forked) {   // only set if mirror edit
						if (strlen(newbone->name) < 30) {
							if (a == 0) strcat(newbone->name, "_L");
							else strcat(newbone->name, "_R");
						}
					}
					unique_editbone_name(arm->edbo, newbone->name, NULL);
					
					/* Add the new bone to the list */
					BLI_addtail(arm->edbo, newbone);
					if (!first)
						first = newbone;
					
					/* restore ebone if we were flipping */
					if (a == 1 && flipbone)
						SWAP(EditBone *, flipbone, ebone);
				}
			}
			
			/* Deselect the old bone */
			ebone->flag &= ~(BONE_TIPSEL | BONE_SELECTED | BONE_ROOTSEL);
		}
	}
	/* if only one bone, make this one active */
	if (totbone == 1 && first) arm->act_edbone = first;

	if (totbone == 0) return OPERATOR_CANCELLED;

	/* Transform the endpoints */
	ED_armature_sync_selection(arm->edbo);

	WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, obedit);

	return OPERATOR_FINISHED;
}

void ARMATURE_OT_extrude(wmOperatorType *ot)
{
	/* identifiers */
	ot->name = "Extrude";
	ot->idname = "ARMATURE_OT_extrude";
	ot->description = "Create new bones from the selected joints";
	
	/* api callbacks */
	ot->exec = armature_extrude_exec;
	ot->poll = ED_operator_editarmature;
	
	/* flags */
	ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
	
	/* props */
	RNA_def_boolean(ot->srna, "forked", 0, "Forked", "");
}
/* ********************** Bone Add ********************/

/*op makes a new bone and returns it with its tip selected */

static int armature_bone_primitive_add_exec(bContext *C, wmOperator *op) 
{
	RegionView3D *rv3d = CTX_wm_region_view3d(C);
	Object *obedit = CTX_data_edit_object(C);
	EditBone *bone;
	float obmat[3][3], curs[3], viewmat[3][3], totmat[3][3], imat[3][3];
	char name[MAXBONENAME];
	
	RNA_string_get(op->ptr, "name", name);
	
	copy_v3_v3(curs, give_cursor(CTX_data_scene(C), CTX_wm_view3d(C)));

	/* Get inverse point for head and orientation for tail */
	invert_m4_m4(obedit->imat, obedit->obmat);
	mul_m4_v3(obedit->imat, curs);

	if (rv3d && (U.flag & USER_ADD_VIEWALIGNED))
		copy_m3_m4(obmat, rv3d->viewmat);
	else unit_m3(obmat);
	
	copy_m3_m4(viewmat, obedit->obmat);
	mul_m3_m3m3(totmat, obmat, viewmat);
	invert_m3_m3(imat, totmat);
	
	ED_armature_deselect_all(obedit, 0);
	
	/*	Create a bone	*/
	bone = ED_armature_edit_bone_add(obedit->data, name);

	copy_v3_v3(bone->head, curs);
	
	if (rv3d && (U.flag & USER_ADD_VIEWALIGNED))
		add_v3_v3v3(bone->tail, bone->head, imat[1]);   // bone with unit length 1
	else
		add_v3_v3v3(bone->tail, bone->head, imat[2]);   // bone with unit length 1, pointing up Z

	/* note, notifier might evolve */
	WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, obedit);

	return OPERATOR_FINISHED;
}

void ARMATURE_OT_bone_primitive_add(wmOperatorType *ot)
{
	/* identifiers */
	ot->name = "Add Bone";
	ot->idname = "ARMATURE_OT_bone_primitive_add";
	ot->description = "Add a new bone located at the 3D-Cursor";
	
	/* api callbacks */
	ot->exec = armature_bone_primitive_add_exec;
	ot->poll = ED_operator_editarmature;
	
	/* flags */
	ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
	
	RNA_def_string(ot->srna, "name", "Bone", MAXBONENAME, "Name", "Name of the newly created bone");
	
}


/* ----------- */

/* Subdivide Operators:
 * This group of operators all use the same 'exec' callback, but they are called
 * through several different operators - a combined menu (which just calls the exec in the 
 * appropriate ways), and two separate ones.
 */

static int armature_subdivide_exec(bContext *C, wmOperator *op)
{
	Object *obedit = CTX_data_edit_object(C);
	bArmature *arm = obedit->data;
	EditBone *newbone, *tbone;
	int cuts, i;
	
	/* there may not be a number_cuts property defined (for 'simple' subdivide) */
	cuts = RNA_int_get(op->ptr, "number_cuts");
	
	/* loop over all editable bones */
	// XXX the old code did this in reverse order though!
	CTX_DATA_BEGIN(C, EditBone *, ebone, selected_editable_bones)
	{
		for (i = cuts + 1; i > 1; i--) {
			/* compute cut ratio first */
			float cutratio = 1.0f / (float)i;
			float cutratioI = 1.0f - cutratio;
			
			float val1[3];
			float val2[3];
			float val3[3];
			
			newbone = MEM_mallocN(sizeof(EditBone), "ebone subdiv");
			*newbone = *ebone;
			BLI_addtail(arm->edbo, newbone);
			
			/* calculate location of newbone->head */
			copy_v3_v3(val1, ebone->head);
			copy_v3_v3(val2, ebone->tail);
			copy_v3_v3(val3, newbone->head);
			
			val3[0] = val1[0] * cutratio + val2[0] * cutratioI;
			val3[1] = val1[1] * cutratio + val2[1] * cutratioI;
			val3[2] = val1[2] * cutratio + val2[2] * cutratioI;
			
			copy_v3_v3(newbone->head, val3);
			copy_v3_v3(newbone->tail, ebone->tail);
			copy_v3_v3(ebone->tail, newbone->head);
			
			newbone->rad_head = 0.5f * (ebone->rad_head + ebone->rad_tail);
			ebone->rad_tail = newbone->rad_head;
			
			newbone->flag |= BONE_CONNECTED;
			
			unique_editbone_name(arm->edbo, newbone->name, NULL);
			
			/* correct parent bones */
			for (tbone = arm->edbo->first; tbone; tbone = tbone->next) {
				if (tbone->parent == ebone)
					tbone->parent = newbone;
			}
			newbone->parent = ebone;
		}
	}
	CTX_DATA_END;
	
	/* note, notifier might evolve */
	WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, obedit);
	
	return OPERATOR_FINISHED;
}

void ARMATURE_OT_subdivide(wmOperatorType *ot)
{
	PropertyRNA *prop;

	/* identifiers */
	ot->name = "Subdivide Multi";
	ot->idname = "ARMATURE_OT_subdivide";
	ot->description = "Break selected bones into chains of smaller bones";
	
	/* api callbacks */
	ot->exec = armature_subdivide_exec;
	ot->poll = ED_operator_editarmature;
	
	/* flags */
	ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
	
	/* Properties */
	prop = RNA_def_int(ot->srna, "number_cuts", 1, 1, INT_MAX, "Number of Cuts", "", 1, 10);
	/* avoid re-using last var because it can cause _very_ high poly meshes and annoy users (or worse crash) */
	RNA_def_property_flag(prop, PROP_SKIP_SAVE);
}

/* ----------- */

/* Switch Direction operator:
 * Currently, this does not use context loops, as context loops do not make it
 * easy to retrieve any hierarchical/chain relationships which are necessary for
 * this to be done easily.
 */
 
/* helper to clear BONE_TRANSFORM flags */
static void armature_clear_swap_done_flags(bArmature *arm)
{
	EditBone *ebone;
	
	for (ebone = arm->edbo->first; ebone; ebone = ebone->next) {
		ebone->flag &= ~BONE_TRANSFORM;
	}
}

static int armature_switch_direction_exec(bContext *C, wmOperator *UNUSED(op)) 
{
	Object *ob = CTX_data_edit_object(C);
	bArmature *arm = (bArmature *)ob->data;
	ListBase chains = {NULL, NULL};
	LinkData *chain;
	
	/* get chains of bones (ends on chains) */
	chains_find_tips(arm->edbo, &chains);
	if (chains.first == NULL) return OPERATOR_CANCELLED;
	
	/* ensure that mirror bones will also be operated on */
	armature_tag_select_mirrored(arm);
	
	/* clear BONE_TRANSFORM flags 
	 * - used to prevent duplicate/cancelling operations from occurring [#34123] 
	 * - BONE_DONE cannot be used here as that's already used for mirroring
	 */
	armature_clear_swap_done_flags(arm);
	
	/* loop over chains, only considering selected and visible bones */
	for (chain = chains.first; chain; chain = chain->next) {
		EditBone *ebo, *child = NULL, *parent = NULL;
		
		/* loop over bones in chain */
		for (ebo = chain->data; ebo; ebo = parent) {
			/* parent is this bone's original parent
			 *	- we store this, as the next bone that is checked is this one
			 *	  but the value of ebo->parent may change here...
			 */
			parent = ebo->parent;
			
			/* skip bone if already handled... [#34123] */
			if ((ebo->flag & BONE_TRANSFORM) == 0) {
				/* only if selected and editable */
				if (EBONE_VISIBLE(arm, ebo) && EBONE_EDITABLE(ebo)) {
					/* swap head and tail coordinates */
					SWAP(float, ebo->head[0], ebo->tail[0]);
					SWAP(float, ebo->head[1], ebo->tail[1]);
					SWAP(float, ebo->head[2], ebo->tail[2]);
					
					/* do parent swapping:
					 *	- use 'child' as new parent
					 *	- connected flag is only set if points are coincidental
					 */
					ebo->parent = child;
					if ((child) && equals_v3v3(ebo->head, child->tail))
						ebo->flag |= BONE_CONNECTED;
					else
						ebo->flag &= ~BONE_CONNECTED;
					
					/* get next bones 
					 *	- child will become the new parent of next bone
					 */
					child = ebo;
				}
				else {
					/* not swapping this bone, however, if its 'parent' got swapped, unparent us from it 
					 * as it will be facing in opposite direction
					 */
					if ((parent) && (EBONE_VISIBLE(arm, parent) && EBONE_EDITABLE(parent))) {
						ebo->parent = NULL;
						ebo->flag &= ~BONE_CONNECTED;
					}
					
					/* get next bones
					 *	- child will become new parent of next bone (not swapping occurred, 
					 *	  so set to NULL to prevent infinite-loop)
					 */
					child = NULL;
				}
				
				/* tag as done (to prevent double-swaps) */
				ebo->flag |= BONE_TRANSFORM;
			}
		}
	}
	
	/* free chains */
	BLI_freelistN(&chains);
	
	/* clear temp flags */
	armature_clear_swap_done_flags(arm);
	armature_tag_unselect(arm);
	
	/* note, notifier might evolve */
	WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob);
	
	return OPERATOR_FINISHED;
}

void ARMATURE_OT_switch_direction(wmOperatorType *ot)
{
	/* identifiers */
	ot->name = "Switch Direction";
	ot->idname = "ARMATURE_OT_switch_direction";
	ot->description = "Change the direction that a chain of bones points in (head <-> tail swap)";
	
	/* api callbacks */
	ot->exec = armature_switch_direction_exec;
	ot->poll = ED_operator_editarmature;
	
	/* flags */
	ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
/* ***************** Parenting *********************** */

/* armature parenting options */
#define ARM_PAR_CONNECT 1
#define ARM_PAR_OFFSET  2

/* check for null, before calling! */
static void bone_connect_to_existing_parent(EditBone *bone)
{
	bone->flag |= BONE_CONNECTED;
	copy_v3_v3(bone->head, bone->parent->tail);
	bone->rad_head = bone->parent->rad_tail;
}

static void bone_connect_to_new_parent(ListBase *edbo, EditBone *selbone, EditBone *actbone, short mode)
{
	EditBone *ebone;
	float offset[3];
	
	if ((selbone->parent) && (selbone->flag & BONE_CONNECTED))
		selbone->parent->flag &= ~(BONE_TIPSEL);
	
	/* make actbone the parent of selbone */
	selbone->parent = actbone;
	
	/* in actbone tree we cannot have a loop */
	for (ebone = actbone->parent; ebone; ebone = ebone->parent) {
		if (ebone->parent == selbone) {
			ebone->parent = NULL;
			ebone->flag &= ~BONE_CONNECTED;
		}
	}
	
	if (mode == ARM_PAR_CONNECT) {
		/* Connected: Child bones will be moved to the parent tip */
		selbone->flag |= BONE_CONNECTED;
		sub_v3_v3v3(offset, actbone->tail, selbone->head);
		
		copy_v3_v3(selbone->head, actbone->tail);
		selbone->rad_head = actbone->rad_tail;
		
		add_v3_v3(selbone->tail, offset);
		
		/* offset for all its children */
		for (ebone = edbo->first; ebone; ebone = ebone->next) {
			EditBone *par;
			
			for (par = ebone->parent; par; par = par->parent) {
				if (par == selbone) {
					add_v3_v3(ebone->head, offset);
					add_v3_v3(ebone->tail, offset);
					break;
				}
			}
		}
	}
	else {
		/* Offset: Child bones will retain their distance from the parent tip */
		selbone->flag &= ~BONE_CONNECTED;
	}
}

static EnumPropertyItem prop_editarm_make_parent_types[] = {
	{ARM_PAR_CONNECT, "CONNECTED", 0, "Connected", ""},
	{ARM_PAR_OFFSET, "OFFSET", 0, "Keep Offset", ""},
	{0, NULL, 0, NULL, NULL}
};

static int armature_parent_set_exec(bContext *C, wmOperator *op) 
{
	Object *ob = CTX_data_edit_object(C);
	bArmature *arm = (bArmature *)ob->data;
	EditBone *actbone = CTX_data_active_bone(C);
	EditBone *actmirb = NULL;
	short val = RNA_enum_get(op->ptr, "type");
	
	/* there must be an active bone */
	if (actbone == NULL) {
		BKE_report(op->reports, RPT_ERROR, "Operation requires an active bone");
		return OPERATOR_CANCELLED;
	}
	else if (arm->flag & ARM_MIRROR_EDIT) {
		/* For X-Axis Mirror Editing option, we may need a mirror copy of actbone
		 * - if there's a mirrored copy of selbone, try to find a mirrored copy of actbone 
		 *   (i.e.  selbone="child.L" and actbone="parent.L", find "child.R" and "parent.R").
		 * This is useful for arm-chains, for example parenting lower arm to upper arm
		 * - if there's no mirrored copy of actbone (i.e. actbone = "parent.C" or "parent")
		 *   then just use actbone. Useful when doing upper arm to spine.
		 */
		actmirb = ED_armature_bone_get_mirrored(arm->edbo, actbone);
		if (actmirb == NULL) 
			actmirb = actbone;
	}
	
	/* if there is only 1 selected bone, we assume that that is the active bone, 
	 * since a user will need to have clicked on a bone (thus selecting it) to make it active
	 */
	if (CTX_DATA_COUNT(C, selected_editable_bones) <= 1) {
		/* When only the active bone is selected, and it has a parent,
		 * connect it to the parent, as that is the only possible outcome. 
		 */
		if (actbone->parent) {
			bone_connect_to_existing_parent(actbone);
			
			if ((arm->flag & ARM_MIRROR_EDIT) && (actmirb->parent))
				bone_connect_to_existing_parent(actmirb);
		}
	}
	else {
		/* Parent 'selected' bones to the active one
		 * - the context iterator contains both selected bones and their mirrored copies,
		 *   so we assume that unselected bones are mirrored copies of some selected bone
		 * - since the active one (and/or its mirror) will also be selected, we also need 
		 *  to check that we are not trying to operate on them, since such an operation
		 *	would cause errors
		 */
		
		/* parent selected bones to the active one */
		CTX_DATA_BEGIN(C, EditBone *, ebone, selected_editable_bones)
		{
			if (ELEM(ebone, actbone, actmirb) == 0) {
				if (ebone->flag & BONE_SELECTED) 
					bone_connect_to_new_parent(arm->edbo, ebone, actbone, val);
				else
					bone_connect_to_new_parent(arm->edbo, ebone, actmirb, val);
			}
		}
		CTX_DATA_END;
	}
	

	/* note, notifier might evolve */
	WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob);
	
	return OPERATOR_FINISHED;
}

static int armature_parent_set_invoke(bContext *C, wmOperator *UNUSED(op), wmEvent *UNUSED(event))
{
	EditBone *actbone = CTX_data_active_bone(C);
	uiPopupMenu *pup = uiPupMenuBegin(C, "Make Parent ", ICON_NONE);
	uiLayout *layout = uiPupMenuLayout(pup);
	int allchildbones = 0;
	
	CTX_DATA_BEGIN(C, EditBone *, ebone, selected_editable_bones)
	{
		if (ebone != actbone) {
			if (ebone->parent != actbone) allchildbones = 1;
		}
	}
	CTX_DATA_END;

	uiItemEnumO(layout, "ARMATURE_OT_parent_set", NULL, 0, "type", ARM_PAR_CONNECT);
	
	/* ob becomes parent, make the associated menus */
	if (allchildbones)
		uiItemEnumO(layout, "ARMATURE_OT_parent_set", NULL, 0, "type", ARM_PAR_OFFSET);
		
	uiPupMenuEnd(C, pup);
	
	return OPERATOR_CANCELLED;
}

void ARMATURE_OT_parent_set(wmOperatorType *ot)
{
	/* identifiers */
	ot->name = "Make Parent";
	ot->idname = "ARMATURE_OT_parent_set";
	ot->description = "Set the active bone as the parent of the selected bones";
	
	/* api callbacks */
	ot->invoke = armature_parent_set_invoke;
	ot->exec = armature_parent_set_exec;
	ot->poll = ED_operator_editarmature;
	
	/* flags */
	ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
	
	RNA_def_enum(ot->srna, "type", prop_editarm_make_parent_types, 0, "ParentType", "Type of parenting");
}

static EnumPropertyItem prop_editarm_clear_parent_types[] = {
	{1, "CLEAR", 0, "Clear Parent", ""},
	{2, "DISCONNECT", 0, "Disconnect Bone", ""},
	{0, NULL, 0, NULL, NULL}
};

static void editbone_clear_parent(EditBone *ebone, int mode)
{
	if (ebone->parent) {
		/* for nice selection */
		ebone->parent->flag &= ~(BONE_TIPSEL);
	}
	
	if (mode == 1) ebone->parent = NULL;
	ebone->flag &= ~BONE_CONNECTED;
}

static int armature_parent_clear_exec(bContext *C, wmOperator *op) 
{
	Object *ob = CTX_data_edit_object(C);
	bArmature *arm = (bArmature *)ob->data;
	int val = RNA_enum_get(op->ptr, "type");
		
	CTX_DATA_BEGIN(C, EditBone *, ebone, selected_editable_bones)
	{
		editbone_clear_parent(ebone, val);
	}
	CTX_DATA_END;
	
	ED_armature_sync_selection(arm->edbo);

	/* note, notifier might evolve */
	WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob);
	
	return OPERATOR_FINISHED;
}

void ARMATURE_OT_parent_clear(wmOperatorType *ot)
{
	/* identifiers */
	ot->name = "Clear Parent";
	ot->idname = "ARMATURE_OT_parent_clear";
	ot->description = "Remove the parent-child relationship between selected bones and their parents";
	
	/* api callbacks */
	ot->invoke = WM_menu_invoke;
	ot->exec = armature_parent_clear_exec;
	ot->poll = ED_operator_editarmature;
	
	/* flags */
	ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
	
	ot->prop = RNA_def_enum(ot->srna, "type", prop_editarm_clear_parent_types, 0, "ClearType", "What way to clear parenting");
}

/* ****************  Selections  ******************/

static int armature_select_inverse_exec(bContext *C, wmOperator *UNUSED(op))
{
	/*	Set the flags */
	CTX_DATA_BEGIN(C, EditBone *, ebone, visible_bones)
	{
		/* ignore bone if selection can't change */
		if ((ebone->flag & BONE_UNSELECTABLE) == 0) {
			/* select bone */
			ebone->flag ^= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
		}
	}
	CTX_DATA_END;

	WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, NULL);
	
	return OPERATOR_FINISHED;
}

void ARMATURE_OT_select_inverse(wmOperatorType *ot)
{
	/* identifiers */
	ot->name = "Select Inverse";
	ot->idname = "ARMATURE_OT_select_inverse";
	ot->description = "Flip the selection status of bones (selected -> unselected, unselected -> selected)";
	
	/* api callbacks */
	ot->exec = armature_select_inverse_exec;
	ot->poll = ED_operator_editarmature;
	
	/* flags */
	ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
	
}
static int armature_de_select_all_exec(bContext *C, wmOperator *op)
{
	int action = RNA_enum_get(op->ptr, "action");

	if (action == SEL_TOGGLE) {
		action = SEL_SELECT;
		/* Determine if there are any selected bones
		 * And therefore whether we are selecting or deselecting */
		if (CTX_DATA_COUNT(C, selected_bones) > 0)
			action = SEL_DESELECT;
	}
	
	/*	Set the flags */
	CTX_DATA_BEGIN(C, EditBone *, ebone, visible_bones)
	{
		/* ignore bone if selection can't change */
		if ((ebone->flag & BONE_UNSELECTABLE) == 0) {
			switch (action) {
				case SEL_SELECT:
					ebone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
					if (ebone->parent)
						ebone->parent->flag |= (BONE_TIPSEL);
					break;
				case SEL_DESELECT:
					ebone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
					break;
				case SEL_INVERT:
					if (ebone->flag & BONE_SELECTED) {
						ebone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
					}
					else {
						ebone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
						if (ebone->parent)
							ebone->parent->flag |= (BONE_TIPSEL);
					}
					break;
			}
		}
	}
	CTX_DATA_END;

	WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, NULL);
	
	return OPERATOR_FINISHED;
}

void ARMATURE_OT_select_all(wmOperatorType *ot)
{
	/* identifiers */
	ot->name = "(De)select All";
	ot->idname = "ARMATURE_OT_select_all";
	ot->description = "Toggle selection status of all bones";
	
	/* api callbacks */
	ot->exec = armature_de_select_all_exec;
	ot->poll = ED_operator_editarmature;
	
	/* flags */
	ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
	
	WM_operator_properties_select_all(ot);
}

enum {
	SIMEDBONE_LENGTH = 1,
	SIMEDBONE_DIRECTION,
	SIMEDBONE_PREFIX,
	SIMEDBONE_SUFFIX,
	SIMEDBONE_LAYER
};

static EnumPropertyItem prop_similar_types[] = {
	{SIMEDBONE_LENGTH, "LENGTH", 0, "Length", ""},
	{SIMEDBONE_DIRECTION, "DIRECTION", 0, "Direction (Y axis)", ""},
	{SIMEDBONE_PREFIX, "PREFIX", 0, "Prefix", ""},
	{SIMEDBONE_SUFFIX, "SUFFIX", 0, "Suffix", ""},
	{SIMEDBONE_LAYER, "LAYER", 0, "Layer", ""},
	{0, NULL, 0, NULL, NULL}
};

/* could be used in more places */
static void ED_armature_edit_bone_select(EditBone *ebone)
{
	BLI_assert((ebone->flag & BONE_UNSELECTABLE) == 0);
	ebone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);

	if ((ebone->flag & BONE_CONNECTED) && (ebone->parent != NULL)) {
		ebone->parent->flag |= BONE_TIPSEL;
	}
}

static void select_similar_length(bArmature *arm, EditBone *ebone_act, const float thresh)
{
	EditBone *ebone;

	/* thresh is always relative to current length */
	const float len_min = ebone_act->length / (1.0f + thresh);
	const float len_max = ebone_act->length * (1.0f + thresh);

	for (ebone = arm->edbo->first; ebone; ebone = ebone->next) {
		if (EBONE_SELECTABLE(arm, ebone)) {
			if ((ebone->length >= len_min) &&
			    (ebone->length <= len_max))
			{
				ED_armature_edit_bone_select(ebone);
			}
		}
	}
}

static void select_similar_direction(bArmature *arm, EditBone *ebone_act, const float thresh)
{
	EditBone *ebone;
	float dir_act[3];
	sub_v3_v3v3(dir_act, ebone_act->head, ebone_act->tail);

	for (ebone = arm->edbo->first; ebone; ebone = ebone->next) {
		if (EBONE_SELECTABLE(arm, ebone)) {
			float dir[3];
			sub_v3_v3v3(dir, ebone->head, ebone->tail);

			if (angle_v3v3(dir_act, dir) / (float)M_PI < thresh) {
				ED_armature_edit_bone_select(ebone);
			}
		}
	}
}

static void select_similar_layer(bArmature *arm, EditBone *ebone_act)
{
	EditBone *ebone;

	for (ebone = arm->edbo->first; ebone; ebone = ebone->next) {
		if (EBONE_SELECTABLE(arm, ebone)) {
			if (ebone->layer & ebone_act->layer) {
				ED_armature_edit_bone_select(ebone);
			}
		}
	}
}

static void select_similar_prefix(bArmature *arm, EditBone *ebone_act)
{
	EditBone *ebone;

	char body_tmp[MAX_VGROUP_NAME];
	char prefix_act[MAX_VGROUP_NAME];

	BKE_deform_split_prefix(ebone_act->name, prefix_act, body_tmp);

	if (prefix_act[0] == '\0')
		return;

	/* Find matches */
	for (ebone = arm->edbo->first; ebone; ebone = ebone->next) {
		if (EBONE_SELECTABLE(arm, ebone)) {
			char prefix_other[MAX_VGROUP_NAME];
			BKE_deform_split_prefix(ebone->name, prefix_other, body_tmp);
			if (!strcmp(prefix_act, prefix_other)) {
				ED_armature_edit_bone_select(ebone);
			}
		}
	}
}

static void select_similar_suffix(bArmature *arm, EditBone *ebone_act)
{
	EditBone *ebone;

	char body_tmp[MAX_VGROUP_NAME];
	char suffix_act[MAX_VGROUP_NAME];

	BKE_deform_split_suffix(ebone_act->name, body_tmp, suffix_act);

	if (suffix_act[0] == '\0')
		return;

	/* Find matches */
	for (ebone = arm->edbo->first; ebone; ebone = ebone->next) {
		if (EBONE_SELECTABLE(arm, ebone)) {
			char suffix_other[MAX_VGROUP_NAME];
			BKE_deform_split_suffix(ebone->name, body_tmp, suffix_other);
			if (!strcmp(suffix_act, suffix_other)) {
				ED_armature_edit_bone_select(ebone);
			}
		}
	}
}

static int armature_select_similar_exec(bContext *C, wmOperator *op)
{
	Object *obedit = CTX_data_edit_object(C);
	bArmature *arm = obedit->data;
	EditBone *ebone_act = CTX_data_active_bone(C);

	/* Get props */
	int type = RNA_enum_get(op->ptr, "type");
	float thresh = RNA_float_get(op->ptr, "threshold");

	/* Check for active bone */
	if (ebone_act == NULL) {
		BKE_report(op->reports, RPT_ERROR, "Operation requires an active bone");
		return OPERATOR_CANCELLED;
	}

	switch (type) {
		case SIMEDBONE_LENGTH:
			select_similar_length(arm, ebone_act, thresh);
			break;
		case SIMEDBONE_DIRECTION:
			select_similar_direction(arm, ebone_act, thresh);
			break;
		case SIMEDBONE_PREFIX:
			select_similar_prefix(arm, ebone_act);
			break;
		case SIMEDBONE_SUFFIX:
			select_similar_suffix(arm, ebone_act);
			break;
		case SIMEDBONE_LAYER:
			select_similar_layer(arm, ebone_act);
			break;
	}

	WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, obedit);

	return OPERATOR_FINISHED;
}

void ARMATURE_OT_select_similar(wmOperatorType *ot)
{
	/* identifiers */
	ot->name = "Select Similar";
	ot->idname = "ARMATURE_OT_select_similar";

	/* callback functions */
	ot->invoke = WM_menu_invoke;
	ot->exec = armature_select_similar_exec;
	ot->poll = ED_operator_editarmature;
	ot->description = "Select similar bones by property types";

	/* flags */
	ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;

	/* properties */
	ot->prop = RNA_def_enum(ot->srna, "type", prop_similar_types, 0, "Type", "");
	RNA_def_float(ot->srna, "threshold", 0.1f, 0.0f, 1.0f, "Threshold", "", 0.0f, 1.0f);
}

/* ********************* select hierarchy operator ************** */

static int armature_select_hierarchy_exec(bContext *C, wmOperator *op)
{
	Object *obedit = CTX_data_edit_object(C);
	Object *ob;
	bArmature *arm;
	EditBone *curbone, *pabone, *chbone;
	int direction = RNA_enum_get(op->ptr, "direction");
	int add_to_sel = RNA_boolean_get(op->ptr, "extend");
	
	ob = obedit;
	arm = (bArmature *)ob->data;
	
	for (curbone = arm->edbo->first; curbone; curbone = curbone->next) {
		/* only work on bone if it is visible and its selection can change */
		if (EBONE_SELECTABLE(arm, curbone)) {
			if (curbone == arm->act_edbone) {
				if (direction == BONE_SELECT_PARENT) {
					if (curbone->parent == NULL) continue;
					else pabone = curbone->parent;
					
					if (EBONE_VISIBLE(arm, pabone)) {
						pabone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
						arm->act_edbone = pabone;
						if (pabone->parent) pabone->parent->flag |= BONE_TIPSEL;
						
						if (!add_to_sel) curbone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
						break;
					}
					
				}
				else { // BONE_SELECT_CHILD
					chbone = editbone_get_child(arm, curbone, 1);
					if (chbone == NULL) continue;
					
					if (EBONE_SELECTABLE(arm, chbone)) {
						chbone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
						arm->act_edbone = chbone;
						
						if (!add_to_sel) {
							curbone->flag &= ~(BONE_SELECTED | BONE_ROOTSEL);
							if (curbone->parent) curbone->parent->flag &= ~BONE_TIPSEL;
						}
						break;
					}
				}
			}
		}
	}
	
	ED_armature_sync_selection(arm->edbo);
	
	WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob);
	
	return OPERATOR_FINISHED;
}

void ARMATURE_OT_select_hierarchy(wmOperatorType *ot)
{
	static EnumPropertyItem direction_items[] = {
		{BONE_SELECT_PARENT, "PARENT", 0, "Select Parent", ""},
		{BONE_SELECT_CHILD, "CHILD", 0, "Select Child", ""},
		{0, NULL, 0, NULL, NULL}
	};
	
	/* identifiers */
	ot->name = "Select Hierarchy";
	ot->idname = "ARMATURE_OT_select_hierarchy";
	ot->description = "Select immediate parent/children of selected bones";
	
	/* api callbacks */
	ot->exec = armature_select_hierarchy_exec;
	ot->poll = ED_operator_editarmature;
	
	/* flags */
	ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;

	/* props */
	RNA_def_enum(ot->srna, "direction", direction_items,
	             BONE_SELECT_PARENT, "Direction", "");
	RNA_def_boolean(ot->srna, "extend", false, "Extend", "Extend the selection");
}

/* ***************** EditBone Alignment ********************* */

/* helper to fix a ebone position if its parent has moved due to alignment*/
static void fix_connected_bone(EditBone *ebone)
{
	float diff[3];
	
	if (!(ebone->parent) || !(ebone->flag & BONE_CONNECTED) || equals_v3v3(ebone->parent->tail, ebone->head))
		return;
	
	/* if the parent has moved we translate child's head and tail accordingly*/
	sub_v3_v3v3(diff, ebone->parent->tail, ebone->head);
	add_v3_v3(ebone->head, diff);
	add_v3_v3(ebone->tail, diff);
	return;
}

/* helper to recursively find chains of connected bones starting at ebone and fix their position */
static void fix_editbone_connected_children(ListBase *edbo, EditBone *ebone)
{
	EditBone *selbone;
	
	for (selbone = edbo->first; selbone; selbone = selbone->next) {
		if ((selbone->parent) && (selbone->parent == ebone) && (selbone->flag & BONE_CONNECTED)) {
			fix_connected_bone(selbone);
			fix_editbone_connected_children(edbo, selbone);
		}
	}
	return;
}			

static void bone_align_to_bone(ListBase *edbo, EditBone *selbone, EditBone *actbone)
{
	float selboneaxis[3], actboneaxis[3], length;

	sub_v3_v3v3(actboneaxis, actbone->tail, actbone->head);
	normalize_v3(actboneaxis);

	sub_v3_v3v3(selboneaxis, selbone->tail, selbone->head);
	length =  len_v3(selboneaxis);

	mul_v3_fl(actboneaxis, length);
	add_v3_v3v3(selbone->tail, selbone->head, actboneaxis);
	selbone->roll = actbone->roll;
	
	/* if the bone being aligned has connected descendants they must be moved
	 * according to their parent new position, otherwise they would be left
	 * in an inconsistent state: connected but away from the parent*/
	fix_editbone_connected_children(edbo, selbone);
	return;
}

static int armature_align_bones_exec(bContext *C, wmOperator *op) 
{
	Object *ob = CTX_data_edit_object(C);
	bArmature *arm = (bArmature *)ob->data;
	EditBone *actbone = CTX_data_active_bone(C);
	EditBone *actmirb = NULL;
	
	/* there must be an active bone */
	if (actbone == NULL) {
		BKE_report(op->reports, RPT_ERROR, "Operation requires an active bone");
		return OPERATOR_CANCELLED;
	}
	else if (arm->flag & ARM_MIRROR_EDIT) {
		/* For X-Axis Mirror Editing option, we may need a mirror copy of actbone
		 * - if there's a mirrored copy of selbone, try to find a mirrored copy of actbone 
		 *   (i.e.  selbone="child.L" and actbone="parent.L", find "child.R" and "parent.R").
		 *   This is useful for arm-chains, for example parenting lower arm to upper arm
		 * - if there's no mirrored copy of actbone (i.e. actbone = "parent.C" or "parent")
		 *   then just use actbone. Useful when doing upper arm to spine.
		 */
		actmirb = ED_armature_bone_get_mirrored(arm->edbo, actbone);
		if (actmirb == NULL) 
			actmirb = actbone;
	}
	
	/* if there is only 1 selected bone, we assume that that is the active bone, 
	 * since a user will need to have clicked on a bone (thus selecting it) to make it active
	 */
	if (CTX_DATA_COUNT(C, selected_editable_bones) <= 1) {
		/* When only the active bone is selected, and it has a parent,
		 * align it to the parent, as that is the only possible outcome. 
		 */
		if (actbone->parent) {
			bone_align_to_bone(arm->edbo, actbone, actbone->parent);
			
			if ((arm->flag & ARM_MIRROR_EDIT) && (actmirb->parent))
				bone_align_to_bone(arm->edbo, actmirb, actmirb->parent);
		}
	}
	else {
		/* Align 'selected' bones to the active one
		 * - the context iterator contains both selected bones and their mirrored copies,
		 *   so we assume that unselected bones are mirrored copies of some selected bone
		 * - since the active one (and/or its mirror) will also be selected, we also need 
		 *   to check that we are not trying to operate on them, since such an operation
		 *   would cause errors
		 */
		
		/* align selected bones to the active one */
		CTX_DATA_BEGIN(C, EditBone *, ebone, selected_editable_bones)
		{
			if (ELEM(ebone, actbone, actmirb) == 0) {
				if (ebone->flag & BONE_SELECTED)
					bone_align_to_bone(arm->edbo, ebone, actbone);
				else
					bone_align_to_bone(arm->edbo, ebone, actmirb);
			}
		}
		CTX_DATA_END;
	}
	

	/* note, notifier might evolve */
	WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, ob);
	
	return OPERATOR_FINISHED;
}

void ARMATURE_OT_align(wmOperatorType *ot)
{
	/* identifiers */
	ot->name = "Align Bones";
	ot->idname = "ARMATURE_OT_align";
	ot->description = "Align selected bones to the active bone (or to their parent)";
	
	/* api callbacks */
	ot->invoke = WM_operator_confirm;
	ot->exec = armature_align_bones_exec;
	ot->poll = ED_operator_editarmature;
	
	/* flags */
	ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}

/* ***************** Pose tools ********************* */

/* XXX bone_looper is only to be used when we want to access settings
 * (i.e. editability/visibility/selected) that context doesn't offer */
static int bone_looper(Object *ob, Bone *bone, void *data,
                       int (*bone_func)(Object *, Bone *, void *))
{
	/* We want to apply the function bone_func to every bone 
	 * in an armature -- feed bone_looper the first bone and 
	 * a pointer to the bone_func and watch it go!. The int count 
	 * can be useful for counting bones with a certain property
	 * (e.g. skinnable)
	 */
	int count = 0;
	
	if (bone) {
		/* only do bone_func if the bone is non null */
		count += bone_func(ob, bone, data);
		
		/* try to execute bone_func for the first child */
		count += bone_looper(ob, bone->childbase.first, data, bone_func);
		
		/* try to execute bone_func for the next bone at this
		 * depth of the recursion.
		 */
		count += bone_looper(ob, bone->next, data, bone_func);
	}
	
	return count;
}

/* called from editview.c, for mode-less pose selection */
/* assumes scene obact and basact is still on old situation */
int ED_do_pose_selectbuffer(Scene *scene, Base *base, unsigned int *buffer, short hits,
                            short extend, short deselect, short toggle)
{
	Object *ob = base->object;
	Bone *nearBone;
	
	if (!ob || !ob->pose) return 0;

	nearBone = get_bone_from_selectbuffer(scene, base, buffer, hits, 1);
	
	/* if the bone cannot be affected, don't do anything */
	if ((nearBone) && !(nearBone->flag & BONE_UNSELECTABLE)) {
		Object *ob_act = OBACT;
		bArmature *arm = ob->data;
		
		/* since we do unified select, we don't shift+select a bone if the
		 * armature object was not active yet.
		 * note, special exception for armature mode so we can do multi-select
		 * we could check for multi-select explicitly but think its fine to
		 * always give predictable behavior in weight paint mode - campbell */
		if ((!extend && !deselect && !toggle) ||
		    ((ob_act && (ob_act != ob) && (ob_act->mode & OB_MODE_WEIGHT_PAINT) == 0)))
		{
			ED_pose_deselectall(ob, 0);
			nearBone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
			arm->act_bone = nearBone;
		}
		else {
			if (extend) {
				nearBone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
				arm->act_bone = nearBone;
			}
			else if (deselect) {
				nearBone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
			}
			else if (toggle) {
				if (nearBone->flag & BONE_SELECTED) {
					/* if not active, we make it active */
					if (nearBone != arm->act_bone) {
						arm->act_bone = nearBone;
					}
					else {
						nearBone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
					}
				}
				else {
					nearBone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
					arm->act_bone = nearBone;
				}
			}
		}
		
		if (ob_act) {
			/* in weightpaint we select the associated vertex group too */
			if (ob_act->mode & OB_MODE_WEIGHT_PAINT) {
				if (nearBone == arm->act_bone) {
					ED_vgroup_select_by_name(ob_act, nearBone->name);
					DAG_id_tag_update(&ob_act->id, OB_RECALC_DATA);
				}
			}
			/* if there are some dependencies for visualizing armature state 
			 * (e.g. Mask Modifier in 'Armature' mode), force update 
			 */
			else if (arm->flag & ARM_HAS_VIZ_DEPS) {
				DAG_id_tag_update(&ob_act->id, OB_RECALC_DATA);
			}
		}
	}
	
	return nearBone != NULL;
}

/* test==0: deselect all
 * test==1: swap select (apply to all the opposite of current situation) 
 * test==2: only clear active tag
 * test==3: swap select (no test / inverse selection status of all independently)
 */
void ED_pose_deselectall(Object *ob, int test)
{
	bArmature *arm = ob->data;
	bPoseChannel *pchan;
	int selectmode = 0;
	
	/* we call this from outliner too */
	if (ob->pose == NULL) {
		return;
	}
	
	/*	Determine if we're selecting or deselecting	*/
	if (test == 1) {
		for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) {
			if (PBONE_VISIBLE(arm, pchan->bone)) {
				if (pchan->bone->flag & BONE_SELECTED)
					break;
			}
		}
		
		if (pchan == NULL)
			selectmode = 1;
	}
	else if (test == 2)
		selectmode = 2;
	
	/*	Set the flags accordingly	*/
	for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) {
		/* ignore the pchan if it isn't visible or if its selection cannot be changed */
		if ((pchan->bone->layer & arm->layer) && !(pchan->bone->flag & (BONE_HIDDEN_P | BONE_UNSELECTABLE))) {
			if (test == 3) {
				pchan->bone->flag ^= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
			}
			else {
				if (selectmode == 0) pchan->bone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
				else if (selectmode == 1) pchan->bone->flag |= BONE_SELECTED;
			}
		}
	}
}

static int bone_skinnable_cb(Object *ob, Bone *bone, void *datap)
{
	/* Bones that are deforming
	 * are regarded to be "skinnable" and are eligible for
	 * auto-skinning.
	 *
	 * This function performs 2 functions:
	 *
	 *   a) It returns 1 if the bone is skinnable.
	 *      If we loop over all bones with this 
	 *      function, we can count the number of
	 *      skinnable bones.
	 *   b) If the pointer data is non null,
	 *      it is treated like a handle to a
	 *      bone pointer -- the bone pointer
	 *      is set to point at this bone, and
	 *      the pointer the handle points to
	 *      is incremented to point to the
	 *      next member of an array of pointers
	 *      to bones. This way we can loop using
	 *      this function to construct an array of
	 *      pointers to bones that point to all
	 *      skinnable bones.
	 */
	Bone ***hbone;
	int a, segments;
	struct { Object *armob; void *list; int heat; } *data = datap;

	if (!(ob->mode & OB_MODE_WEIGHT_PAINT) || !(bone->flag & BONE_HIDDEN_P)) {
		if (!(bone->flag & BONE_NO_DEFORM)) {
			if (data->heat && data->armob->pose && BKE_pose_channel_find_name(data->armob->pose, bone->name))
				segments = bone->segments;
			else
				segments = 1;
			
			if (data->list != NULL) {
				hbone = (Bone ***) &data->list;
				
				for (a = 0; a < segments; a++) {
					**hbone = bone;
					++*hbone;
				}
			}
			return segments;
		}
	}
	return 0;
}

static int vgroup_add_unique_bone_cb(Object *ob, Bone *bone, void *UNUSED(ptr)) 
{
	/* This group creates a vertex group to ob that has the
	 * same name as bone (provided the bone is skinnable). 
	 * If such a vertex group aleady exist the routine exits.
	 */
	if (!(bone->flag & BONE_NO_DEFORM)) {
		if (!defgroup_find_name(ob, bone->name)) {
			ED_vgroup_add_name(ob, bone->name);
			return 1;
		}
	}
	return 0;
}

static int dgroup_skinnable_cb(Object *ob, Bone *bone, void *datap) 
{
	/* Bones that are deforming
	 * are regarded to be "skinnable" and are eligible for
	 * auto-skinning.
	 *
	 * This function performs 2 functions:
	 *
	 *   a) If the bone is skinnable, it creates 
	 *      a vertex group for ob that has
	 *      the name of the skinnable bone
	 *      (if one doesn't exist already).
	 *   b) If the pointer data is non null,
	 *      it is treated like a handle to a
	 *      bDeformGroup pointer -- the 
	 *      bDeformGroup pointer is set to point
	 *      to the deform group with the bone's
	 *      name, and the pointer the handle 
	 *      points to is incremented to point to the
	 *      next member of an array of pointers
	 *      to bDeformGroups. This way we can loop using
	 *      this function to construct an array of
	 *      pointers to bDeformGroups, all with names
	 *      of skinnable bones.
	 */
	bDeformGroup ***hgroup, *defgroup = NULL;
	int a, segments;
	struct { Object *armob; void *list; int heat; } *data = datap;
	int wpmode = (ob->mode & OB_MODE_WEIGHT_PAINT);
	bArmature *arm = data->armob->data;

	if (!wpmode || !(bone->flag & BONE_HIDDEN_P)) {
		if (!(bone->flag & BONE_NO_DEFORM)) {
			if (data->heat && data->armob->pose && BKE_pose_channel_find_name(data->armob->pose, bone->name))
				segments = bone->segments;
			else
				segments = 1;

			if (!wpmode || ((arm->layer & bone->layer) && (bone->flag & BONE_SELECTED)))
				if (!(defgroup = defgroup_find_name(ob, bone->name)))
					defgroup = ED_vgroup_add_name(ob, bone->name);
			
			if (data->list != NULL) {
				hgroup = (bDeformGroup ***) &data->list;
				
				for (a = 0; a < segments; a++) {
					**hgroup = defgroup;
					++*hgroup;
				}
			}
			return segments;
		}
	}
	return 0;
}

static void add_vgroups__mapFunc(void *userData, int index, const float co[3],
                                 const float UNUSED(no_f[3]), const short UNUSED(no_s[3]))
{
	/* DerivedMesh mapFunc for getting final coords in weight paint mode */

	float (*verts)[3] = userData;
	copy_v3_v3(verts[index], co);
}

static void envelope_bone_weighting(Object *ob, Mesh *mesh, float (*verts)[3], int numbones, Bone **bonelist,
                                    bDeformGroup **dgrouplist, bDeformGroup **dgroupflip,
                                    float (*root)[3], float (*tip)[3], int *selected, float scale)
{
	/* Create vertex group weights from envelopes */

	Bone *bone;
	bDeformGroup *dgroup;
	float distance;
	int i, iflip, j;

	/* for each vertex in the mesh */
	for (i = 0; i < mesh->totvert; i++) {
		iflip = (dgroupflip) ? mesh_get_x_mirror_vert(ob, i) : 0;
		
		/* for each skinnable bone */
		for (j = 0; j < numbones; ++j) {
			if (!selected[j])
				continue;
			
			bone = bonelist[j];
			dgroup = dgrouplist[j];
			
			/* store the distance-factor from the vertex to the bone */
			distance = distfactor_to_bone(verts[i], root[j], tip[j],
			                              bone->rad_head * scale, bone->rad_tail * scale, bone->dist * scale);
			
			/* add the vert to the deform group if (weight != 0.0) */
			if (distance != 0.0f)
				ED_vgroup_vert_add(ob, dgroup, i, distance, WEIGHT_REPLACE);
			else
				ED_vgroup_vert_remove(ob, dgroup, i);
			
			/* do same for mirror */
			if (dgroupflip && dgroupflip[j] && iflip >= 0) {
				if (distance != 0.0f)
					ED_vgroup_vert_add(ob, dgroupflip[j], iflip, distance,
					                   WEIGHT_REPLACE);
				else
					ED_vgroup_vert_remove(ob, dgroupflip[j], iflip);
			}
		}
	}
}

static void add_verts_to_dgroups(ReportList *reports, Scene *scene, Object *ob, Object *par, int heat, int mirror)
{
	/* This functions implements the automatic computation of vertex group
	 * weights, either through envelopes or using a heat equilibrium.
	 *
	 * This function can be called both when parenting a mesh to an armature,
	 * or in weightpaint + posemode. In the latter case selection is taken
	 * into account and vertex weights can be mirrored.
	 *
	 * The mesh vertex positions used are either the final deformed coords
	 * from the derivedmesh in weightpaint mode, the final subsurf coords
	 * when parenting, or simply the original mesh coords.
	 */

	bArmature *arm = par->data;
	Bone **bonelist, *bone;
	bDeformGroup **dgrouplist, **dgroupflip;
	bDeformGroup *dgroup;
	bPoseChannel *pchan;
	Mesh *mesh;
	Mat4 *bbone = NULL;
	float (*root)[3], (*tip)[3], (*verts)[3];
	int *selected;
	int numbones, vertsfilled = 0, i, j, segments = 0;
	int wpmode = (ob->mode & OB_MODE_WEIGHT_PAINT);
	struct { Object *armob; void *list; int heat; } looper_data;

	looper_data.armob = par;
	looper_data.heat = heat;
	looper_data.list = NULL;

	/* count the number of skinnable bones */
	numbones = bone_looper(ob, arm->bonebase.first, &looper_data, bone_skinnable_cb);
	
	if (numbones == 0)
		return;
	
	if (ED_vgroup_data_create(ob->data) == FALSE)
		return;

	/* create an array of pointer to bones that are skinnable
	 * and fill it with all of the skinnable bones */
	bonelist = MEM_callocN(numbones * sizeof(Bone *), "bonelist");
	looper_data.list = bonelist;
	bone_looper(ob, arm->bonebase.first, &looper_data, bone_skinnable_cb);

	/* create an array of pointers to the deform groups that
	 * correspond to the skinnable bones (creating them
	 * as necessary. */
	dgrouplist = MEM_callocN(numbones * sizeof(bDeformGroup *), "dgrouplist");
	dgroupflip = MEM_callocN(numbones * sizeof(bDeformGroup *), "dgroupflip");

	looper_data.list = dgrouplist;
	bone_looper(ob, arm->bonebase.first, &looper_data, dgroup_skinnable_cb);

	/* create an array of root and tip positions transformed into
	 * global coords */
	root = MEM_callocN(numbones * sizeof(float) * 3, "root");
	tip = MEM_callocN(numbones * sizeof(float) * 3, "tip");
	selected = MEM_callocN(numbones * sizeof(int), "selected");

	for (j = 0; j < numbones; ++j) {
		bone = bonelist[j];
		dgroup = dgrouplist[j];
		
		/* handle bbone */
		if (heat) {
			if (segments == 0) {
				segments = 1;
				bbone = NULL;
				
				if ((par->pose) && (pchan = BKE_pose_channel_find_name(par->pose, bone->name))) {
					if (bone->segments > 1) {
						segments = bone->segments;
						bbone = b_bone_spline_setup(pchan, 1);
					}
				}
			}
			
			segments--;
		}
		
		/* compute root and tip */
		if (bbone) {
			mul_v3_m4v3(root[j], bone->arm_mat, bbone[segments].mat[3]);
			if ((segments + 1) < bone->segments) {
				mul_v3_m4v3(tip[j], bone->arm_mat, bbone[segments + 1].mat[3]);
			}
			else {
				copy_v3_v3(tip[j], bone->arm_tail);
			}
		}
		else {
			copy_v3_v3(root[j], bone->arm_head);
			copy_v3_v3(tip[j], bone->arm_tail);
		}
		
		mul_m4_v3(par->obmat, root[j]);
		mul_m4_v3(par->obmat, tip[j]);
		
		/* set selected */
		if (wpmode) {
			if ((arm->layer & bone->layer) && (bone->flag & BONE_SELECTED))
				selected[j] = 1;
		}
		else
			selected[j] = 1;
		
		/* find flipped group */
		if (dgroup && mirror) {
			char name[MAXBONENAME];

			// 0 = don't strip off number extensions
			flip_side_name(name, dgroup->name, FALSE);
			dgroupflip[j] = defgroup_find_name(ob, name);
		}
	}

	/* create verts */
	mesh = (Mesh *)ob->data;
	verts = MEM_callocN(mesh->totvert * sizeof(*verts), "closestboneverts");

	if (wpmode) {
		/* if in weight paint mode, use final verts from derivedmesh */
		DerivedMesh *dm = mesh_get_derived_final(scene, ob, CD_MASK_BAREMESH);
		
		if (dm->foreachMappedVert) {
			dm->foreachMappedVert(dm, add_vgroups__mapFunc, (void *)verts);
			vertsfilled = 1;
		}
		
		dm->release(dm);
	}
	else if (modifiers_findByType(ob, eModifierType_Subsurf)) {
		/* is subsurf on? Lets use the verts on the limit surface then.
		 * = same amount of vertices as mesh, but vertices  moved to the
		 * subsurfed position, like for 'optimal'. */
		subsurf_calculate_limit_positions(mesh, verts);
		vertsfilled = 1;
	}

	/* transform verts to global space */
	for (i = 0; i < mesh->totvert; i++) {
		if (!vertsfilled)
			copy_v3_v3(verts[i], mesh->mvert[i].co);
		mul_m4_v3(ob->obmat, verts[i]);
	}

	/* compute the weights based on gathered vertices and bones */
	if (heat) {
		const char *error = NULL;
		heat_bone_weighting(ob, mesh, verts, numbones, dgrouplist, dgroupflip,
		                    root, tip, selected, &error);
		
		if (error) {
			BKE_report(reports, RPT_WARNING, error);
		}
	}
	else {
		envelope_bone_weighting(ob, mesh, verts, numbones, bonelist, dgrouplist,
		                        dgroupflip, root, tip, selected, mat4_to_scale(par->obmat));
	}

	/* only generated in some cases but can call anyway */
	mesh_octree_table(ob, NULL, NULL, 'e');

	/* free the memory allocated */
	MEM_freeN(bonelist);
	MEM_freeN(dgrouplist);
	MEM_freeN(dgroupflip);
	MEM_freeN(root);
	MEM_freeN(tip);
	MEM_freeN(selected);
	MEM_freeN(verts);
}

void create_vgroups_from_armature(ReportList *reports, Scene *scene, Object *ob, Object *par, int mode, int mirror)
{
	/* Lets try to create some vertex groups 
	 * based on the bones of the parent armature.
	 */
	bArmature *arm = par->data;

	if (mode == ARM_GROUPS_NAME) {
		const int defbase_tot = BLI_countlist(&ob->defbase);
		int defbase_add;
		/* Traverse the bone list, trying to create empty vertex 
		 * groups corresponding to the bone.
		 */
		defbase_add = bone_looper(ob, arm->bonebase.first, NULL, vgroup_add_unique_bone_cb);

		if (defbase_add) {
			/* its possible there are DWeight's outside the range of the current
			 * objects deform groups, in this case the new groups wont be empty [#33889] */
			ED_vgroup_data_clamp_range(ob->data, defbase_tot);
		}
	}
	else if (mode == ARM_GROUPS_ENVELOPE || mode == ARM_GROUPS_AUTO) {
		/* Traverse the bone list, trying to create vertex groups 
		 * that are populated with the vertices for which the
		 * bone is closest.
		 */
		add_verts_to_dgroups(reports, scene, ob, par, (mode == ARM_GROUPS_AUTO), mirror);
	}
} 
/* ************* Clear Pose *****************************/

/* clear scale of pose-channel */
static void pchan_clear_scale(bPoseChannel *pchan)
{
	if ((pchan->protectflag & OB_LOCK_SCALEX) == 0)
		pchan->size[0] = 1.0f;
	if ((pchan->protectflag & OB_LOCK_SCALEY) == 0)
		pchan->size[1] = 1.0f;
	if ((pchan->protectflag & OB_LOCK_SCALEZ) == 0)
		pchan->size[2] = 1.0f;
}

/* clear location of pose-channel */
static void pchan_clear_loc(bPoseChannel *pchan)
{
	if ((pchan->protectflag & OB_LOCK_LOCX) == 0)
		pchan->loc[0] = 0.0f;
	if ((pchan->protectflag & OB_LOCK_LOCY) == 0)
		pchan->loc[1] = 0.0f;
	if ((pchan->protectflag & OB_LOCK_LOCZ) == 0)
		pchan->loc[2] = 0.0f;
}

/* clear rotation of pose-channel */
static void pchan_clear_rot(bPoseChannel *pchan)
{
	if (pchan->protectflag & (OB_LOCK_ROTX | OB_LOCK_ROTY | OB_LOCK_ROTZ | OB_LOCK_ROTW)) {
		/* check if convert to eulers for locking... */
		if (pchan->protectflag & OB_LOCK_ROT4D) {
			/* perform clamping on a component by component basis */
			if (pchan->rotmode == ROT_MODE_AXISANGLE) {
				if ((pchan->protectflag & OB_LOCK_ROTW) == 0)
					pchan->rotAngle = 0.0f;
				if ((pchan->protectflag & OB_LOCK_ROTX) == 0)
					pchan->rotAxis[0] = 0.0f;
				if ((pchan->protectflag & OB_LOCK_ROTY) == 0)
					pchan->rotAxis[1] = 0.0f;
				if ((pchan->protectflag & OB_LOCK_ROTZ) == 0)
					pchan->rotAxis[2] = 0.0f;
					
				/* check validity of axis - axis should never be 0,0,0 (if so, then we make it rotate about y) */
				if (IS_EQF(pchan->rotAxis[0], pchan->rotAxis[1]) && IS_EQF(pchan->rotAxis[1], pchan->rotAxis[2]))
					pchan->rotAxis[1] = 1.0f;
			}
			else if (pchan->rotmode == ROT_MODE_QUAT) {
				if ((pchan->protectflag & OB_LOCK_ROTW) == 0)
					pchan->quat[0] = 1.0f;
				if ((pchan->protectflag & OB_LOCK_ROTX) == 0)
					pchan->quat[1] = 0.0f;
				if ((pchan->protectflag & OB_LOCK_ROTY) == 0)
					pchan->quat[2] = 0.0f;
				if ((pchan->protectflag & OB_LOCK_ROTZ) == 0)
					pchan->quat[3] = 0.0f;
			}
			else {
				/* the flag may have been set for the other modes, so just ignore the extra flag... */
				if ((pchan->protectflag & OB_LOCK_ROTX) == 0)
					pchan->eul[0] = 0.0f;
				if ((pchan->protectflag & OB_LOCK_ROTY) == 0)
					pchan->eul[1] = 0.0f;
				if ((pchan->protectflag & OB_LOCK_ROTZ) == 0)
					pchan->eul[2] = 0.0f;
			}
		}
		else {
			/* perform clamping using euler form (3-components) */
			float eul[3], oldeul[3], quat1[4] = {0};
			float qlen = 0.0f;
			
			if (pchan->rotmode == ROT_MODE_QUAT) {
				qlen = normalize_qt_qt(quat1, pchan->quat);
				quat_to_eul(oldeul, quat1);
			}
			else if (pchan->rotmode == ROT_MODE_AXISANGLE) {
				axis_angle_to_eulO(oldeul, EULER_ORDER_DEFAULT, pchan->rotAxis, pchan->rotAngle);
			}
			else {
				copy_v3_v3(oldeul, pchan->eul);
			}
			
			eul[0] = eul[1] = eul[2] = 0.0f;
			
			if (pchan->protectflag & OB_LOCK_ROTX)
				eul[0] = oldeul[0];
			if (pchan->protectflag & OB_LOCK_ROTY)
				eul[1] = oldeul[1];
			if (pchan->protectflag & OB_LOCK_ROTZ)
				eul[2] = oldeul[2];
			
			if (pchan->rotmode == ROT_MODE_QUAT) {
				eul_to_quat(pchan->quat, eul);
				
				/* restore original quat size */
				mul_qt_fl(pchan->quat, qlen);
				
				/* quaternions flip w sign to accumulate rotations correctly */
				if ((quat1[0] < 0.0f && pchan->quat[0] > 0.0f) || (quat1[0] > 0.0f && pchan->quat[0] < 0.0f)) {
					mul_qt_fl(pchan->quat, -1.0f);
				}
			}
			else if (pchan->rotmode == ROT_MODE_AXISANGLE) {
				eulO_to_axis_angle(pchan->rotAxis, &pchan->rotAngle, eul, EULER_ORDER_DEFAULT);
			}
			else {
				copy_v3_v3(pchan->eul, eul);
			}
		}
	}       /* Duplicated in source/blender/editors/object/object_transform.c */
	else {
		if (pchan->rotmode == ROT_MODE_QUAT) {
			unit_qt(pchan->quat);
		}
		else if (pchan->rotmode == ROT_MODE_AXISANGLE) {
			/* by default, make rotation of 0 radians around y-axis (roll) */
			unit_axis_angle(pchan->rotAxis, &pchan->rotAngle);
		}
		else {
			zero_v3(pchan->eul);
		}
	}
}

/* clear loc/rot/scale of pose-channel */
static void pchan_clear_transforms(bPoseChannel *pchan)
{
	pchan_clear_loc(pchan);
	pchan_clear_rot(pchan);
	pchan_clear_scale(pchan);
}

/* --------------- */

/* generic exec for clear-pose operators */
static int pose_clear_transform_generic_exec(bContext *C, wmOperator *op, 
                                             void (*clear_func)(bPoseChannel *), const char default_ksName[])
{
	Scene *scene = CTX_data_scene(C);
	Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C));
	short autokey = 0;
	
	/* sanity checks */
	if (ELEM(NULL, clear_func, default_ksName)) {
		BKE_report(op->reports, RPT_ERROR, "Programming error: missing clear transform function or keying set name");
		return OPERATOR_CANCELLED;
	}
	
	/* only clear relevant transforms for selected bones */
	CTX_DATA_BEGIN(C, bPoseChannel *, pchan, selected_pose_bones)
	{
		/* run provided clearing function */
		clear_func(pchan);
		
		/* do auto-keyframing as appropriate */
		if (autokeyframe_cfra_can_key(scene, &ob->id)) {
			/* clear any unkeyed tags */
			if (pchan->bone)
				pchan->bone->flag &= ~BONE_UNKEYED;
				
			/* tag for autokeying later */
			autokey = 1;
		}
		else {
			/* add unkeyed tags */
			if (pchan->bone)
				pchan->bone->flag |= BONE_UNKEYED;
		}
	}
	CTX_DATA_END;
	
	/* perform autokeying on the bones if needed */
	if (autokey) {
		/* get KeyingSet to use */
		KeyingSet *ks = ANIM_get_keyingset_for_autokeying(scene, default_ksName);
		
		/* insert keyframes */
		ANIM_apply_keyingset(C, NULL, NULL, ks, MODIFYKEY_MODE_INSERT, (float)CFRA);
		
		/* now recalculate paths */
		if ((ob->pose->avs.path_bakeflag & MOTIONPATH_BAKE_HAS_PATHS))
			ED_pose_recalculate_paths(scene, ob);
	}
	
	DAG_id_tag_update(&ob->id, OB_RECALC_DATA);

	/* note, notifier might evolve */
	WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, ob);
	
	return OPERATOR_FINISHED;
}

/* --------------- */

static int pose_clear_scale_exec(bContext *C, wmOperator *op) 
{
	return pose_clear_transform_generic_exec(C, op, pchan_clear_scale, ANIM_KS_SCALING_ID);
}

void POSE_OT_scale_clear(wmOperatorType *ot)
{
	/* identifiers */
	ot->name = "Clear Pose Scale";
	ot->idname = "POSE_OT_scale_clear";
	ot->description = "Reset scaling of selected bones to their default values";
	
	/* api callbacks */
	ot->exec = pose_clear_scale_exec;
	ot->poll = ED_operator_posemode;
	
	/* flags */
	ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}


static int pose_clear_rot_exec(bContext *C, wmOperator *op) 
{
	return pose_clear_transform_generic_exec(C, op, pchan_clear_rot, ANIM_KS_ROTATION_ID);
}

void POSE_OT_rot_clear(wmOperatorType *ot)
{
	/* identifiers */
	ot->name = "Clear Pose Rotation";
	ot->idname = "POSE_OT_rot_clear";
	ot->description = "Reset rotations of selected bones to their default values";
	
	/* api callbacks */
	ot->exec = pose_clear_rot_exec;
	ot->poll = ED_operator_posemode;
	
	/* flags */
	ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}


static int pose_clear_loc_exec(bContext *C, wmOperator *op) 
{
	return pose_clear_transform_generic_exec(C, op, pchan_clear_loc, ANIM_KS_LOCATION_ID);
}

void POSE_OT_loc_clear(wmOperatorType *ot)
{
	/* identifiers */
	ot->name = "Clear Pose Location";
	ot->idname = "POSE_OT_loc_clear";
	ot->description = "Reset locations of selected bones to their default values";
	
	/* api callbacks */
	ot->exec = pose_clear_loc_exec;
	ot->poll = ED_operator_posemode;
	
	/* flags */
	ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}


static int pose_clear_transforms_exec(bContext *C, wmOperator *op) 
{
	return pose_clear_transform_generic_exec(C, op, pchan_clear_transforms, ANIM_KS_LOC_ROT_SCALE_ID);
}

void POSE_OT_transforms_clear(wmOperatorType *ot)
{
	/* identifiers */
	ot->name = "Clear Pose Transforms";
	ot->idname = "POSE_OT_transforms_clear";
	ot->description = "Reset location, rotation, and scaling of selected bones to their default values";
	
	/* api callbacks */
	ot->exec = pose_clear_transforms_exec;
	ot->poll = ED_operator_posemode;
	
	/* flags */
	ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}

/* ***************** selections ********************** */

static int pose_de_select_all_exec(bContext *C, wmOperator *op)
{
	int action = RNA_enum_get(op->ptr, "action");
	
	Scene *scene = CTX_data_scene(C);
	Object *ob = ED_object_context(C);
	bArmature *arm = ob->data;
	int multipaint = scene->toolsettings->multipaint;

	if (action == SEL_TOGGLE) {
		action = CTX_DATA_COUNT(C, selected_pose_bones) ? SEL_DESELECT : SEL_SELECT;
	}
	
	/*	Set the flags */
	CTX_DATA_BEGIN(C, bPoseChannel *, pchan, visible_pose_bones)
	{
		/* select pchan only if selectable, but deselect works always */
		switch (action) {
			case SEL_SELECT:
				if ((pchan->bone->flag & BONE_UNSELECTABLE) == 0)
					pchan->bone->flag |= BONE_SELECTED;
				break;
			case SEL_DESELECT:
				pchan->bone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
				break;
			case SEL_INVERT:
				if (pchan->bone->flag & BONE_SELECTED) {
					pchan->bone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
				}
				else if ((pchan->bone->flag & BONE_UNSELECTABLE) == 0) {
					pchan->bone->flag |= BONE_SELECTED;
				}
				break;
		}
	}
	CTX_DATA_END;

	WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, NULL);
	
	/* weightpaint or mask modifiers need depsgraph updates */
	if (multipaint || (arm->flag & ARM_HAS_VIZ_DEPS)) {
		DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
	}

	return OPERATOR_FINISHED;
}

void POSE_OT_select_all(wmOperatorType *ot)
{
	/* identifiers */
	ot->name = "(De)select All";
	ot->idname = "POSE_OT_select_all";
	ot->description = "Toggle selection status of all bones";
	
	/* api callbacks */
	ot->exec = pose_de_select_all_exec;
	ot->poll = ED_operator_posemode;
	
	/* flags */
	ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
	
	WM_operator_properties_select_all(ot);
}

static int pose_select_parent_exec(bContext *C, wmOperator *UNUSED(op))
{
	Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C));
	bArmature *arm = (bArmature *)ob->data;
	bPoseChannel *pchan, *parent;

	/* Determine if there is an active bone */
	pchan = CTX_data_active_pose_bone(C);
	if (pchan) {
		parent = pchan->parent;
		if ((parent) && !(parent->bone->flag & (BONE_HIDDEN_P | BONE_UNSELECTABLE))) {
			parent->bone->flag |= BONE_SELECTED;
			arm->act_bone = parent->bone;
		}
		else {
			return OPERATOR_CANCELLED;
		}
	}
	else {
		return OPERATOR_CANCELLED;
	}
	
	/* updates */
	WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob);
	
	if (arm->flag & ARM_HAS_VIZ_DEPS) {
		/* mask modifier ('armature' mode), etc. */
		DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
	}
	
	return OPERATOR_FINISHED;
}

void POSE_OT_select_parent(wmOperatorType *ot)
{
	/* identifiers */
	ot->name = "Select Parent Bone";
	ot->idname = "POSE_OT_select_parent";
	ot->description = "Select bones that are parents of the currently selected bones";

	/* api callbacks */
	ot->exec = pose_select_parent_exec;
	ot->poll = ED_operator_posemode;

	/* flags */
	ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
	
}

/* ************* hide/unhide pose bones ******************* */

static int hide_selected_pose_bone_cb(Object *ob, Bone *bone, void *UNUSED(ptr)) 
{
	bArmature *arm = ob->data;
	
	if (arm->layer & bone->layer) {
		if (bone->flag & BONE_SELECTED) {
			bone->flag |= BONE_HIDDEN_P;
			bone->flag &= ~BONE_SELECTED;
			if (arm->act_bone == bone)
				arm->act_bone = NULL;
		}
	}
	return 0;
}

static int hide_unselected_pose_bone_cb(Object *ob, Bone *bone, void *UNUSED(ptr)) 
{
	bArmature *arm = ob->data;
	
	if (arm->layer & bone->layer) {
		/* hrm... typo here? */
		if ((bone->flag & BONE_SELECTED) == 0) {
			bone->flag |= BONE_HIDDEN_P;
			if (arm->act_bone == bone)
				arm->act_bone = NULL;
		}
	}
	return 0;
}

/* active object is armature in posemode, poll checked */
static int pose_hide_exec(bContext *C, wmOperator *op) 
{
	Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C));
	bArmature *arm = ob->data;

	if (RNA_boolean_get(op->ptr, "unselected"))
		bone_looper(ob, arm->bonebase.first, NULL, hide_unselected_pose_bone_cb);
	else
		bone_looper(ob, arm->bonebase.first, NULL, hide_selected_pose_bone_cb);
	
	/* note, notifier might evolve */
	WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob);
	
	return OPERATOR_FINISHED;
}

void POSE_OT_hide(wmOperatorType *ot)
{
	/* identifiers */
	ot->name = "Hide Selected";
	ot->idname = "POSE_OT_hide";
	ot->description = "Tag selected bones to not be visible in Pose Mode";
	
	/* api callbacks */
	ot->exec = pose_hide_exec;
	ot->poll = ED_operator_posemode;
	
	/* flags */
	ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
	
	/* props */
	RNA_def_boolean(ot->srna, "unselected", 0, "Unselected", "");
}

static int show_pose_bone_cb(Object *ob, Bone *bone, void *UNUSED(ptr)) 
{
	bArmature *arm = ob->data;
	
	if (arm->layer & bone->layer) {
		if (bone->flag & BONE_HIDDEN_P) {
			bone->flag &= ~BONE_HIDDEN_P;
			bone->flag |= BONE_SELECTED;
		}
	}
	
	return 0;
}

/* active object is armature in posemode, poll checked */
static int pose_reveal_exec(bContext *C, wmOperator *UNUSED(op)) 
{
	Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C));
	bArmature *arm = ob->data;
	
	bone_looper(ob, arm->bonebase.first, NULL, show_pose_bone_cb);
	
	/* note, notifier might evolve */
	WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob);

	return OPERATOR_FINISHED;
}

void POSE_OT_reveal(wmOperatorType *ot)
{
	/* identifiers */
	ot->name = "Reveal Selected";
	ot->idname = "POSE_OT_reveal";
	ot->description = "Unhide all bones that have been tagged to be hidden in Pose Mode";
	
	/* api callbacks */
	ot->exec = pose_reveal_exec;
	ot->poll = ED_operator_posemode;
	
	/* flags */
	ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}

/* ************* RENAMING DISASTERS ************ */

static int bone_unique_check(void *arg, const char *name)
{
	return BKE_armature_find_bone_name((bArmature *)arg, name) != NULL;
}

static void unique_bone_name(bArmature *arm, char *name)
{
	BLI_uniquename_cb(bone_unique_check, (void *)arm, "Bone", '.', name, sizeof(((Bone *)NULL)->name));
}

/* helper call for armature_bone_rename */
static void constraint_bone_name_fix(Object *ob, ListBase *conlist, char *oldname, char *newname)
{
	bConstraint *curcon;
	bConstraintTarget *ct;
	
	for (curcon = conlist->first; curcon; curcon = curcon->next) {
		bConstraintTypeInfo *cti = BKE_constraint_get_typeinfo(curcon);
		ListBase targets = {NULL, NULL};
		
		if (cti && cti->get_constraint_targets) {
			cti->get_constraint_targets(curcon, &targets);
			
			for (ct = targets.first; ct; ct = ct->next) {
				if (ct->tar == ob) {
					if (!strcmp(ct->subtarget, oldname) )
						BLI_strncpy(ct->subtarget, newname, MAXBONENAME);
				}
			}
			
			if (cti->flush_constraint_targets)
				cti->flush_constraint_targets(curcon, &targets, 0);
		}
	}
}

/* called by UI for renaming a bone */
/* warning: make sure the original bone was not renamed yet! */
/* seems messy, but thats what you get with not using pointers but channel names :) */
void ED_armature_bone_rename(bArmature *arm, const char *oldnamep, const char *newnamep)
{
	Object *ob;
	char newname[MAXBONENAME];
	char oldname[MAXBONENAME];
	
	/* names better differ! */
	if (strncmp(oldnamep, newnamep, MAXBONENAME)) {
		
		/* we alter newname string... so make copy */
		BLI_strncpy(newname, newnamep, MAXBONENAME);
		/* we use oldname for search... so make copy */
		BLI_strncpy(oldname, oldnamep, MAXBONENAME);
		
		/* now check if we're in editmode, we need to find the unique name */
		if (arm->edbo) {
			EditBone *eBone = editbone_name_exists(arm->edbo, oldname);
			
			if (eBone) {
				unique_editbone_name(arm->edbo, newname, NULL);
				BLI_strncpy(eBone->name, newname, MAXBONENAME);
			}
			else return;
		}
		else {
			Bone *bone = BKE_armature_find_bone_name(arm, oldname);
			
			if (bone) {
				unique_bone_name(arm, newname);
				BLI_strncpy(bone->name, newname, MAXBONENAME);
			}
			else return;
		}
		
		/* do entire dbase - objects */
		for (ob = G.main->object.first; ob; ob = ob->id.next) {
			ModifierData *md;
			
			/* we have the object using the armature */
			if (arm == ob->data) {
				Object *cob;
				
				/* Rename the pose channel, if it exists */
				if (ob->pose) {
					bPoseChannel *pchan = BKE_pose_channel_find_name(ob->pose, oldname);
					if (pchan) {
						BLI_strncpy(pchan->name, newname, MAXBONENAME);
						
						if (ob->pose->chanhash) {
							GHash *gh = ob->pose->chanhash;
							
							/* remove the old hash entry, and replace with the new name */
							BLI_ghash_remove(gh, oldname, NULL, NULL);
							BLI_ghash_insert(gh, pchan->name, pchan);
						}
					}
				}
				
				/* Update any object constraints to use the new bone name */
				for (cob = G.main->object.first; cob; cob = cob->id.next) {
					if (cob->constraints.first)
						constraint_bone_name_fix(ob, &cob->constraints, oldname, newname);
					if (cob->pose) {
						bPoseChannel *pchan;
						for (pchan = cob->pose->chanbase.first; pchan; pchan = pchan->next) {
							constraint_bone_name_fix(ob, &pchan->constraints, oldname, newname);
						}
					}
				}
			}
			
			/* See if an object is parented to this armature */
			if (ob->parent && (ob->parent->data == arm)) {
				if (ob->partype == PARBONE) {
					/* bone name in object */
					if (!strcmp(ob->parsubstr, oldname))
						BLI_strncpy(ob->parsubstr, newname, MAXBONENAME);
				}
			}
			
			if (modifiers_usesArmature(ob, arm)) {
				bDeformGroup *dg = defgroup_find_name(ob, oldname);
				if (dg) {
					BLI_strncpy(dg->name, newname, MAXBONENAME);
				}
			}
			
			/* fix modifiers that might be using this name */
			for (md = ob->modifiers.first; md; md = md->next) {
				if (md->type == eModifierType_Hook) {
					HookModifierData *hmd = (HookModifierData *)md;
					
					/* uses armature, so may use the affected bone name */
					if (hmd->object && (hmd->object->data == arm)) {
						if (!strcmp(hmd->subtarget, oldname))
							BLI_strncpy(hmd->subtarget, newname, MAXBONENAME);
					}
				}
			}
		}
		
		/* Fix all animdata that may refer to this bone - we can't just do the ones attached to objects, since
		 * other ID-blocks may have drivers referring to this bone [#29822]
		 */
		{
			
			BKE_all_animdata_fix_paths_rename(&arm->id, "pose.bones", oldname, newname);
		}
		
		/* correct view locking */
		{
			bScreen *screen;
			for (screen = G.main->screen.first; screen; screen = screen->id.next) {
				ScrArea *sa;
				/* add regions */
				for (sa = screen->areabase.first; sa; sa = sa->next) {
					SpaceLink *sl;
					for (sl = sa->spacedata.first; sl; sl = sl->next) {
						if (sl->spacetype == SPACE_VIEW3D) {
							View3D *v3d = (View3D *)sl;
							if (v3d->ob_centre && v3d->ob_centre->data == arm) {
								if (!strcmp(v3d->ob_centre_bone, oldname)) {
									BLI_strncpy(v3d->ob_centre_bone, newname, MAXBONENAME);
								}
							}
						}
					}
				}
			}
		}
	}
}


static int armature_flip_names_exec(bContext *C, wmOperator *UNUSED(op))
{
	Object *ob = CTX_data_edit_object(C);
	bArmature *arm;
	char newname[MAXBONENAME];
	
	/* paranoia checks */
	if (ELEM(NULL, ob, ob->pose)) 
		return OPERATOR_CANCELLED;
	arm = ob->data;
	
	/* loop through selected bones, auto-naming them */
	CTX_DATA_BEGIN(C, EditBone *, ebone, selected_editable_bones)
	{
		flip_side_name(newname, ebone->name, TRUE); // 1 = do strip off number extensions
		ED_armature_bone_rename(arm, ebone->name, newname);
	}
	CTX_DATA_END;
	
	/* since we renamed stuff... */
	DAG_id_tag_update(&ob->id, OB_RECALC_DATA);

	/* note, notifier might evolve */
	WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob);
	
	return OPERATOR_FINISHED;
}

void ARMATURE_OT_flip_names(wmOperatorType *ot)
{
	/* identifiers */
	ot->name = "Flip Names";
	ot->idname = "ARMATURE_OT_flip_names";
	ot->description = "Flips (and corrects) the axis suffixes of the names of selected bones";
	
	/* api callbacks */
	ot->exec = armature_flip_names_exec;
	ot->poll = ED_operator_editarmature;
	
	/* flags */
	ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}


static int armature_autoside_names_exec(bContext *C, wmOperator *op)
{
	Object *ob = CTX_data_edit_object(C);
	bArmature *arm;
	char newname[MAXBONENAME];
	short axis = RNA_enum_get(op->ptr, "type");
	
	/* paranoia checks */
	if (ELEM(NULL, ob, ob->pose)) 
		return OPERATOR_CANCELLED;
	arm = ob->data;
	
	/* loop through selected bones, auto-naming them */
	CTX_DATA_BEGIN(C, EditBone *, ebone, selected_editable_bones)
	{
		BLI_strncpy(newname, ebone->name, sizeof(newname));
		if (bone_autoside_name(newname, 1, axis, ebone->head[axis], ebone->tail[axis]))
			ED_armature_bone_rename(arm, ebone->name, newname);
	}
	CTX_DATA_END;
	
	/* since we renamed stuff... */
	DAG_id_tag_update(&ob->id, OB_RECALC_DATA);

	/* note, notifier might evolve */
	WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob);
	
	return OPERATOR_FINISHED;
}

void ARMATURE_OT_autoside_names(wmOperatorType *ot)
{
	static EnumPropertyItem axis_items[] = {
		{0, "XAXIS", 0, "X-Axis", "Left/Right"},
		{1, "YAXIS", 0, "Y-Axis", "Front/Back"},
		{2, "ZAXIS", 0, "Z-Axis", "Top/Bottom"},
		{0, NULL, 0, NULL, NULL}
	};
	
	/* identifiers */
	ot->name = "AutoName by Axis";
	ot->idname = "ARMATURE_OT_autoside_names";
	ot->description = "Automatically renames the selected bones according to which side of the target axis they fall on";
	
	/* api callbacks */
	ot->invoke = WM_menu_invoke;
	ot->exec = armature_autoside_names_exec;
	ot->poll = ED_operator_editarmature;
	
	/* flags */
	ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
	
	/* settings */
	ot->prop = RNA_def_enum(ot->srna, "type", axis_items, 0, "Axis", "Axis tag names with");
}



/* if editbone (partial) selected, copy data */
/* context; editmode armature, with mirror editing enabled */
void transform_armature_mirror_update(Object *obedit)
{
	bArmature *arm = obedit->data;
	EditBone *ebo, *eboflip;
	
	for (ebo = arm->edbo->first; ebo; ebo = ebo->next) {
		/* no layer check, correct mirror is more important */
		if (ebo->flag & (BONE_TIPSEL | BONE_ROOTSEL)) {
			eboflip = ED_armature_bone_get_mirrored(arm->edbo, ebo);
			
			if (eboflip) {
				/* we assume X-axis flipping for now */
				if (ebo->flag & BONE_TIPSEL) {
					EditBone *children;
					
					eboflip->tail[0] = -ebo->tail[0];
					eboflip->tail[1] = ebo->tail[1];
					eboflip->tail[2] = ebo->tail[2];
					eboflip->rad_tail = ebo->rad_tail;
					eboflip->roll = -ebo->roll;

					/* Also move connected children, in case children's name aren't mirrored properly */
					for (children = arm->edbo->first; children; children = children->next) {
						if (children->parent == eboflip && children->flag & BONE_CONNECTED) {
							copy_v3_v3(children->head, eboflip->tail);
							children->rad_head = ebo->rad_tail;
						}
					}
				}
				if (ebo->flag & BONE_ROOTSEL) {
					eboflip->head[0] = -ebo->head[0];
					eboflip->head[1] = ebo->head[1];
					eboflip->head[2] = ebo->head[2];
					eboflip->rad_head = ebo->rad_head;
					eboflip->roll = -ebo->roll;
					
					/* Also move connected parent, in case parent's name isn't mirrored properly */
					if (eboflip->parent && eboflip->flag & BONE_CONNECTED) {
						EditBone *parent = eboflip->parent;
						copy_v3_v3(parent->tail, eboflip->head);
						parent->rad_tail = ebo->rad_head;
					}
				}
				if (ebo->flag & BONE_SELECTED) {
					eboflip->dist = ebo->dist;
					eboflip->roll = -ebo->roll;
					eboflip->xwidth = ebo->xwidth;
					eboflip->zwidth = ebo->zwidth;
				}
			}
		}
	}
}


/*****************************************************************************************************/
/*************************************** SKELETON GENERATOR ******************************************/
/*****************************************************************************************************/

#if 0

/**************************************** SUBDIVISION ALGOS ******************************************/

EditBone *subdivideByAngle(Scene *scene, Object *obedit, ReebArc *arc, ReebNode *head, ReebNode *tail)
{
	bArmature *arm = obedit->data;
	EditBone *lastBone = NULL;
	
	if (scene->toolsettings->skgen_options & SKGEN_CUT_ANGLE) {
		ReebArcIterator arc_iter;
		BArcIterator *iter = (BArcIterator *)&arc_iter;
		float *previous = NULL, *current = NULL;
		EditBone *child = NULL;
		EditBone *parent = NULL;
		EditBone *root = NULL;
		float angleLimit = (float)cos(scene->toolsettings->skgen_angle_limit * M_PI / 180.0f);
		
		parent = ED_armature_edit_bone_add(arm, "Bone");
		parent->flag |= BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL;
		copy_v3_v3(parent->head, head->p);
		
		root = parent;
		
		initArcIterator(iter, arc, head);
		IT_next(iter);
		previous = iter->p;
		
		for (IT_next(iter);
		     IT_stopped(iter) == 0;
		     previous = iter->p, IT_next(iter))
		{
			float vec1[3], vec2[3];
			float len1, len2;
			
			current = iter->p;

			sub_v3_v3v3(vec1, previous, parent->head);
			sub_v3_v3v3(vec2, current, previous);

			len1 = normalize_v3(vec1);
			len2 = normalize_v3(vec2);

			if (len1 > 0.0f && len2 > 0.0f && dot_v3v3(vec1, vec2) < angleLimit) {
				copy_v3_v3(parent->tail, previous);

				child = ED_armature_edit_bone_add(arm, "Bone");
				copy_v3_v3(child->head, parent->tail);
				child->parent = parent;
				child->flag |= BONE_CONNECTED | BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL;
				
				parent = child; /* new child is next parent */
			}
		}
		copy_v3_v3(parent->tail, tail->p);
		
		/* If the bone wasn't subdivided, delete it and return NULL
		 * to let subsequent subdivision methods do their thing. 
		 * */
		if (parent == root) {
			if (parent == arm->act_edbone) arm->act_edbone = NULL;
			ED_armature_edit_bone_remove(arm, parent);
			parent = NULL;
		}
		
		lastBone = parent; /* set last bone in the chain */
	}
	
	return lastBone;
}

EditBone *test_subdivideByCorrelation(Scene *scene, Object *obedit, ReebArc *arc, ReebNode *head, ReebNode *tail)
{
	EditBone *lastBone = NULL;

	if (scene->toolsettings->skgen_options & SKGEN_CUT_CORRELATION) {
		float invmat[4][4] = MAT4_UNITY;
		float tmat[3][3] = MAT3_UNITY;
		ReebArcIterator arc_iter;
		BArcIterator *iter = (BArcIterator *)&arc_iter;
		bArmature *arm = obedit->data;
		
		initArcIterator(iter, arc, head);
		
		lastBone = subdivideArcBy(arm, arm->edbo, iter, invmat, tmat, nextAdaptativeSubdivision);
	}
	
	return lastBone;
}

float arcLengthRatio(ReebArc *arc)
{
	float arcLength = 0.0f;
	float embedLength = 0.0f;
	int i;
	
	arcLength = len_v3v3(arc->head->p, arc->tail->p);
	
	if (arc->bcount > 0) {
		/* Add the embedding */
		for (i = 1; i < arc->bcount; i++) {
			embedLength += len_v3v3(arc->buckets[i - 1].p, arc->buckets[i].p);
		}
		/* Add head and tail -> embedding vectors */
		embedLength += len_v3v3(arc->head->p, arc->buckets[0].p);
		embedLength += len_v3v3(arc->tail->p, arc->buckets[arc->bcount - 1].p);
	}
	else {
		embedLength = arcLength;
	}
	
	return embedLength / arcLength;
}

EditBone *test_subdivideByLength(Scene *scene, Object *obedit, ReebArc *arc, ReebNode *head, ReebNode *tail)
{
	EditBone *lastBone = NULL;
	if ((scene->toolsettings->skgen_options & SKGEN_CUT_LENGTH) &&
	    arcLengthRatio(arc) >= G.scene->toolsettings->skgen_length_ratio)
	{
		float invmat[4][4] = MAT4_UNITY;
		float tmat[3][3] = MAT3_UNITY;
		ReebArcIterator arc_iter;
		BArcIterator *iter = (BArcIterator *)&arc_iter;
		bArmature *arm = obedit->data;
		
		initArcIterator(iter, arc, head);
		
		lastBone = subdivideArcBy(arm, arm->edbo, iter, invmat, tmat, nextLengthSubdivision);
	}
	
	return lastBone;
}

/***************************************** MAIN ALGORITHM ********************************************/

void generateSkeletonFromReebGraph(Scene *scene, ReebGraph *rg)
{
	Object *obedit = scene->obedit; // XXX get from context
	GHash *arcBoneMap = NULL;
	ReebArc *arc = NULL;
	ReebNode *node = NULL;
	Object *src = NULL;
	Object *dst = NULL;
	
	src = scene->basact->object;
	
	if (obedit != NULL) {
		ED_armature_from_edit(obedit);
		ED_armature_edit_free(obedit);
	}
	
	dst = BKE_object_add(scene, OB_ARMATURE);
	ED_object_base_init_transform(NULL, scene->basact, NULL, NULL);     // XXX NULL is C, loc, rot
	obedit = scene->basact->object;
	
	/* Copy orientation from source */
	copy_v3_v3(dst->loc, src->obmat[3]);
	mat4_to_eul(dst->rot, src->obmat);
	mat4_to_size(dst->size, src->obmat);
	
	BKE_object_where_is_calc(scene, obedit);
	
	ED_armature_to_edit(obedit);

	arcBoneMap = BLI_ghash_ptr_new("SkeletonFromReebGraph gh");
	
	BLI_markdownSymmetry((BGraph *)rg, rg->nodes.first, scene->toolsettings->skgen_symmetry_limit);
	
	for (arc = rg->arcs.first; arc; arc = arc->next) 
	{
		EditBone *lastBone = NULL;
		ReebNode *head, *tail;
		int i;

		/* Find out the direction of the arc through simple heuristics (in order of priority) :
		 * 
		 * 1- Arcs on primary symmetry axis (symmetry == 1) point up (head: high weight -> tail: low weight)
		 * 2- Arcs starting on a primary axis point away from it (head: node on primary axis)
		 * 3- Arcs point down (head: low weight -> tail: high weight)
		 *
		 * Finally, the arc direction is stored in its flag: 1 (low -> high), -1 (high -> low)
		 */

		/* if arc is a symmetry axis, internal bones go up the tree */
		if (arc->symmetry_level == 1 && arc->tail->degree != 1) {
			head = arc->tail;
			tail = arc->head;
			
			arc->flag = -1; /* mark arc direction */
		}
		/* Bones point AWAY from the symmetry axis */
		else if (arc->head->symmetry_level == 1) {
			head = arc->head;
			tail = arc->tail;
			
			arc->flag = 1; /* mark arc direction */
		}
		else if (arc->tail->symmetry_level == 1) {
			head = arc->tail;
			tail = arc->head;
			
			arc->flag = -1; /* mark arc direction */
		}
		/* otherwise, always go from low weight to high weight */
		else {
			head = arc->head;
			tail = arc->tail;
			
			arc->flag = 1; /* mark arc direction */
		}
		
		/* Loop over subdivision methods */
		for (i = 0; lastBone == NULL && i < SKGEN_SUB_TOTAL; i++) {
			switch (scene->toolsettings->skgen_subdivisions[i]) {
				case SKGEN_SUB_LENGTH:
					lastBone = test_subdivideByLength(scene, obedit, arc, head, tail);
					break;
				case SKGEN_SUB_ANGLE:
					lastBone = subdivideByAngle(scene, obedit, arc, head, tail);
					break;
				case SKGEN_SUB_CORRELATION:
					lastBone = test_subdivideByCorrelation(scene, obedit, arc, head, tail);
					break;
			}
		}
	
		if (lastBone == NULL) {
			EditBone    *bone;
			bone = ED_armature_edit_bone_add(obedit->data, "Bone");
			bone->flag |= BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL;
			
			copy_v3_v3(bone->head, head->p);
			copy_v3_v3(bone->tail, tail->p);
			
			/* set first and last bone, since there's only one */
			lastBone = bone;
		}
		
		BLI_ghash_insert(arcBoneMap, arc, lastBone);
	}

	/* Second pass, setup parent relationship between arcs */
	for (node = rg->nodes.first; node; node = node->next)
	{
		ReebArc *incomingArc = NULL;
		int i;

		for (i = 0; i < node->degree; i++) {
			arc = (ReebArc *)node->arcs[i];

			/* if arc is incoming into the node */
			if ((arc->head == node && arc->flag == -1) ||
			    (arc->tail == node && arc->flag ==  1))
			{
				if (incomingArc == NULL) {
					incomingArc = arc;
					/* loop further to make sure there's only one incoming arc */
				}
				else {
					/* skip this node if more than one incomingArc */
					incomingArc = NULL;
					break; /* No need to look further, we are skipping already */
				}
			}
		}

		if (incomingArc != NULL) {
			EditBone *parentBone = BLI_ghash_lookup(arcBoneMap, incomingArc);

			/* Look for outgoing arcs and parent their bones */
			for (i = 0; i < node->degree; i++)
			{
				arc = node->arcs[i];

				/* if arc is outgoing from the node */
				if ((arc->head == node && arc->flag == 1) || (arc->tail == node && arc->flag == -1)) {
					EditBone *childBone = BLI_ghash_lookup(arcBoneMap, arc);

					/* find the root bone */
					while (childBone->parent != NULL)
					{
						childBone = childBone->parent;
					}

					childBone->parent = parentBone;
					childBone->flag |= BONE_CONNECTED;
				}
			}
		}
	}
	
	BLI_ghash_free(arcBoneMap, NULL, NULL);
}

void generateSkeleton(Scene *scene)
{
	ReebGraph *reebg;
	
//	setcursor_space(SPACE_VIEW3D, CURSOR_WAIT);
	
	reebg = BIF_ReebGraphFromEditMesh();

	generateSkeletonFromReebGraph(scene, reebg);

	REEB_freeGraph(reebg);

	//setcursor_space(SPACE_VIEW3D, CURSOR_EDIT);
}

#endif
