# include "Ximage.h"

# define DrawLine(X,Y,DX,DY) (XDrawLine (graphic->display, graphic->window, graphic->gc, (int)(X), (int)(Y), (int)(X+DX), (int)(Y+DY)))
  
int DrawFrame (KapaGraphWidget *graph) {
  
  int i, j, Nticks, P, doffset;
  double fx, fy, dfx, dfy, dx = 0, dy = 0, lweight;
  Graphic *graphic;
  TickMarkData *ticks;
  int color;

  graphic = GetGraphic();

  /* each axis is drawn independently, but ticks and labels are placed according to perpendicular distance. */
  for (i = 0; i < 4; i++) {
    lweight = MAX (0, MIN (10, graph[0].axis[i].lweight));
    color = MAX (0, MIN (15, graph[0].axis[i].color));

    /* temporarily assume rectilinear axes */
    doffset = ((int)(lweight) % 2) ? 0.5*(lweight - 1) : 0.5*lweight;
    if (i == 0) { dx = doffset; dy = 0.0; }
    if (i == 2) { dx = doffset; dy = 0.0; }
    if (i == 1) { dx = 0.0; dy = doffset; }
    if (i == 3) { dx = 0.0; dy = doffset; }

    fx  = graph[0].axis[i].fx - dx;
    fy  = graph[0].axis[i].fy - dy;
    dfx = graph[0].axis[i].dfx + 2*dx;
    dfy = graph[0].axis[i].dfy + 2*dy;

    P = hypot (graph[0].axis[(i+1)%2].dfx, graph[0].axis[(i+1)%2].dfy);
    P *= (1 + 0.25*lweight);

    XSetLineAttributes (graphic->display, graphic->gc, lweight, LineSolid, CapNotLast, JoinMiter);
    XSetForeground (graphic->display, graphic->gc, graphic->color[color]);
    DrawRotTextInit (graphic->display, graphic->window, graphic->gc, graphic->color[color], graphic->back);

    if (graph[0].axis[i].isaxis) {
      DrawLine (fx, fy, dfx, dfy);
    }
    
    if (graph[0].axis[i].areticks) {
      ticks = CreateAxisTicks (&graph[0].axis[i], &Nticks);
      for (j = 0; j < Nticks; j++) {
	DrawTick (graphic, &graph[0].axis[i], P, &ticks[j], i);
      }
      FREE (ticks);
    }
  }
  return (TRUE);
}

int PrintTick (char *string, double value, double min, double max, int nsignif) {

  int Nexp = 0;
  int Nchar = 0;
  
  if (fabs(value/(max-min)) < 0.001) { 
    value = 0.0; 
    Nchar = sprintf (string, "%.1f", value);
    return Nchar;
  }

//   double lvalue = log10(fabs(value));
//   if (isfinite(lvalue)) {
//     Nexp = fabs(lvalue);
//   } else {
//     Nexp = 0;
//   }
  
  Nexp = abs(nsignif);

  if (Nexp > 3) {
    Nchar = sprintf (string, "%.1e", value);
  } else {
    if (nsignif < 0) {
      char format[16];
      snprintf (format, 16, "%%.%df", -1 * nsignif);
      Nchar = sprintf (string, format, value);
    } else {
      Nchar = sprintf (string, "%.1f", value);
    }
  }
  return Nchar;
}

void DrawTick (Graphic *graphic, Axis *axis, int P, TickMarkData *tick, int naxis) {
  
  double x, y, dx, dy;
  int pos, dir, fontsize;
  double size, n, pad;
  char string[64], *fontname;

  double fx  = axis->fx;
  double fy  = axis->fy;
  double dfx = axis->dfx;
  double dfy = axis->dfy;

  double min = axis->min;
  double max = axis->max;

  double value = tick->value;

  pos = size = 0;

  if (tick->IsMajor) { 
    size = MAX (0.02, 7.0 / P); 
  } else {
    size = MAX (0.01, 4.0 / P); 
  }
  
  n = P / hypot(dfx, dfy);
  x = fx + (value-min)*dfx/(max - min);
  y = fy + (value-min)*dfy/(max - min);

  if ((naxis == 0) || (naxis == 2)) {
    x = MAX(x, fx);
  }
  //   y = MAX(y, fy);
  // }

  dir = ((naxis == 0) || (naxis == 1)) ? -1 : +1;
  dx = dir*size*dfy*n;	
  dy = dir*size*dfx*n;
  
  DrawLine (x, y, dx, dy);

  if (tick->IsLabel) {
    int xt, yt;

    fontname = GetRotFont (&fontsize);
    
    pad = !isnan(axis->ticktextPad) ? axis->ticktextPad*fontsize : fontsize + 4.0;
    // pad = !isnan(axis->ticktextPad) ? axis->ticktextPad*fontsize : 0.8*fontsize + 1.0;
    
    /* temporarily assume rectilinear axes */
    if (naxis == 0) { dx = 0; dy = +1.0*pad; pos = 7; }
    if (naxis == 2) { dx = 0; dy = -1.0*pad; pos = 1; }

    if (naxis == 1) { dy = 0; dx = -0.5*pad; pos = 3; }
    if (naxis == 3) { dy = 0; dx = +0.5*pad; pos = 5; }

    xt = fx + (value-min)*dfx/(max - min) + dx;
    yt = fy + (value-min)*dfy/(max - min) + dy;

    PrintTick (string, value, min, max, tick->nsignif);
    DrawRotText (xt, yt, string, pos, 0.0);
  }
}

// do I really want this?  allow an override?
# define MIN_RANGE 1e-30

void AxisTickScale (Axis *axis, double *major, double *minor, int *nsignif) {

  double range, lrange, factor, mantis, fmantis, power;

  range = axis[0].max - axis[0].min;
  if (fabs(range) < MIN_RANGE) {
    range = (range < 0) ? -MIN_RANGE : +MIN_RANGE;
  }

  lrange = log10(MAX(fabs(range), MIN_RANGE));
  mantis = modf (lrange, &factor);
  if (mantis < 0.0) {
    mantis += 1.0;
    factor -= 1.0;
  }
  
  // how many significant digits are needed?

  power = MAX(pow(10.0, factor), MIN_RANGE);
  fmantis = pow(10.0, mantis);
  *major = MAX(0.5 * power, MIN_RANGE);
  *minor = MAX(0.1 * power, MIN_RANGE);
  
  if ((fmantis >= 1.0) && (fmantis <  1.999)) {
    *major = 0.5 * power;
    *minor = 0.1 * power;
    *nsignif = factor - 1;
    if (axis[0].areticks == 1) {
      *major = 0.25 * power;
      *minor = 0.05 * power;
      *nsignif = factor - 2;
    }	  
  }
  if ((fmantis >= 1.999) && (fmantis <  3.999)) {
    *major = 1.0 * power;
    *minor = 0.2 * power;
    *nsignif = factor;
    if (axis[0].areticks == 1) {
      *major = 0.5 * power;
      *minor = 0.1 * power;
      *nsignif = factor - 1;
    }	  
  }
  if ((fmantis >= 3.999) && (fmantis <  5.999)) {
    *major = 1.0 * power;
    *minor = 0.5 * power;
    *nsignif = factor;
    if (axis[0].areticks == 1) {
      *major = 1.0 * power;
      *minor = 0.2 * power;
      *nsignif = factor;
    }	  
  }
  if ((fmantis >= 5.999) && (fmantis <   7.999)) {
    *major = 2.0 * power;
    *minor = 0.5 * power;
    *nsignif = factor;
    if (axis[0].areticks == 1) {
      *major = 1.0 * power;
      *minor = 0.5 * power;
      *nsignif = factor;
    }	  
  }
  if ((fmantis >= 7.999) && (fmantis <  10.000)) {
    *major = 2.5 * power;
    *minor = 0.5 * power;
      *nsignif = factor - 1;
    if (axis[0].areticks == 1) {
      *major = 2.0 * power;
      *minor = 0.5 * power;
      *nsignif = factor;
    }	  
  }
}

TickMarkData *CreateAxisTicks (Axis *axis, int *nticks) {

  TickMarkData *ticks;
  double range, major, minor, first, value, dPixels, overshoot;
  int i, NTICKS, nPixels, done, ifirst, nsignif;

  *nticks = 0;

  if (isnan(axis->min) || isinf(axis->min)) return NULL;
  if (isnan(axis->max) || isinf(axis->max)) return NULL;
  
  range = axis->max - axis->min;

  // not sure why I skip out here instead of using the MIN_RANGE value
  if (fabs(range) < MIN_RANGE) return NULL;
  
  if (fabs(range) < MIN_RANGE) {
    range = (range < 0) ? -MIN_RANGE : +MIN_RANGE;
  }

  // length of the axis in pixels
  nPixels = hypot(axis->dfx, axis->dfy);
  dPixels = nPixels / range; // axis pixel-scale

  AxisTickScale (axis, &major, &minor, &nsignif);

  // be a little generous 
  NTICKS = (int)(fabs(range / minor)) + 2;
  ALLOCATE (ticks, TickMarkData, NTICKS);

  // value of the tick mark
  ifirst = (axis->min < 0.0) ? (axis->min/minor - 0.05) : (axis->min/minor + 0.05);
  first = minor*ifirst;
  
  // allow undershoot by 1 pixel
  overshoot = (axis->min - first) * dPixels;
  // if (overshoot > 0.5) {
  //   if (range > 0) {
  //     first += minor;
  //   } else {
  //     first -= minor;
  //   }
  // }

  // XXX need to fix first and last value for printing label: currently prints first label
  // too early & fails to do last label if too close

  // loop to find the ticks
  done = FALSE;
  value = first; 
  for (i = 0; !done && (i < NTICKS); i++) {
    ticks[i].IsMajor = FALSE;
    ticks[i].IsMajor |= (fabs((int)(value/major) - (value/major)) < 0.5*(minor/major));
    ticks[i].IsMajor |= (fabs((int)((value + 0.5*minor)/major) - (value/major)) < 0.5*(minor/major));
    ticks[i].IsMajor |= (fabs((int)((value - 0.5*minor)/major) - (value/major)) < 0.5*(minor/major));
    ticks[i].IsLabel = (ticks[i].IsMajor && axis->islabel);
    ticks[i].value = value;
    ticks[i].nsignif = nsignif;
    if (range > 0) 
      value += minor;
    else 
      value -= minor;
    
    done |= (range > 0) && (value > axis->max);
    done |= (range < 0) && (value < axis->max);
  }

  *nticks = i;
  return (ticks);
}
