/**
 * File ng_handles.c
 * Contains intermediate level function to manage handles
 * for objects by calling the low level one gg_handles in gg_utils.c
 */

#include <math.h>

#include "globals.h"
#include "nn_tree.h"

#include "ng_objects.h"

// pour deboguer avec ppp
extern void ppp (char *s);


/* the maximum distance away from an object
 * you may be when picking it
 * (in viewport  coordinates) */
/* for objects */
#define NG_MAXPICKDIST 0.015


extern  void gg_handles (view v ,int nh ,int center);
extern  void gg_one_handle (VPoint vp);
extern graph *g;

static void draw_legend_handles (int i)
{
  if (g != NULL) {
    if (g[i].l.active == FALSE || g[i].l.handle_on == TRUE) return;
    gg_handles (g[i].l.bb ,4 ,FALSE);
    g[i].l.handle_on = TRUE;
  }
}

static void clear_legend_handles (int i)
{
  if (g != NULL) {
    if (g[i].l.active == FALSE || g[i].l.handle_on == FALSE) return;
    gg_handles (g[i].l.bb ,4 ,FALSE);
    g[i].l.handle_on = FALSE;
  }
}

static void draw_clear_obj_handles (int i ,QDobject *po ,int sw)
{
  int center ,j;
  VPoint vp0 ,vp;
  view v;
  if (po->active    == FALSE || po->id < 0) return;
  if (po->handle_on == sw) return;
  obj_get_shift (i ,&vp0);
  if (po->pmask & MA_N_HANDLES) {    /* for Q_Polyline */
    if (po->x != NULL) {
      for (j = 0; j < po->nxy; j++) {
	obj_polyline_get_vp (i ,j ,&vp);
	vp.x += vp0.x;
	vp.y += vp0.y;
	gg_one_handle (vp);
      }
    }
  } else if (po->pmask & MA_2_HANDLES) {    /* for Q_Line  */
    if (po->loctyp == COORD_WORLD) {
      world2view (po->x1 ,po->y1 ,&v.xv1 ,&v.yv1);
      world2view (po->x2 ,po->y2 ,&v.xv2 ,&v.yv2);
    } else {
      v.xv1 = po->x1;
      v.yv1 = po->y1;
      v.xv2 = po->x2;
      v.yv2 = po->y2;
    }
    v.xv1 += vp0.x;
    v.yv1 += vp0.y;
    v.xv2 += vp0.x;
    v.yv2 += vp0.y;
    gg_handles (v ,2 ,FALSE);
  } else {               /* nh==4 use bounding box */
    center =  po->pmask & MA_CENTERED;
    gg_handles (po->bb ,4 ,center);
  }
  po->handle_on = sw;
}


void draw_obj_handles (int i)
{
  draw_clear_obj_handles (i ,&objs[i] ,TRUE);
}

void clear_obj_handles (int i)
{
  int gno;
  QDobject *po = &objs[i];
  if (po->typ == Q_LegendBox) {
    gno = po->father_id;
    clear_legend_handles (gno);
  } else {
  draw_clear_obj_handles (i ,po ,FALSE);
  }
}

void draw_typed_handles (Qtype typ)
{
  int i;
  for (i = 0; i < objs_max (); i++) {
    if (objs[i].active == TRUE && objs[i].typ == typ) {
      draw_obj_handles (i);
    }
  }
}

void clear_typed_handles (Qtype typ)
{
  int i;
  for (i = 0; i < objs_max (); i++) {
    if (objs[i].active == TRUE && objs[i].typ == typ) {
      clear_obj_handles (i);
    }
  }
}


void draw_curobj_handles (void)
{
  if (cur_obj_typ == Q_LegendBox) {
    draw_legend_handles (cur_parent_id);
  } else {
    draw_obj_handles (cur_obj_num);
  }
}

static void ng_draw_handles (int i ,int depth)
{
  switch (objs[i].typ) {
  case Q_Arc:
  case Q_Box:
  case Q_Colorbar:
  case Q_Line:
  case Q_Polyline:
  case Q_String:
  case Q_TimeStamp:
    draw_obj_handles (i);
    break;
  case Q_LegendBox:
    draw_legend_handles (objs[i].father_id);
    break;
  case Q_Compound:
    draw_obj_handles (i);
    nn_traverse_stop (TRUE);
    break;
  default :
    break;
  }
}

void draw_branch_handles (int i)
{
  nn_traverse_stop (FALSE);
  nn_traverse_child_first (i ,ng_draw_handles ,NULL);
}

void clear_all_handles  (void)
{
  int i;
  for (i = 0; i < number_of_graphs(); i++) {
    clear_legend_handles (i);
  }
  for (i = iTS; i < objs_max (); i++) {
    clear_obj_handles (i);
  }
}

double dist_vp_xy (VPoint vp ,double x ,double y)
{
  double dx = vp.x - x;
  double dy = vp.y - y;
  return (sqrt (dx*dx + dy*dy));
}



/**
 *  find_one_obj_handle : find handle of objs[i]
 *       vp is the point to test
 * 	 nh is the number of handles (2 or 4)
 *       returns the handle id (see below)
 * 	 if nh=4,  the opposite corner as anchor_vp
 * 	           or the center if MA_CENTERED
 *  return handle id: 0: not found, 1:(x1,y1) , 2:(x2,y2) , 3:(x2,y1) , 4:(x1,y2)
 * 	 if nh=2 anchor_vp is the opposite point
 */
static int find_one_obj_handle (VPoint vp ,int i ,int nh ,VPoint *anchor_vp)
{
  view bb ,v;
  double x ,y ,xop ,yop;
  VPoint vp0;
  int retval;
  if (nh == 4) {
    bb = objs[i].bb;
    if (dist_vp_xy (vp ,bb.xv1 ,bb.yv1) < NG_MAXPICKDIST) {
      x   = bb.xv1;
      y   = bb.yv1;
      xop = bb.xv2;
      yop = bb.yv2;
      retval = 1;
    } else if (dist_vp_xy (vp  ,bb.xv2 ,bb.yv2) < NG_MAXPICKDIST) {
      x   = bb.xv2;
      y   = bb.yv2;
      xop = bb.xv1;
      yop = bb.yv1;
      retval = 2;
    } else if (dist_vp_xy (vp ,bb.xv2 ,bb.yv1) < NG_MAXPICKDIST) {
      x   = bb.xv2;
      y   = bb.yv1;
      xop = bb.xv1;
      yop = bb.yv2;
      retval = 3;
    } else if (dist_vp_xy (vp ,bb.xv1 ,bb.yv2) < NG_MAXPICKDIST) {
      x   = bb.xv1;
      y   = bb.yv2;
      xop = bb.xv2;
      yop = bb.yv1;
      retval = 4;
    } else {
      return 0;
    }
    if (objs[i].pmask & MA_CENTERED) {
      xop = 0.5 * (x + xop);
      yop = 0.5 * (y + yop);
    }
    anchor_vp->x = xop;
    anchor_vp->y = yop;
  } else {                      /* nh==2 don't use bounding box */
    QDobject *po = &objs[i];
    if (po->loctyp == COORD_WORLD) {
      world2view (po->x1 ,po->y1 ,&v.xv1 ,&v.yv1);
      world2view (po->x2 ,po->y2 ,&v.xv2 ,&v.yv2);
    } else {
      v.xv1 = po->x1;
      v.yv1 = po->y1;
      v.xv2 = po->x2;
      v.yv2 = po->y2;
    }
    obj_get_shift (i ,&vp0);
    v.xv1 += vp0.x;
    v.yv1 += vp0.y;
    v.xv2 += vp0.x;
    v.yv2 += vp0.y;
    if (dist_vp_xy (vp ,v.xv1 ,v.yv1) < NG_MAXPICKDIST) {
      anchor_vp->x = v.xv2;
      anchor_vp->y = v.yv2;
      retval = 1;
    } else if (dist_vp_xy (vp ,v.xv2 ,v.yv2) < NG_MAXPICKDIST) {
      anchor_vp->x = v.xv1;
      anchor_vp->y = v.yv1;
      retval = 2;
    } else {
      return 0;
    }
  }
  return retval;
}

/**
 *  find_handle : returns the handle number if "vp" is onto one handle and
 *      makes the object current, returns his bounding box "bb"
 *      and the opposite corner or center in "anchor_vp"
 * retval:
 *  return id: 0: not found, 1:(x1,y1) , 2:(x2,y2) , 3:(x2,y1) , 4:(x1,y2)
 */
int find_handle (VPoint vp ,int all_obj ,int with_strings ,VPoint *anchor_vp ,view *bb)
{
  Qtype typ;
  int i ,nh ,npoint ,num;
  QDobject o;
  int retval;

  if (all_obj) {
    /* The loop on types is used to make a hierarchical find */
    for (typ = Q_Compound; typ < Q_Last_objs; typ++) {
      if (typ != Q_String || with_strings) {
	for (i = iTS+1; i < objs_max (); i++) {
	  o = objs[i];
	  if (o.active && o.handle_on && o.typ == typ && o.hidden == FALSE) {
	    if (typ == Q_Polyline) {
	      if (find_polyline (&vp ,FALSE ,o.id ,&npoint ,&num) == RETURN_SUCCESS) {
		obj_tid_make_current (typ ,o.id ,o.father_typ ,o.father_id);
		cur_obj_num = i;
		*bb         = o.bb;
		return npoint+1;
	      }
	    } else {
	      nh = (o.pmask & MA_2_HANDLES) ? 2 : 4;
	      retval = find_one_obj_handle (vp ,i ,nh ,anchor_vp);
	      if (retval) {
		obj_tid_make_current (typ ,o.id ,o.father_typ ,o.father_id);
		cur_obj_num = i;
		*bb         = o.bb;
		return retval;
	      }
	    }
	  }
	}
      }
    }
  } else {
    i = cur_obj_num;
    o = objs[i];
    if (o.active && (o.typ != Q_String || with_strings)) {
      nh = (o.pmask & MA_2_HANDLES) ? 2 : 4;
      retval = find_one_obj_handle (vp ,i ,nh ,anchor_vp);
      if (retval) {
	*bb = o.bb;
	return retval;
      }
    }
  }
  return 0;
}

