/**
 * $Id$
 *
 * ***** 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-2008 full recode
 *
 * ***** END GPL LICENSE BLOCK *****
 */

#include <stdlib.h>
#include <string.h>

#include "MEM_guardedalloc.h"

#include "BLI_math.h"
#include "BLI_editVert.h"
#include "BLI_listbase.h"
#include "BLI_string.h"
#include "BLI_utildefines.h"

#include "DNA_curve_types.h"
#include "DNA_lattice_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"

#include "BKE_action.h"
#include "BKE_context.h"
#include "BKE_depsgraph.h"
#include "BKE_main.h"
#include "BKE_mesh.h"
#include "BKE_modifier.h"
#include "BKE_object.h"
#include "BKE_report.h"
#include "BKE_scene.h"

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

#include "ED_curve.h"
#include "ED_mesh.h"
#include "ED_screen.h"

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

#include "UI_resources.h"

#include "object_intern.h"

static int return_editmesh_indexar(EditMesh *em, int *tot, int **indexar, float *cent)
{
	EditVert *eve;
	int *index, nr, totvert=0;
	
	for(eve= em->verts.first; eve; eve= eve->next) {
		if(eve->f & SELECT) totvert++;
	}
	if(totvert==0) return 0;
	
	*indexar= index= MEM_mallocN(4*totvert, "hook indexar");
	*tot= totvert;
	nr= 0;
	cent[0]= cent[1]= cent[2]= 0.0;
	
	for(eve= em->verts.first; eve; eve= eve->next) {
		if(eve->f & SELECT) {
			*index= nr; index++;
			add_v3_v3(cent, eve->co);
		}
		nr++;
	}
	
	mul_v3_fl(cent, 1.0f/(float)totvert);
	
	return totvert;
}

static int return_editmesh_vgroup(Object *obedit, EditMesh *em, char *name, float *cent)
{
	MDeformVert *dvert;
	EditVert *eve;
	int i, totvert=0;
	
	cent[0]= cent[1]= cent[2]= 0.0;
	
	if(obedit->actdef) {
		
		/* find the vertices */
		for(eve= em->verts.first; eve; eve= eve->next) {
			dvert= CustomData_em_get(&em->vdata, eve->data, CD_MDEFORMVERT);

			if(dvert) {
				for(i=0; i<dvert->totweight; i++){
					if(dvert->dw[i].def_nr == (obedit->actdef-1)) {
						totvert++;
						add_v3_v3(cent, eve->co);
					}
				}
			}
		}
		if(totvert) {
			bDeformGroup *defGroup = BLI_findlink(&obedit->defbase, obedit->actdef-1);
			strcpy(name, defGroup->name);
			mul_v3_fl(cent, 1.0f/(float)totvert);
			return 1;
		}
	}
	
	return 0;
}	

static void select_editmesh_hook(Object *ob, HookModifierData *hmd)
{
	Mesh *me= ob->data;
	EditMesh *em= BKE_mesh_get_editmesh(me);
	EditVert *eve;
	int index=0, nr=0;
	
	if (hmd->indexar == NULL)
		return;
	
	for(eve= em->verts.first; eve; eve= eve->next, nr++) {
		if(nr==hmd->indexar[index]) {
			eve->f |= SELECT;
			if(index < hmd->totindex-1) index++;
		}
	}
	EM_select_flush(em);

	BKE_mesh_end_editmesh(me, em);
}

static int return_editlattice_indexar(Lattice *editlatt, int *tot, int **indexar, float *cent)
{
	BPoint *bp;
	int *index, nr, totvert=0, a;
	
	/* count */
	a= editlatt->pntsu*editlatt->pntsv*editlatt->pntsw;
	bp= editlatt->def;
	while(a--) {
		if(bp->f1 & SELECT) {
			if(bp->hide==0) totvert++;
		}
		bp++;
	}

	if(totvert==0) return 0;
	
	*indexar= index= MEM_mallocN(4*totvert, "hook indexar");
	*tot= totvert;
	nr= 0;
	cent[0]= cent[1]= cent[2]= 0.0;
	
	a= editlatt->pntsu*editlatt->pntsv*editlatt->pntsw;
	bp= editlatt->def;
	while(a--) {
		if(bp->f1 & SELECT) {
			if(bp->hide==0) {
				*index= nr; index++;
				add_v3_v3(cent, bp->vec);
			}
		}
		bp++;
		nr++;
	}
	
	mul_v3_fl(cent, 1.0f/(float)totvert);
	
	return totvert;
}

static void select_editlattice_hook(Object *obedit, HookModifierData *hmd)
{
	Lattice *lt= obedit->data, *editlt;
	BPoint *bp;
	int index=0, nr=0, a;

	editlt= lt->editlatt->latt;
	/* count */
	a= editlt->pntsu*editlt->pntsv*editlt->pntsw;
	bp= editlt->def;
	while(a--) {
		if(hmd->indexar[index]==nr) {
			bp->f1 |= SELECT;
			if(index < hmd->totindex-1) index++;
		}
		nr++;
		bp++;
	}
}

static int return_editcurve_indexar(Object *obedit, int *tot, int **indexar, float *cent)
{
	ListBase *editnurb= curve_get_editcurve(obedit);
	Nurb *nu;
	BPoint *bp;
	BezTriple *bezt;
	int *index, a, nr, totvert=0;
	
	for(nu= editnurb->first; nu; nu= nu->next) {
		if(nu->type == CU_BEZIER) {
			bezt= nu->bezt;
			a= nu->pntsu;
			while(a--) {
				if(bezt->f1 & SELECT) totvert++;
				if(bezt->f2 & SELECT) totvert++;
				if(bezt->f3 & SELECT) totvert++;
				bezt++;
			}
		}
		else {
			bp= nu->bp;
			a= nu->pntsu*nu->pntsv;
			while(a--) {
				if(bp->f1 & SELECT) totvert++;
				bp++;
			}
		}
	}
	if(totvert==0) return 0;
	
	*indexar= index= MEM_mallocN(4*totvert, "hook indexar");
	*tot= totvert;
	nr= 0;
	cent[0]= cent[1]= cent[2]= 0.0;
	
	for(nu= editnurb->first; nu; nu= nu->next) {
		if(nu->type == CU_BEZIER) {
			bezt= nu->bezt;
			a= nu->pntsu;
			while(a--) {
				if(bezt->f1 & SELECT) {
					*index= nr; index++;
					add_v3_v3(cent, bezt->vec[0]);
				}
				nr++;
				if(bezt->f2 & SELECT) {
					*index= nr; index++;
					add_v3_v3(cent, bezt->vec[1]);
				}
				nr++;
				if(bezt->f3 & SELECT) {
					*index= nr; index++;
					add_v3_v3(cent, bezt->vec[2]);
				}
				nr++;
				bezt++;
			}
		}
		else {
			bp= nu->bp;
			a= nu->pntsu*nu->pntsv;
			while(a--) {
				if(bp->f1 & SELECT) {
					*index= nr; index++;
					add_v3_v3(cent, bp->vec);
				}
				nr++;
				bp++;
			}
		}
	}
	
	mul_v3_fl(cent, 1.0f/(float)totvert);
	
	return totvert;
}

static int object_hook_index_array(Object *obedit, int *tot, int **indexar, char *name, float *cent_r)
{
	*indexar= NULL;
	*tot= 0;
	name[0]= 0;
	
	switch(obedit->type) {
		case OB_MESH:
		{
			Mesh *me= obedit->data;
			EditMesh *em = BKE_mesh_get_editmesh(me);

			/* check selected vertices first */
			if( return_editmesh_indexar(em, tot, indexar, cent_r)) {
				BKE_mesh_end_editmesh(me, em);
				return 1;
			} else {
				int ret = return_editmesh_vgroup(obedit, em, name, cent_r);
				BKE_mesh_end_editmesh(me, em);
				return ret;
			}
		}
		case OB_CURVE:
		case OB_SURF:
			return return_editcurve_indexar(obedit, tot, indexar, cent_r);
		case OB_LATTICE:
		{
			Lattice *lt= obedit->data;
			return return_editlattice_indexar(lt->editlatt->latt, tot, indexar, cent_r);
		}
		default:
			return 0;
	}
}

static void select_editcurve_hook(Object *obedit, HookModifierData *hmd)
{
	ListBase *editnurb= curve_get_editcurve(obedit);
	Nurb *nu;
	BPoint *bp;
	BezTriple *bezt;
	int index=0, a, nr=0;
	
	for(nu= editnurb->first; nu; nu= nu->next) {
		if(nu->type == CU_BEZIER) {
			bezt= nu->bezt;
			a= nu->pntsu;
			while(a--) {
				if(nr == hmd->indexar[index]) {
					bezt->f1 |= SELECT;
					if(index<hmd->totindex-1) index++;
				}
				nr++;
				if(nr == hmd->indexar[index]) {
					bezt->f2 |= SELECT;
					if(index<hmd->totindex-1) index++;
				}
				nr++;
				if(nr == hmd->indexar[index]) {
					bezt->f3 |= SELECT;
					if(index<hmd->totindex-1) index++;
				}
				nr++;
				
				bezt++;
			}
		}
		else {
			bp= nu->bp;
			a= nu->pntsu*nu->pntsv;
			while(a--) {
				if(nr == hmd->indexar[index]) {
					bp->f1 |= SELECT;
					if(index<hmd->totindex-1) index++;
				}
				nr++;
				bp++;
			}
		}
	}
}

static void object_hook_select(Object *ob, HookModifierData *hmd) 
{
	if (hmd->indexar == NULL)
		return;
	
	if(ob->type==OB_MESH) select_editmesh_hook(ob, hmd);
	else if(ob->type==OB_LATTICE) select_editlattice_hook(ob, hmd);
	else if(ob->type==OB_CURVE) select_editcurve_hook(ob, hmd);
	else if(ob->type==OB_SURF) select_editcurve_hook(ob, hmd);
}

/* special poll operators for hook operators */
// TODO: check for properties window modifier context too as alternative?
static int hook_op_edit_poll(bContext *C)
{
	Object *obedit= CTX_data_edit_object(C);
	
	if (obedit) {
		if (ED_operator_editmesh(C)) return 1;
		if (ED_operator_editsurfcurve(C)) return 1;
		if (ED_operator_editlattice(C)) return 1;
		//if (ED_operator_editmball(C)) return 1;
	}
	
	return 0;
}

static Object *add_hook_object_new(Scene *scene, Object *obedit)
{
	Base *base, *basedit;
	Object *ob;

	ob= add_object(scene, OB_EMPTY);
	
	basedit = object_in_scene(obedit, scene);
	base = object_in_scene(ob, scene);
	base->lay = ob->lay = obedit->lay;
	
	/* icky, add_object sets new base as active.
	 * so set it back to the original edit object */
	scene->basact = basedit;

	return ob;
}

static void add_hook_object(Main *bmain, Scene *scene, Object *obedit, Object *ob, int mode)
{
	ModifierData *md=NULL;
	HookModifierData *hmd = NULL;
	float cent[3];
	int tot, ok, *indexar;
	char name[32];
	
	ok = object_hook_index_array(obedit, &tot, &indexar, name, cent);
	
	if (!ok) return;	// XXX error("Requires selected vertices or active Vertex Group");
	
	if (mode==OBJECT_ADDHOOK_NEWOB && !ob) {
		
		ob = add_hook_object_new(scene, obedit);
		
		/* transform cent to global coords for loc */
		mul_v3_m4v3(ob->loc, obedit->obmat, cent);
	}
	
	md = obedit->modifiers.first;
	while (md && modifierType_getInfo(md->type)->type==eModifierTypeType_OnlyDeform) {
		md = md->next;
	}
	
	hmd = (HookModifierData*) modifier_new(eModifierType_Hook);
	BLI_insertlinkbefore(&obedit->modifiers, md, hmd);
	BLI_snprintf(hmd->modifier.name, sizeof(hmd->modifier.name), "Hook-%s", ob->id.name+2);
	modifier_unique_name(&obedit->modifiers, (ModifierData*)hmd);
	
	hmd->object= ob;
	hmd->indexar= indexar;
	copy_v3_v3(hmd->cent, cent);
	hmd->totindex= tot;
	BLI_strncpy(hmd->name, name, sizeof(hmd->name));
	
	/* matrix calculus */
	/* vert x (obmat x hook->imat) x hook->obmat x ob->imat */
	/*        (parentinv         )                          */
	where_is_object(scene, ob);
	
	invert_m4_m4(ob->imat, ob->obmat);
	/* apparently this call goes from right to left... */
	mul_serie_m4(hmd->parentinv, ob->imat, obedit->obmat, NULL, 
				 NULL, NULL, NULL, NULL, NULL);
	
	DAG_scene_sort(bmain, scene);
}

static int object_add_hook_selob_exec(bContext *C, wmOperator *op)
{
	Main *bmain= CTX_data_main(C);
	Scene *scene= CTX_data_scene(C);
	Object *obedit = CTX_data_edit_object(C);
	Object *obsel=NULL;
	
	CTX_DATA_BEGIN(C, Object*, ob, selected_objects)
	{
		if (ob != obedit) {
			obsel = ob;
			break;
		}
	}
	CTX_DATA_END;
	
	if (!obsel) {
		BKE_report(op->reports, RPT_ERROR, "Can't add hook with no other selected objects.");
		return OPERATOR_CANCELLED;
	}
	
	add_hook_object(bmain, scene, obedit, obsel, OBJECT_ADDHOOK_SELOB);
	
	WM_event_add_notifier(C, NC_OBJECT|ND_MODIFIER, obedit);
	return OPERATOR_FINISHED;
}

void OBJECT_OT_hook_add_selobj(wmOperatorType *ot)
{
	/* identifiers */
	ot->name= "Hook to Selected Object";
	ot->description= "Hook selected vertices to the first selected Object";
	ot->idname= "OBJECT_OT_hook_add_selob";
	
	/* api callbacks */
	ot->exec= object_add_hook_selob_exec;
	ot->poll= hook_op_edit_poll;
	
	/* flags */
	ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
}

static int object_add_hook_newob_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);

	add_hook_object(bmain, scene, obedit, NULL, OBJECT_ADDHOOK_NEWOB);
	
	WM_event_add_notifier(C, NC_SCENE|ND_OB_SELECT, scene);
	WM_event_add_notifier(C, NC_OBJECT|ND_MODIFIER, obedit);
	return OPERATOR_FINISHED;
}

void OBJECT_OT_hook_add_newobj(wmOperatorType *ot)
{
	/* identifiers */
	ot->name= "Hook to New Object";
	ot->description= "Hook selected vertices to the first selected Object";
	ot->idname= "OBJECT_OT_hook_add_newob";
	
	/* api callbacks */
	ot->exec= object_add_hook_newob_exec;
	ot->poll= hook_op_edit_poll;
	
	/* flags */
	ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
}

static int object_hook_remove_exec(bContext *C, wmOperator *op)
{
	int num= RNA_enum_get(op->ptr, "modifier");
	Object *ob=NULL;
	HookModifierData *hmd=NULL;

	ob = CTX_data_edit_object(C);
	hmd = (HookModifierData *)BLI_findlink(&ob->modifiers, num);

	if (!ob || !hmd) {
		BKE_report(op->reports, RPT_ERROR, "Couldn't find hook modifier");
		return OPERATOR_CANCELLED;
	}
	
	/* remove functionality */
	
	BLI_remlink(&ob->modifiers, (ModifierData *)hmd);
	modifier_free((ModifierData *)hmd);
	
	DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
	WM_event_add_notifier(C, NC_OBJECT|ND_MODIFIER, ob);
	
	return OPERATOR_FINISHED;
}

static EnumPropertyItem *hook_mod_itemf(bContext *C, PointerRNA *UNUSED(ptr), int *free)
{	
	Object *ob = CTX_data_edit_object(C);
	EnumPropertyItem tmp = {0, "", 0, "", ""};
	EnumPropertyItem *item= NULL;
	ModifierData *md = NULL;
	int a, totitem= 0;
	
	if(!ob)
		return DummyRNA_NULL_items;
	
	for(a=0, md=ob->modifiers.first; md; md= md->next, a++) {
		if (md->type==eModifierType_Hook) {
			tmp.value= a;
			tmp.icon = ICON_HOOK;
			tmp.identifier= md->name;
			tmp.name= md->name;
			RNA_enum_item_add(&item, &totitem, &tmp);
		}
	}
	
	RNA_enum_item_end(&item, &totitem);
	*free= 1;
	
	return item;
}

void OBJECT_OT_hook_remove(wmOperatorType *ot)
{
	PropertyRNA *prop;
	
	/* identifiers */
	ot->name= "Remove Hook";
	ot->idname= "OBJECT_OT_hook_remove";
	ot->description= "Remove a hook from the active object";
	
	/* api callbacks */
	ot->exec= object_hook_remove_exec;
	ot->invoke= WM_menu_invoke;
	ot->poll= hook_op_edit_poll;
	
	/* flags */
	ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
	
	/* properties */
	prop= RNA_def_enum(ot->srna, "modifier", DummyRNA_NULL_items, 0, "Modifier", "Modifier number to remove.");
	RNA_def_enum_funcs(prop, hook_mod_itemf);
	ot->prop= prop;
}

static int object_hook_reset_exec(bContext *C, wmOperator *op)
{
	PointerRNA ptr= CTX_data_pointer_get_type(C, "modifier", &RNA_HookModifier);
	int num= RNA_enum_get(op->ptr, "modifier");
	Object *ob=NULL;
	HookModifierData *hmd=NULL;
	
	if (ptr.data) {		/* if modifier context is available, use that */
		ob = ptr.id.data;
		hmd= ptr.data;
	} 
	else {			/* use the provided property */
		ob = CTX_data_edit_object(C);
		hmd = (HookModifierData *)BLI_findlink(&ob->modifiers, num);
	}
	if (!ob || !hmd) {
		BKE_report(op->reports, RPT_ERROR, "Couldn't find hook modifier");
		return OPERATOR_CANCELLED;
	}
	
	/* reset functionality */
	if(hmd->object) {
		bPoseChannel *pchan= get_pose_channel(hmd->object->pose, hmd->subtarget);
		
		if(hmd->subtarget[0] && pchan) {
			float imat[4][4], mat[4][4];
			
			/* calculate the world-space matrix for the pose-channel target first, then carry on as usual */
			mul_m4_m4m4(mat, pchan->pose_mat, hmd->object->obmat);
			
			invert_m4_m4(imat, mat);
			mul_serie_m4(hmd->parentinv, imat, ob->obmat, NULL, NULL, NULL, NULL, NULL, NULL);
		}
		else {
			invert_m4_m4(hmd->object->imat, hmd->object->obmat);
			mul_serie_m4(hmd->parentinv, hmd->object->imat, ob->obmat, NULL, NULL, NULL, NULL, NULL, NULL);
		}
	}
	
	DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
	WM_event_add_notifier(C, NC_OBJECT|ND_MODIFIER, ob);
	
	return OPERATOR_FINISHED;
}

void OBJECT_OT_hook_reset(wmOperatorType *ot)
{
	PropertyRNA *prop;
	
	/* identifiers */
	ot->name= "Reset Hook";
	ot->description= "Recalculate and clear offset transformation";
	ot->idname= "OBJECT_OT_hook_reset";
	
	/* callbacks */
	ot->exec= object_hook_reset_exec;
	ot->poll= hook_op_edit_poll;
	
	/* flags */
	ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
	
	/* properties */
	prop= RNA_def_enum(ot->srna, "modifier", DummyRNA_NULL_items, 0, "Modifier", "Modifier number to assign to.");
	RNA_def_enum_funcs(prop, hook_mod_itemf);
}

static int object_hook_recenter_exec(bContext *C, wmOperator *op)
{
	PointerRNA ptr= CTX_data_pointer_get_type(C, "modifier", &RNA_HookModifier);
	int num= RNA_enum_get(op->ptr, "modifier");
	Object *ob=NULL;
	HookModifierData *hmd=NULL;
	Scene *scene = CTX_data_scene(C);
	float bmat[3][3], imat[3][3];
	
	if (ptr.data) {		/* if modifier context is available, use that */
		ob = ptr.id.data;
		hmd= ptr.data;
	} 
	else {			/* use the provided property */
		ob = CTX_data_edit_object(C);
		hmd = (HookModifierData *)BLI_findlink(&ob->modifiers, num);
	}
	if (!ob || !hmd) {
		BKE_report(op->reports, RPT_ERROR, "Couldn't find hook modifier");
		return OPERATOR_CANCELLED;
	}
	
	/* recenter functionality */
	copy_m3_m4(bmat, ob->obmat);
	invert_m3_m3(imat, bmat);
	
	sub_v3_v3v3(hmd->cent, scene->cursor, ob->obmat[3]);
	mul_m3_v3(imat, hmd->cent);
	
	DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
	WM_event_add_notifier(C, NC_OBJECT|ND_MODIFIER, ob);
	
	return OPERATOR_FINISHED;
}

void OBJECT_OT_hook_recenter(wmOperatorType *ot)
{
	PropertyRNA *prop;
	
	/* identifiers */
	ot->name= "Recenter Hook";
	ot->description= "Set hook center to cursor position";
	ot->idname= "OBJECT_OT_hook_recenter";
	
	/* callbacks */
	ot->exec= object_hook_recenter_exec;
	ot->poll= hook_op_edit_poll;
	
	/* flags */
	ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
	
	/* properties */
	prop= RNA_def_enum(ot->srna, "modifier", DummyRNA_NULL_items, 0, "Modifier", "Modifier number to assign to.");
	RNA_def_enum_funcs(prop, hook_mod_itemf);
}

static int object_hook_assign_exec(bContext *C, wmOperator *op)
{
	PointerRNA ptr= CTX_data_pointer_get_type(C, "modifier", &RNA_HookModifier);
	int num= RNA_enum_get(op->ptr, "modifier");
	Object *ob=NULL;
	HookModifierData *hmd=NULL;
	float cent[3];
	char name[32];
	int *indexar, tot;
	
	if (ptr.data) {		/* if modifier context is available, use that */
		ob = ptr.id.data;
		hmd= ptr.data;
	} 
	else {			/* use the provided property */
		ob = CTX_data_edit_object(C);
		hmd = (HookModifierData *)BLI_findlink(&ob->modifiers, num);
	}
	if (!ob || !hmd) {
		BKE_report(op->reports, RPT_ERROR, "Couldn't find hook modifier");
		return OPERATOR_CANCELLED;
	}
	
	/* assign functionality */
	
	if(!object_hook_index_array(ob, &tot, &indexar, name, cent)) {
		BKE_report(op->reports, RPT_WARNING, "Requires selected vertices or active vertex group");
		return OPERATOR_CANCELLED;
	}
	if(hmd->indexar)
		MEM_freeN(hmd->indexar);
	
	copy_v3_v3(hmd->cent, cent);
	hmd->indexar= indexar;
	hmd->totindex= tot;
	
	DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
	WM_event_add_notifier(C, NC_OBJECT|ND_MODIFIER, ob);
	
	return OPERATOR_FINISHED;
}

void OBJECT_OT_hook_assign(wmOperatorType *ot)
{
	PropertyRNA *prop;
	
	/* identifiers */
	ot->name= "Assign to Hook";
	ot->description= "Assign the selected vertices to a hook";
	ot->idname= "OBJECT_OT_hook_assign";
	
	/* callbacks */
	ot->exec= object_hook_assign_exec;
	ot->poll= hook_op_edit_poll;
	
	/* flags */
	ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
	
	/* properties */
	prop= RNA_def_enum(ot->srna, "modifier", DummyRNA_NULL_items, 0, "Modifier", "Modifier number to assign to.");
	RNA_def_enum_funcs(prop, hook_mod_itemf);
}

static int object_hook_select_exec(bContext *C, wmOperator *op)
{
	PointerRNA ptr= CTX_data_pointer_get_type(C, "modifier", &RNA_HookModifier);
	int num= RNA_enum_get(op->ptr, "modifier");
	Object *ob=NULL;
	HookModifierData *hmd=NULL;
	
	if (ptr.data) {		/* if modifier context is available, use that */
		ob = ptr.id.data;
		hmd= ptr.data;
	} 
	else {			/* use the provided property */
		ob = CTX_data_edit_object(C);
		hmd = (HookModifierData *)BLI_findlink(&ob->modifiers, num);
	}
	if (!ob || !hmd) {
		BKE_report(op->reports, RPT_ERROR, "Couldn't find hook modifier");
		return OPERATOR_CANCELLED;
	}
	
	/* select functionality */
	object_hook_select(ob, hmd);
	
	WM_event_add_notifier(C, NC_GEOM|ND_SELECT, ob->data);
	
	return OPERATOR_FINISHED;
}

void OBJECT_OT_hook_select(wmOperatorType *ot)
{
	PropertyRNA *prop;
	
	/* identifiers */
	ot->name= "Select Hook";
	ot->description= "Selects effected vertices on mesh";
	ot->idname= "OBJECT_OT_hook_select";
	
	/* callbacks */
	ot->exec= object_hook_select_exec;
	ot->poll= hook_op_edit_poll;
	
	/* flags */
	ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
	
	/* properties */
	prop= RNA_def_enum(ot->srna, "modifier", DummyRNA_NULL_items, 0, "Modifier", "Modifier number to remove.");
	RNA_def_enum_funcs(prop, hook_mod_itemf);
}

