/**
 * File ng_objs_draw.c
 *
 *  Used with and without NOGUI
 *
 *
 *  The first arg of  *obj* is the index in array objs (if relevant)
 *  the two firsts arg of *_t_obj* arg (typ,id)
 */

#include <config.h>
#include <cmath.h>

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

#include "globals.h"

#include "graphs.h"
#include "utils.h"
#include "protos.h"
#include "plotone.h"

#include "ng_objects.h"
#include "nn_tree.h"
#include "zc.h"

#define DEG_TO_RAD (3.1415927 / 180.0)

static void draw_arc_arrows (QDobject *o ,VPoint vp1 ,VPoint vp2);
static void draw_line_label (QDobject *o ,VPoint vp1 ,VPoint vp2);

/************ D R A W    objs ***************/

/**
 *  draw_obj : draw "typ_to_plot"
 *             i.e. timestamp, box, string, arc, line and polyline
 *             and compute their bounding box bb.
 *             Prepare also to compound any typ_to_plot.
 */
static Qtype typ_to_plot;
static double xori, yori ,xsca ,ysca;
static int cur_draw_layer;

/**
 * "i" is the index in objs[] and depth is in the tree (not the layer)
 *  For Q_Compound, up to now, xsca = ysca = 1.0
 */
static void draw_obj (int i ,int depth)
{
  WPoint wptmp;
  VPoint vptmp ,vp1, vp2 ,svp1 ,svp2 ,*ovp ,*svp;
  double vxo ,vyo;
  view   sbb;
  QDobject o;
  Qtype typ;
  int nh ,nvp ,j;
  size_t lenvp;

  /* Preliminary checking */
  if (obj_is_active (i) == FALSE) return;
  obj_get (i ,&o);
  if (o.id < 0) return;                    /* templates are not drawable */
  if (o.hidden) nn_traverse_stop (TRUE);
  typ = o.typ;
  if (obj_t_is_geometric (typ) == FALSE) {
    return;
  } else if (typ == Q_Compound) {
    xori += o.x1;            		  /* translate origin */
    yori += o.y1;
    xsca *= o.x2;
    ysca *= o.y2;
    o.bb.xv1 = 0.0;          /* reset bbox to invalid to forget old value */
    o.bb.yv1 = 0.0;
    o.bb.xv2 = 0.0;
    o.bb.yv2 = 0.0;
    return;
  } else if (o.layer != cur_draw_layer) {
    nn_traverse_stop (TRUE);
    return;
  }
  if (o.hidden) return;
  if (typ != typ_to_plot) return;  /* We draw only object with typ == typ_to_plot */

  /* Actually draw */
  nh = 4;
  setclipping (FALSE);
  if (o.loctyp == COORD_WORLD) {
    wptmp.x = xori + xsca*o.x1;
    wptmp.y = yori + ysca* o.y1;
    vp1 = Wpoint2Vpoint (wptmp);
    if (typ != Q_String) {
      wptmp.x = xori + xsca*o.x2;
      wptmp.y = yori + ysca*o.y2;
      vp2 = Wpoint2Vpoint (wptmp);
    }
  } else {
    vp1.x = xori + xsca*o.x1;
    vp1.y = yori + ysca*o.y1;
    vp2.x = xori + xsca*o.x2;
    vp2.y = yori + ysca*o.y2;
  }
  activate_bbox (BBOX_TYPE_TEMP, TRUE);
  reset_bbox    (BBOX_TYPE_TEMP);
  setcolor   (o.fillcolor);
  setpattern (o.fillpattern);
  if (typ == Q_Box) {
    FillRect (vp1, vp2);
  } else if (typ == Q_Arc) {
    DrawFilledArc (vp1, vp2 ,o.astart ,o.aend ,0);  // mode A FAIRE
  }
  setcolor (o.color);
  setlinewidth (o.linew);
  setlinestyle (o.lines);
  setpattern (1);
  switch (typ) {
  case Q_Line:
    DrawLine (vp1, vp2);
    nh = 2;
    switch (o.arrow_end) {
    case 0:
      break;
    case 1:
      draw_arrowhead (vp2, vp1, &o.arrow);
      break;
    case 2:
      draw_arrowhead (vp1, vp2, &o.arrow);
      break;
    case 3:
      draw_arrowhead (vp2, vp1, &o.arrow);
      draw_arrowhead (vp1, vp2, &o.arrow);
      break;
    }
    draw_line_label (&o ,vp1 ,vp2);
    break;
  case Q_Polyline:
    lenvp = o.nxy * sizeof(VPoint);
    /* Copy o.(x,y) into ovp, converting in view coord. if necessary */
    ovp = xmalloc (lenvp);
    if (o.loctyp == COORD_VIEW) {
      for (j = 0; j < o.nxy; j++) {
	ovp[j].x = o.x[j];
	ovp[j].y = o.y[j];
      }
    } else {
      for (j = 0; j < o.nxy; j++) {
	world2view (o.x[j] ,o.y[j] ,&ovp[j].x ,&ovp[j].y);
      }
    }
    if (o.pmask & MA_SMOOTH) {
      /* Interpolate with X-splines into svp */
      svp = xmalloc (lenvp);
      xspline_eval_vp (&svp ,ovp ,o.nxy ,&nvp ,o.xspline_s ,o.xspline_p ,o.pmask & MA_IS_CLOSED);
    } else {
      svp = ovp;
      nvp = o.nxy;
    }
    /* add shift if polyline is into a compound */
    if (o.loctyp == COORD_VIEW) {
      vxo = xori;
      vyo = yori;
    } else {
      wptmp.x = xori;
      wptmp.y = yori;
      vptmp = Wshift2Vshift (wptmp);
      vxo = vptmp.x;
      vyo = vptmp.y;
    }
    for (j = 0; j < nvp; j++) {
      svp[j].x += vxo;
      svp[j].y += vyo;
    }
    /* Actually draw the polyline */
    if (o.pmask & MA_IS_CLOSED) {
      DrawPolyline (svp ,nvp ,POLYLINE_CLOSED);
    } else {
      DrawPolyline (svp ,nvp ,POLYLINE_OPEN);
      nh = 2;
      switch (o.arrow_end) {
      case 0:
	break;
      case 1:
	draw_arrowhead (svp[1] ,svp[0] ,&o.arrow);
	break;
      case 2:
	draw_arrowhead (svp[nvp-2] ,svp[nvp-1] ,&o.arrow);
	break;
      case 3:
	draw_arrowhead (svp[1] ,svp[0] ,&o.arrow);
	draw_arrowhead (svp[nvp-2] ,svp[nvp-1] ,&o.arrow);
	break;
      }
    }
    xfree (svp);
    if (o.pmask & MA_SMOOTH) xfree (ovp);
    break;
  case Q_Box:
  case Q_Colorbar:
    DrawRect (vp1, vp2);
    if (typ == Q_Colorbar) zc_colorbar_fill (o.self);
    break;
  case Q_Arc:
    DrawArc (vp1 ,vp2 ,o.astart ,o.aend);
    if (o.arrow_end > 0) draw_arc_arrows (&o ,vp1 ,vp2);
    break;
  case Q_String:
  case Q_TimeStamp:
    setcharsize (o.charsize);
    setfont     (o.font);
    if (o.fillpattern > 0 || o.lines > 0) {
      t1fonts_rasterize_only (TRUE);
      WriteString (vp1, o.rot, o.just, o.s);
      sbb = get_bbox (BBOX_TYPE_TEMP);
      view_extend (&sbb ,3 * getlinewidth());
      svp1.x = sbb.xv1;
      svp1.y = sbb.yv1;
      svp2.x = sbb.xv2;
      svp2.y = sbb.yv2;
      if (o.fillpattern > 0) {
	setcolor   (o.fillcolor);
	setpattern (o.fillpattern);
	FillRect (svp1, svp2);
      }
      setcolor (o.color);
      setpattern (1);
      if (o.lines > 0) 	DrawRect (svp1, svp2);
    }
    t1fonts_rasterize_only (FALSE);
    WriteString (vp1, o.rot, o.just, o.s);
    break;
  default:
    break;
  }

  o.bb = get_bbox (BBOX_TYPE_TEMP);
  /* copy back to update bb */
  obj_set (i ,&o);
  setclipping (TRUE);
}

/**
 * When we go out of a compound in the tree, we need to
 * retrieve the original scaling
 */
void scale_back (int i ,int depth)
{
  if (objs[i].typ == Q_Compound) {
    xori -= objs[i].x1;
    yori -= objs[i].y1;
    xsca /= objs[i].x2;
    ysca /= objs[i].y2;
    return;
  }
}

/**
 *  Function to draw Q_Line, Q_Arc, Q_Box, Q_String and Q_TimeStamp
 *  if gno==-1, it is the Q_Project
 *  bb of compounds are not updated here
 */
void draw_objs_in_graph (int gno ,int layer)
{
  int i;
  if (gno >= 0 && is_graph_hidden (gno)) return;
  if (layer_is_active (layer)) {
    cur_draw_layer = layer;
    /* Hierarchical plot in reverse order of Qtype enum */
    for (typ_to_plot = Q_Last_objs - 1;  typ_to_plot >= Q_Compound; typ_to_plot--) {
      xori = 0.0;
      yori = 0.0;
      xsca = 1.0;
      ysca = 1.0;
      if (gno == -1) {
	/* Draw timestamp */
	draw_obj (iTS ,0);
	/* assume Q_Line is the first template  and draw objects
	 *                              not attached to a graph . */
	i = obj_tid_get_num (Q_Line ,-1 ,Q_Project ,-1);
	if (i  >= 0) {
	  nn_traverse_with_middle (i ,draw_obj ,scale_back ,NULL);
	}
      } else {
	i = obj_tid_get_graph_num (gno);
	/* draw objects attached to gno and on layer */
	if (i  >= 0) {
	  nn_traverse_with_middle (objs[i].child ,draw_obj ,scale_back ,NULL);
	}
      }
    }
  }
}

static void draw_arc_arrows (QDobject *po ,VPoint vp1 ,VPoint vp2)
{
  VPoint vpa ,vpb;
  double xc ,yc ,a ,b ,teta ,drad;

  xc = 0.5 * (vp2.x + vp1.x);
  yc = 0.5 * (vp1.y + vp2.y);
  a  = 0.5 * fabs (vp2.x - vp1.x);
  b  = 0.5 * fabs (vp2.y - vp1.y);
  drad = DEG_TO_RAD;
  if (po->arrow_end == 1 || po->arrow_end == 3) {
    teta = DEG_TO_RAD * po->astart;
    vpa.x = xc + a*cos(teta);
    vpa.y = yc + b*sin(teta);
    vpb.x = xc + a*cos(teta - drad);
    vpb.y = yc + b*sin(teta - drad);
    draw_arrowhead (vpa, vpb, &(po->arrow));
  }
  if (po->arrow_end == 2 || po->arrow_end == 3) {
    teta = DEG_TO_RAD * po->aend;
    vpa.x = xc + a*cos(teta);
    vpa.y = yc + b*sin(teta);
    vpb.x = xc + a*cos(teta + drad);
    vpb.y = yc + b*sin(teta + drad);
    draw_arrowhead (vpa, vpb, &(po->arrow));
  }

}


static void draw_line_label (QDobject *po ,VPoint vp1 ,VPoint vp2)
{
  char label[MAX_STRING_LENGTH];
  VPoint vp_label;
  double dx ,dy ,dist ,rot;
  if (po->label_opt > 0) {
    setcharsize (po->charsize);
    setfont     (po->font);
    t1fonts_rasterize_only (FALSE);
    dx = vp2.x - vp1.x;
    dy = vp2.y -vp1.y;
    rot = atan2 (dy ,dx);
    vp_label.x = 0.5*(vp1.x+vp2.x) + po->para*cos(rot) - po->perp*sin(rot);
    vp_label.y = 0.5*(vp1.y+vp2.y) + po->para*sin(rot) + po->perp*cos(rot);
    rot /= DEG_TO_RAD;
    if (po->label_opt == 1) {    /* Length */
      if (po->loctyp == COORD_WORLD) {
	dx = po->x2 - po->x1;
	dy = po->y2 - po->y1;
      }
      dist = sqrt (dx*dx +dy*dy);
      if (sprintf (label ,po->frmt ,dist) > 0) {
	WriteString (vp_label ,po->rot+(int)rot ,po->just ,label);
      } else {
	sprintf (label ,"Error writing label of line %d" ,po->id);
	errmsg (label);
      }
    } else {                    /* use string po->s */
      WriteString (vp_label ,po->rot+(int)rot ,po->just ,po->s);
    }
  }
}
