/*-----------------------------------------------------------------------*\
 |                      General axis routine                             |
\*-----------------------------------------------------------------------*/

#include <math.h>

#include "xypx.h"

#define nint(x) ((x - floor(x) < 0.5) ? (int) (x) : (int) (x + 0.5))
#define min(a,b) (((a) < (b)) ? (a) : (b))
#define max(a,b) (((a) > (b)) ? (a) : (b))

/*-----------------------------------------------------------------------*\
 | External procedures
\*-----------------------------------------------------------------------*/

extern int get_limits (double *xmin, double *ymin, double *xmax, double *ymax);
extern void get_device_limits (int *limx, int *limy);
extern void set_color (int c);
extern int move (int x, int y);
extern int draw (int x, int y);
extern int graphic_text (char *s, int x, int y, int c);

/*-----------------------------------------------------------------------*\
 | External data
\*-----------------------------------------------------------------------*/

extern int p1x,p1y,p2x,p2y;
extern double u1x,u1y,u2x,u2y;
extern char plot_type;
extern char variable_to_count;
extern int *counts;
extern double bin_width;
extern int number_of_bins;
extern double low_bin;

/*-----------------------------------------------------------------------*\
 | Public procedures
\*-----------------------------------------------------------------------*/

int axis (struct axis_characteristics *ax);

/*-----------------------------------------------------------------------*\
 | Public data
\*-----------------------------------------------------------------------*/
/*-----------------------------------------------------------------------*\
 | Static procedures
\*-----------------------------------------------------------------------*/
/*-----------------------------------------------------------------------*\
 | Static data
\*-----------------------------------------------------------------------*/
/*-----------------------------------------------------------------------*\
\*-----------------------------------------------------------------------*/

int axis (struct axis_characteristics *ax) {
    int i,j,k,L;
    int err;
    int ix,iy;
    int tx,ty,mx;
    int pw,px,py,pz;
    int qw,qx,qy,qz;
    int tick_count,tick_length;
    int integer_increment;
    int limx,limy;
    int q1x,q1y,q2x,q2y;
    double upa,upb,upc,upd;
    double pqa,pqb,pqc,pqd;
    double xmin,xmax,xrng;
    double ymin,ymax,yrng;
    double range;
    double low,high;
    double tick;
    char number [12];

    if (u1x == u2x || u1y == u2y) {
        get_limits (&xmin,&ymin,&xmax,&ymax);
        switch (plot_type) {
            case scatter:
                xrng = xmax - xmin;
                if (xrng == 0) xrng = 10.;
                u1x = xmin - .05*xrng;
                u2x = xmax + .05*xrng;
                yrng = ymax - ymin;
                if (yrng == 0) yrng = 10.;
                u1y = ymin - .05*yrng;
                u2y = ymax + .05*yrng;
                break;
            case downcore:
                xrng = xmax - xmin;
                if (xrng == 0) xrng = 10.;
                u1x = xmin - .05*xrng;
                u2x = xmax + .05*xrng;
                yrng = ymax - ymin;
                if (yrng == 0) yrng = 10.;
                u2y = ymin - .05*yrng;      /* Reverse Y axis    */
                u1y = ymax + .05*yrng;      /* on downcore plots */
                break;
            case histogram:
                xmin = low_bin;
                xmax = low_bin + number_of_bins * bin_width;
                xrng = xmax - xmin;
                if (xrng == 0) xrng = 10.;
                u1x = xmin - .05*xrng;
                u2x = xmax + .05*xrng;
                j = 0;
                for (i=0; i < number_of_bins; i++)
                    if (counts[i] > j) j = counts[i];
                u1y = 0;
                u2y = 1.1 * (double) j;
                break;
            }
        }

    /* calculate logical-to-physical transformation */
    upa = ((double) (p2x - p1x)) / (u2x - u1x);
    upb = ((double) (p1x)) - u1x * upa;
    upc = ((double) (p2y - p1y)) / (u2y - u1y);
    upd = ((double) (p1y)) - u1y * upc;

    get_device_limits (&limx,&limy);
    q1x = 0;
    q1y = limy;
    q2x = limx;
    q2y = 0;

    pqa = ((double) (q2x - q1x))/10000;
    pqb = (double) q1x;
    pqc = ((double) (q2y - q1y))/7500;
    pqd = (double) q1y;

    if (ax->a1x == ax->a2x && ax->a1y == ax->a2y) {
        i = ax->dir.variable * 2 + ax->dir.placement;
        switch (i) {
            case 0: /* X low   */
                ax->a1x = u1x;
                ax->a1y = u1y;
                ax->a2x = u2x;
                ax->a2y = u1y;
                break;
            case 1: /* X high  */
                ax->a1x = u1x;
                ax->a1y = u2y;
                ax->a2x = u2x;
                ax->a2y = u2y;
                break;
            case 2: /* Y left  */
                ax->a1x = u1x;
                ax->a1y = u1y;
                ax->a2x = u1x;
                ax->a2y = u2y;
                break;
            case 3: /* Y right */
                ax->a1x = u2x;
                ax->a1y = u1y;
                ax->a2x = u2x;
                ax->a2y = u2y;
                break;
            }
        }

    /* draw axis edge */
    set_color (ax->axis_color);
    px = nint (upa*ax->a1x + upb);
    py = nint (upc*ax->a1y + upd);
    qx = nint (pqa*( (double) px) + pqb);
    qy = nint (pqc*( (double) py) + pqd);
    move (qx,qy);
    px = nint (upa*ax->a2x + upb);
    py = nint (upc*ax->a2y + upd);
    qx = nint (pqa*( (double) px) + pqb);
    qy = nint (pqc*( (double) py) + pqd);
    draw (qx,qy);

    /* if major tick increment is zero, figure out appropriate increment */
    if (ax->tick_increment[0] == 0) {
        range = ((ax->dir.variable == 0) ? fabs (u2x - u1x)
                                         : fabs (u2y - u1y));

        j = (int) log10 (range);
        ax->tick_increment[0] = pow10(j-1);
        tick_count = (int) (range / ax->tick_increment[0]);
        while (tick_count < 3 || tick_count > 11) {
            if (tick_count < 3) ax->tick_increment[0] /= 2;
            if (tick_count > 11) ax->tick_increment[0] *= 5;
            tick_count = (int) (range / ax->tick_increment[0]);
            }
        }
    /* set a flag indicating whether the increment is integer */
    integer_increment = (ax->tick_increment[0] - floor(ax->tick_increment[0]) < .00001);

    /* if minor tick increment is zero, figure out appropriate increment */
    if (ax->tick_increment[1] == 0) {
        range = (ax->dir.variable == 0) ? fabs (u2x - u1x)
                                        : fabs (u2y - u1y);
        j = (int) log10 (range);
        ax->tick_increment[1] = pow10(j-1);
        }

    /* find low and high ends of the axis */
    low = (ax->dir.variable == 0) ? min (u2x,u1x)
                                  : min (u2y,u1y);
    high = (ax->dir.variable == 0) ? max (u2x,u1x)
                                   : max (u2y,u1y);

    /* For major (j=0) and minor (j=1) ticks */
    for (j = 0; j < 2; j++) {
        /* skip altogether if negative tick increment */
        if (ax->tick_increment[j] < 0) continue;

        /* first tick is at the first multiple of the increment greater than low */
        if ( (tick = low / ax->tick_increment[j]) > 0) tick += 1;
        tick = ((int) tick) * ax->tick_increment[j];

        /* major ticks are two percent of the size of the other axis */
        /* minor ticks are one percent                               */
        tick_length = (ax->dir.variable == 0) ? (p2x - p1x)
                                              : (p2y - p1y);
        tick_length /= 50 * (j+1);

        /* adjust sign of tick length according to direction flag */
        if (j == 0)
            tick_length *= (ax->dir.major_ticks == 0) ? -1 : 1;
        else
            tick_length *= (ax->dir.minor_ticks == 0) ? -1 : 1;

        if (ax->dir.variable == 0) { /* X is the variable */
            py = nint (upc*ax->a1y + upd);
            qy = nint (pqc*( (double) py) + pqd);
            pz = py + tick_length;
            qz = nint (pqc*( (double) pz) + pqd);
            ty = nint (pqc*( (double) pz) + pqd);
            if (ax->dir.major_ticks == 0)
                ty += 3;
            else
                ty -= ax->char_height + 2;
            while (tick <= high) {
                px = nint (upa*tick + upb);
                qx = nint (pqa*( (double) px) + pqb);
                move (qx,qy);
                draw (qx,qz);

                if (j == 0) {
                    if (integer_increment)
                        sprintf (number,"%ld", (long) tick);
                    else {
                        sprintf (number,"%lf",tick);
                        L = strlen (number) - 1;
                        while (L > 0 && number [L] == '0') {
                            number [L] = 0;
                            L--;
                            }
                        }
                    L = strlen (number);
                    tx = qx - ((L-1) * ax->char_width / 2);
                    graphic_text (number,tx,ty,1,1,ax->axis_color);
                    }
                tick += ax->tick_increment[j];
                }
            if (j == 0) {
                px = nint (upa*(ax->a1x + ax->a2x)/2 + upb);
                qx = nint (pqa*( (double) px) + pqb) - (ax->char_width/2)*strlen (ax->label);
                qy = ty + ((ax->dir.major_ticks == 0) ? ax->char_height + 2 : 2);
                graphic_text (ax->label,qx,qy,1,1,ax->axis_color);
                }
            }
        else {   /* Y is the variable */
            px = nint (upa*ax->a1x + upb);
            qx = nint (pqa*( (double) px) + pqb);
            pz = px + tick_length;
            qz = nint (pqa*( (double) pz) + pqb);
            mx = qx;
            while (tick <= high) {
                py = nint (upc*tick + upd);
                qy = nint (pqc*( (double) py) + pqd);
                move (qx,qy);
                draw (qz,qy);

                if (j == 0) {
                    if (integer_increment)
                        sprintf (number,"%ld", (long) tick);
                    else {
                        sprintf (number,"%lf",tick);
                        L = strlen (number) - 1;
                        while (L > 0 && number [L] == '0') {
                            number [L] = 0;
                            L--;
                            }
                        }
                    L = strlen (number);
                    if (ax->dir.placement)
                        tx = qz + 4;
                    else
                        tx = qz - (L+1) * ax->char_width;
                    ty = qy - ax->char_height/2;
                    graphic_text (number,tx,ty,1,1,ax->axis_color);
                    if (ax->dir.major_ticks == 0)
                        if (tx < mx) mx = tx;
                    else
                        if (tx > mx) mx = tx;
                    }
                tick += ax->tick_increment[j];
                }
            if (j == 0) {
                py = nint (upc*(ax->a1y + ax->a2y)/2 + upd);
                qy = nint (pqc*( (double) py) + pqd) + (ax->char_width/2)*strlen (ax->label);
                qx = mx;
                if (ax->dir.major_ticks == 0)
                    qx -= (ax->char_height + 2);
                else
                    qx += 2;
                vertical_text (ax->label,qx,qy,1,1,ax->axis_color);
                }
            }
        }
    }

