/*
   File:            agbox.h
   Author:          Andrew Moore
   Created:         7th Feb 1990

   Description:     Standard andrew header file for graphics
*/

#ifndef agbox_H
#define agbox_H

#include "amgr.h"

/* An agbox is very simple. It's just a 2-d rectangle. It is usually
   used to represent a rectangle in ag_graphics coordinates. Often it's
   a rectangle that we want to draw stuff inside. There are lots of 
   utilities for drawing in, and relative to agboxes. There are also
   utilities for making smaller aboxes from bigger ones, and for
   cleverly printing strings inside agboxes in a way that fits. Also,
   in draw/ongr.h, there are utilities for drawing color_strings 
   (strings where different letters may have different colors) in them. */

typedef struct agbox
{
  double xlo;
  double ylo;
  double xhi;
  double yhi;
} agbox;


/* Now we define all functions that deal with bounding boxes, for 
   our purposes called an ag_box.*/
agbox *mk_agbox(double xlo,double ylo,double xhi,double yhi);
void free_agbox(agbox *agb);
agbox *mk_copy_agbox(agbox *agb);
void fprintf_agbox(FILE *s,char *m1,agbox *x,char *m2);

/* Returns x such that x is fraction "frac" of the 
   distance between lo and hi */
double break_agval(double lo,double hi,double frac);

/* returns y (or x) such that y is fraction frac of the way from the bottom
   of the box to the top */
double vertical_break_agval(agbox *agb,double frac);
double horizontal_break_agval(agbox *agb,double frac);

/* TODO: Dan document this!! */
void agbox_piebar(agbox *agb,dyv *widths
                  ,string_array *left,string_array *right
                  ,ivec *colors);

/*
  300secs          400secs       500secs                      700secs
   +------------------------------------------------------------+
   |                |              |                            |
   |                |              |                            |
   +------------------------------------------------------------+

   Suppose parent was the large box above. Suppose you were told that parent
   covers the time period 300secs--700secs. And suppose you wanted to 
   draw in a subregion corresponding to 400-500 secs. What should your agbox
   be? The answer is returned by this function.
*/
agbox *mk_agbox_corresponding_to_time(agbox *parent,
				      double parent_start_time,
				      double parent_end_time,
				      double local_start_time,
				      double local_end_time);

double ag_value_corresponding_to_time(agbox *parent,
				      double parent_start_time,
				      double parent_end_time,
				      double local_time);

double get_time_corresponding_to_agbox(agbox *agb,double start,double end,double x);

/* TODO:  This function should replace the function above!   -- Dan */
double transform_agbox_coord(agbox *agb,double x,double a0,double a1,int comp);

void agbox_render_string_at_bottom(agbox *agb,char *string);

/* Prints horizontally at top left of box */
void agbox_render_string_horizontal(agbox *agb,char *string);

/* Prints vertically at top left of box */
void agbox_render_string_vertical(agbox *agb,char *string);

/* Prints horizontally at top left of box, unless it can't fit the string 
   in horizontally, and it can fit more letters in vertically, in which 
   case prints vertically.  It does clip letters.*/
void agbox_render_string(agbox *agb,char *string);

bool enough_room_for_string_in_agbox(agbox *agb,char *string);

/* As above, except only renders if enough room */
void agbox_render_string_if_enough_room(agbox *agb,char *string);

/* As above, except resizes agb to the leftover horizontal space.
  Returns TRUE iff there was enoguh room.*/
bool agbox_render_string_if_enough_room_and_resize(agbox **agb,char *string);

/*Fits each string into the box the best way it knows how.  Returns how many rows/cols it 
  needed to fit them all, or 0 if it couldn't do it.*/
int agbox_render_string_array(agbox *agb,string_array *sa);
int render_string_array_horizontal_if_possible(agbox *agb,string_array *sa);
int render_string_array_vertical_if_possible(agbox *agb,string_array *sa);

void agbox_render_string_word_wrap(char *str,agbox *agb);

/* Uses full name if it fits, else uses short name
   if it fits. Shortname may be NULL in which case it's never 
   drawn.  */
void agbox_render_string_or_abbrev(agbox *agb,char *name,char *shortname);

agbox *mk_horizontal_stripe_absolute(agbox *parent,double aglo,double aghi);
agbox *mk_horizontal_stripe_relative(agbox *parent,
				     double fraclo,double frachi);

/* Imagine dividing the first arg (parent) up into "numstrips" horizontal
   strips of equal height, exactly covering and partitioning parent. 
   What would be the agbox corresponding to the stripnum'th of these stripes
   from the top (where stripnum==0 corresponds to the topmost stripe?) */
agbox *mk_agbox_horizontal_substripe(agbox *parent,int stripnum,int numstrips);

agbox *mk_vertical_stripe_absolute(agbox *parent,double aglo,double aghi);
agbox *mk_vertical_stripe_relative(agbox *parent,double fraclo,double frachi);
agbox *mk_agbox_vertical_substripe(agbox *parent,int stripnum,int numstrips);

/* Shrinks both sides of agb by distance agwidth
   in the comp'th direction of the agb. If comp is XCOMP 
   the box becomes thinner horizontally (less distance left to right).
   If YCOMP becomes less tall. An equal amount is taken off both
   sides.  */
agbox *mk_shrink_both_sides_absolute(agbox *agb,int comp,double agwidth);

/* Shrinks both sides of agb by fraction "frac" of the entire
   width in the comp'th direction of the agb. If comp is XCOMP 
   the box becomes thinner horizontally (less distance left to right).
   If YCOMP becomes less tall. An equal amount is taken off both
   sides. If frac == 0.25 then the box becomes half the width since
   a quarter is taken off one side and a quarter off the other. */
agbox *mk_shrink_both_sides_relative(agbox *agb,int comp,double frac);

/* Draws a line segment in absolute coordinates. 
   It DOES clip the line so that only its components INSIDE agb are
   drawn. */
void render_line_segment_absolute(agbox *agb,
				  double x0,double y0,double x1,double y1);

/* Draws a relative-coordinate line segment. Assume that the
   bottom left of the agbox should be identified with the
   relative coordinate (0,0) and the top right should be identified
   with (1,1).

   It DOES clip the line so that only its components INSIDE agb are
   drawn. */
void render_line_segment_relative(agbox *agb,
				  double x0,double y0,double x1,double y1);

/* Draws a virtual-coordinate line segment. Assume that the
   bottom left of the agbox should be identified with the
   virtual-coordinates-point (vx0,vy0) and the top right with
   (vx1,vy1). Then this draws the line segment that begins with
   (x0,y0) in virtual coordinates and ends with (x1,y1).

   It DOES clip the line so that only its components INSIDE agb are
   drawn. */
void render_virtual_line_segment(agbox *agb,
				 double vx0,double vy0,double vx1,double vy1,
				 double x0,double y0,double x1,double y1);


/* my_errors unless comp is XCOMP or YCOMP */
void check_comp(int comp);

/* my_error unless hilo is LO or HI */
void check_hilo(int hilo);

/* Returns the value of the corresponding LO or HI
   coordinate of the agbox in the comp'th direction. */
/* PRE: comp is XCOMP or YCOMP
   PRE: hilo is LO or HI */
double agbox_ref(agbox *agb,int comp,int hilo);

/* PRE: comp is XCOMP or YCOMP */
double agbox_lo(agbox *agb,int comp);

/* PRE: comp is XCOMP or YCOMP */
double agbox_hi(agbox *agb,int comp);

/* PRE: comp is XCOMP or YCOMP */
double agbox_width(agbox *agb,int comp);

double agbox_middle(agbox *agb,int comp);

/* Returns v such that "a" is fraction "v" of
   the way thru the box in the comp'th direction.

   Basically transforms ag_coords into the unit square */
/* PRE: comp is XCOMP or YCOMP */
double agbox_abs2rel(agbox *agb,double a,int comp);

/* Basically transforms unit square to coords within agbox */
/* PRE: comp is XCOMP or YCOMP */
double agbox_rel2abs(agbox *agb,double v,int comp);

/* Makes the box (0,0) (1,1) */
agbox *mk_unit_agbox(void);

/* Makes the box (0,0) (512,512) */
agbox *mk_full_screen_agbox(void);

/* Sets the corresponding component of the corresponding
   coordinate of agbox */
void agbox_set(agbox *agb,int comp,int hilo,double a);

/* Increments the given component in the given coordinate
   by delta. It you wanted to move the bottom left corner
   19 pixels to the left, do
     agbox_increment(agb,XCOMP,LO,-19.0) */
void agbox_increment(agbox *agb,int comp,int hilo,double delta);

/* Maybe increase the size of agbox appropriately so
   that the coordinate comp==a would fit inside. 
   So it's a possible stretch (if necessary) of the LO or HI
   end in the comp'th direction. */
void maybe_expand_agbox_comp(agbox *agb,int comp,double a);

/* Maybe increase the size of agbox appropriately so
   that (x,y) would fit inside. 
   So it's a possible stretch (if necessary) of the LO or HI
   end in either or both directions. */
void maybe_expand_agbox(agbox *agb,double x,double y);

double agbox_xlo(agbox *agb);
double agbox_ylo(agbox *agb);
double agbox_xhi(agbox *agb);
double agbox_yhi(agbox *agb);

/* returns min(horix width, vert width) */
double agbox_min_width(agbox *agb);

/* Draws a rectangle around the border */
void agbox_border(agbox *agb);

/* As above but in the given color */
void agbox_color_border(agbox *agb,int agcolor);

/* Draws and fills the rectangle */
void agbox_fill(agbox *agb);

void agbox_color_fill(agbox *agb,int agcolor);

/*Returns TRUE iff (x,y) is within agb (or on the boundary)*/
bool is_in_agbox(agbox *agb,double x,double y);

/* Makes the bounding box of the set of points
    (xarr[0],yarr[0]) (xarr[1], yarr[1]) ...... */
agbox *mk_agbox_surrounding_dyvs(dyv *xarr,dyv *yarr);

/* Maybe enlarges the box if necessary to include
    (xarr[0],yarr[0]) (xarr[1], yarr[1]) ...... */
void maybe_expand_agbox_from_dyvs(agbox *agb,dyv *xarr,dyv *yarr);

/* agb is expanded if necessary to a size sufficient to ensure
   it contains x. It's expanded as little as possible. */
void maybe_expand_agbox_from_agbox(agbox *agb,agbox *x);

/* Call this if you want to reduce the height of agb after 
   you've printed something at the top of agb */
void shrink_agbox_for_printing(agbox *agb);

/* Tells you where to start printing if you want to print at
   the top left of agb */
double agbox_top_left_print_coord(agbox *agb,int comp);

/* Prints the string at the top left and reduces the height of
   agb so that agb no longer covers the region just printed in */
void print_and_shrink(agbox *agb,char *s);

/* As above except does nothing if s is NULL */
void maybe_print_and_shrink(agbox *agb,char *s);

agbox *mk_agbox_surrounding_tics(dyv *xarr,dyv *yarr);
void maybe_expand_agbox_from_tics(agbox *agb,dyv *xtics,dyv *ytics);

/* Returns a copy of ag except the hi/lo component
   of the comp'th direction is changed to v */
agbox *mk_agbox_segment(agbox *ag,double v,int comp,int hilo);

void split_agbox_vertical_absolute(agbox *ag,double x,agbox **r_ag1,
				   agbox **r_ag2);

void split_agbox_horizontal_absolute(agbox *ag,double y,agbox **r_ag1,
				     agbox **r_ag2);

/* Assuming all four numerical arguments are between zero and one
   makes a smaller sub-box of "agb" inside "agb". */
agbox *mk_sub_agbox(agbox *agb,double xlo,double ylo,double xhi,double yhi);

/*
+---------+         +---------+
|         |         |    |    |
|         |         |a01 |a11 |
| agb     | -->     +---------+
|         |         |    |    |
|         |         |a00 |a10 |
+---------+         +---------+
*/
void make_quadrant_sub_agboxes(agbox *agb,
			       agbox **r_a00,agbox **r_a01,
			       agbox **r_a10,agbox **r_a11);

/*
                    +---------+
                    |    |    |
                    |a01 |a11 |
  fullscreen-->     +---------+
                    |    |    |
                    |a00 |a10 |
                    +---------+
*/
void make_quadrant_agboxes(agbox **r_a00,agbox **r_a01,
			   agbox **r_a10,agbox **r_a11);

#endif /* #ifndef agbox_H */

