
/* Generated by Interface Builder */

#import "FormatHandler.h"
#import "Plot.h"
#import "PlotView.h"
#import "defs.h"
#import <appkit/OpenPanel.h>
#import <appkit/SavePanel.h>
#import <appkit/Panel.h>
#import <streams/streams.h>
#import <strings.h>
#import <appkit/color.h>
#import <appkit/NXColorWell.h>
#import <appkit/Button.h>

@implementation FormatHandler

// This is an attempt to get around an infinite loop that could otherwise result
// when we try to read in an "old" format file with the "new" program.
int cautious_read(NXStream *formatStream)
{
  int i;

  while(YES) {
    i = NXGetc(formatStream);
    if (i == ':') return 0;	/* good */
    if (i == EOF) {
      NXRunAlertPanel("Format File Read",
		      "Unexpected end of file while reading format file\n"
		      "(Perhaps you tried to read in an \"old\" format file)",
		      "OK", NULL, NULL);
      return 1;
    }
  }
}

- writeFormatData:(NXStream *)formatStream
{
  id  font;
  int ncurves = [plotParam nCurvesTotal], i;
  NXPoint   point;
  NXRect    windowframe;
  float r, g, b, a;
  NXColor aColor;

  NXPrintf(formatStream, "xmin xmax xinc: %g %g %g\n", [plotParam provideXmin],
	   [plotParam provideXmax], [plotParam provideXinc]);
  NXPrintf(formatStream, "ymin ymax yinc: %g %g %g\n", [plotParam provideYmin],
	   [plotParam provideYmax], [plotParam provideYinc]);
  NXPrintf(formatStream, "xaxis: %s\n", [plotParam xaxisLog]? "log" : "linear");
  NXPrintf(formatStream, "yaxis: %s\n", [plotParam yaxisLog]? "log" : "linear");

  NXPrintf(formatStream, "Main title: %s\n", [mainTitle stringValueAt:0]);
  NXPrintf(formatStream, " Font and size: ");
  font = [canvas provideMainTitleFont];
  if (font == NULL)
    NXPrintf(formatStream, "Helvetica 14.0\n");
  else
    NXPrintf(formatStream, "%s %f\n", [font name], [font pointSize]);
  point = [canvas provideMainTitleBoxOrigin];
  NXPrintf(formatStream, " main title box origin: %f %f\n", point.x, point.y);  

  NXPrintf(formatStream, "X title: %s\n", [xTitle stringValueAt:0]);
  NXPrintf(formatStream, " Font and size: ");
  font = [canvas provideXTitleFont];
  if (font == NULL)
    NXPrintf(formatStream, "Helvetica %f\n", DEFAULTFONTSIZE);
  else
    NXPrintf(formatStream, "%s %f\n", [font name], [font pointSize]);
  point = [canvas provideXTitleBoxOrigin];
  NXPrintf(formatStream, " x-title box origin: %f %f\n", point.x, point.y);  

  NXPrintf(formatStream, "Y title: %s\n", [yTitle stringValueAt:0]);
  NXPrintf(formatStream, " Font and size: ");
  font = [canvas provideYTitleFont];
  if (font == NULL)
    NXPrintf(formatStream, "Helvetica %f\n", DEFAULTFONTSIZE);
  else
    NXPrintf(formatStream, "%s %f\n", [font name], [font pointSize]);
  point = [canvas provideYTitleBoxOrigin];
  NXPrintf(formatStream, " y-title box origin: %f %f\n", point.x, point.y);  

  NXPrintf(formatStream, "Legend on/off: %d\n", [legendOnOff state]);
  NXPrintf(formatStream, "Legend Box on/off: %d\n",
	   [legendBoxOnOff state]);
  NXPrintf(formatStream, "Legend title: %s\n", [legendTitle stringValueAt:0]);
  NXPrintf(formatStream, " Font and size: ");
  font = [canvas provideLegendTitleFont];
  if (font == NULL)
    NXPrintf(formatStream, "Helvetica %f\n", DEFAULTFONTSIZE);
  else
    NXPrintf(formatStream, "%s %f\n", [font name], [font pointSize]);

  NXPrintf(formatStream, "Legend font and size: ");
  font = [canvas provideLegendFont];
  if (font == NULL)
    NXPrintf(formatStream, "Helvetica %f\n", DEFAULTFONTSIZE);
  else
    NXPrintf(formatStream, "%s %f\n", [font name], [font pointSize]);

  NXPrintf(formatStream, "No. of curves: %d\n", ncurves);
  NXPrintf(formatStream, "Curve No.  Line type  Symbol type"
	   "         Color (R,G,B,A)          Title\n");
  for (i = 0; i < ncurves; i++) {
    aColor = [plotParam provideCurveColor:i];
/* Convert to RGB for the sake of getting something human-readable */
    NXConvertColorToRGBA(aColor, &r, &g, &b, &a);
    NXPrintf(formatStream, "%4d %8d %10d %f %f %f %f %18s\n", i+1,
	     [plotParam providelinestyle:i], [plotParam providesymbolstyle:i],
	     r, g, b, a, [legendForm stringValueAt:i]);
  }

  NXPrintf(formatStream, "Line thickness: %f\n", [lineThicknessText floatValue]);
  NXPrintf(formatStream, "Symbol size: %f\n", [symbolSizeText floatValue]);

  NXPrintf(formatStream, "Grid on/off: %d\n", [gridOnOff state]);
  NXPrintf(formatStream, "Grid dotted: %d\n", [gridDotted state]);
  NXPrintf(formatStream, "Grid thickness: %f\n", [gridThicknessText floatValue]);

  NXPrintf(formatStream, "Border box on/off: %d\n",
	   [borderBoxOnOff state]);
  NXPrintf(formatStream, "Border box thickness: %f\n",
	   [borderBoxThicknessText floatValue]);

  NXPrintf(formatStream, "Frame box on/off: %d\n",
	   [frameBoxOnOff state]);
  NXPrintf(formatStream, "Frame box thickness: %f\n",
	   [frameBoxThicknessText floatValue]);

  NXPrintf(formatStream, "Axes on/off: %d\n", [axesOnOff state]);
  NXPrintf(formatStream, "Axis thickness: %f\n", [axisThicknessText floatValue]);

  NXPrintf(formatStream, "Tic label font and size: ");
  font = [canvas provideTicLabelFont];
  if (font == NULL)
    NXPrintf(formatStream, "Helvetica %f\n", DEFAULTFONTSIZE);
  else
    NXPrintf(formatStream, "%s %f\n", [font name], [font pointSize]);

  NXPrintf(formatStream, "Major tic marks on/off: %d\n",
	   [majorTicMarksOnOff state]);

  NXPrintf(formatStream, "Minor tic marks on/off: %d\n",
	   [minorTicMarksOnOff state]);

  NXPrintf(formatStream, "Tic mark length: %f\n",
	   [ticMarkLengthText floatValue]);

  NXPrintf(formatStream, "Tic mark thickness: %f\n",
	   [ticMarkThicknessText floatValue]);

  if (strncmp([ticMarkLocation title], "Axes", 4) == 0)
    i = 0;
  else if (strncmp([ticMarkLocation title], "Frame (2 sides)", 15) == 0)
    i = 1;
  else
    i = 2;
  NXPrintf(formatStream, "Tic mark location: %d\n", i);

  point = [canvas provideLegendBoxOrigin];
  NXPrintf(formatStream, "Legend box origin: %f %f\n", point.x, point.y);

  [canvas provideWindowFrame:&windowframe];
  NXPrintf(formatStream, "Window origin and size: %f %f %f %f\n",
	   windowframe.origin.x, windowframe.origin.y,
	   windowframe.size.width, windowframe.size.height);
  aColor = [plotParam provideTextColor];
  NXConvertColorToRGBA(aColor, &r, &g, &b, &a);
  NXPrintf(formatStream, "Text color (r, g, b, a): %f %f %f %f\n",
	   r, g, b, a);
  aColor = [plotParam provideBackgroundColor];
  NXConvertColorToRGBA(aColor, &r, &g, &b, &a);
  NXPrintf(formatStream, "Background color (r, g, b, a): %f %f %f %f\n",
	   r, g, b, a);

// Following is new for error bars:
  NXPrintf(formatStream, "Error bar base width: %f\n",
	   [ebarBaseWidthText floatValue]);

// Following is new (placed here for backward compatibility)
  NXPrintf(formatStream, "Legend transparency: %d\n", [legendOpaque state]);

  return self;
}

#define READ_ONE_INT(x) if (cautious_read(formatStream) != 0) return self;\
  NXScanf(formatStream, "%d\n", &x);

#define READ_ONE_FLOAT(x) if (cautious_read(formatStream) != 0) return self;\
  NXScanf(formatStream, "%f\n", &x);

#define READ_STRING(s) if (cautious_read(formatStream) != 0) return self; \
  while (NXGetc(formatStream) == ' ') \
    ;				/* throw away spaces */ \
  NXUngetc(formatStream);	/* but put that non-space back! */ \
  for (i = 0; i < 1024  && (s[i]=NXGetc(formatStream)) != '\n'; i++) \
    ;				/* everything up to newline */ \
  s[i] = '\000';

#define READ_STRING_AND_FLOAT(s, x) \
  if (cautious_read(formatStream) != 0) return self; \
  while (NXGetc(formatStream) == ' ') \
    ; \
  NXUngetc(formatStream); \
  (void) NXScanf(formatStream, "%s %f", s, &x); /* font and size */

- readFormatData:(NXStream *)formatStream
{
  char  string[1024];
  double d1, d2, d3;
  float  f1;
  int   i, j, ncurves, curveno, linetype, symtype;
  NXPoint point;
  NXRect  windowframe;
  float r, g, b, a;
  NXColor aColor;

  if (cautious_read(formatStream) != 0) return self;
  (void) NXScanf(formatStream, "%lf %lf %lf\n", &d1, &d2, &d3);
  [plotParam resetXmin:d1];
  [plotParam resetXmax:d2];
  [plotParam resetXinc:d3];
  if (cautious_read(formatStream) != 0) return self;
  (void) NXScanf(formatStream, "%lf %lf %lf\n", &d1, &d2, &d3);
  [plotParam resetYmin:d1];
  [plotParam resetYmax:d2];
  [plotParam resetYinc:d3];

  if (cautious_read(formatStream) != 0) return self;
  (void) NXScanf(formatStream, "%s\n", string);
  if (!strncmp(string, "log", 3))
    [plotParam forceXaxisLog];
  else
    [plotParam forceXaxisLinear];

  if (cautious_read(formatStream) != 0) return self;
  (void) NXScanf(formatStream, "%s\n", string);
  if (!strncmp(string, "log", 3))
    [plotParam forceYaxisLog];
  else
    [plotParam forceYaxisLinear];

  // Main title:
  READ_STRING(string);
  [mainTitle setStringValue:string at:0];
  READ_STRING_AND_FLOAT(string, f1);
  [canvas forceMainTitleFont:string :f1];
  if (cautious_read(formatStream) != 0) return self;
  (void) NXScanf(formatStream, "%f %f\n", &point.x, &point.y); /* maintitlebox */
  [canvas forceMainTitleBoxOrigin:point];

  // X title:
  READ_STRING(string);
  [xTitle setStringValue:string at:0];
  READ_STRING_AND_FLOAT(string, f1);
  [canvas forceXTitleFont:string :f1];
  if (cautious_read(formatStream) != 0) return self;
  (void) NXScanf(formatStream, "%f %f\n", &point.x, &point.y); /* xtitlebox */
  [canvas forceXTitleBoxOrigin:point];

  // Y title:
  READ_STRING(string);
  [yTitle setStringValue:string at:0];
  READ_STRING_AND_FLOAT(string, f1);
  [canvas forceYTitleFont:string :f1];
  if (cautious_read(formatStream) != 0) return self;
  (void) NXScanf(formatStream, "%f %f\n", &point.x, &point.y); /* ytitlebox */
  [canvas forceYTitleBoxOrigin:point];

  // Legend on/off:
  READ_ONE_INT(i);
  [legendOnOff setState:i];

  // Legend Box on/off:
  READ_ONE_INT(i);
  [legendBoxOnOff setState:i];

  // Legend title:
  READ_STRING(string);
  [legendTitle setStringValue:string at:0];

  // Legend title font and size:
  READ_STRING_AND_FLOAT(string, f1);
  [canvas forceLegendTitleFont:string :f1];

  // Legend font:
  READ_STRING_AND_FLOAT(string, f1);
  [canvas forceLegendFont:string :f1];

  // Curves: No., line type, symbol type, colors, title
  READ_ONE_INT(ncurves);
  while (NXGetc(formatStream) != '\n')
    ;
  for (i = 0; i < ncurves; i++) {
    NXScanf(formatStream, "%d %d %d", &curveno, &linetype, &symtype);
    NXScanf(formatStream, "%f %f %f %f", &r, &g, &b, &a);
    aColor = NXConvertRGBAToColor(r, g, b, a);
    while (NXGetc(formatStream) == ' ')
      ;
    NXUngetc(formatStream);
    for (j = 0; j < 1024  && (string[j]=NXGetc(formatStream)) != '\n'; j++)
      ;				/* everything up to newline */
    string[j] = '\000';
    [legendForm setStringValue:string at:i];
    [plotParam makeLineStyle:i :linetype];
    [plotParam makeSymbolType:i :symtype];
    [plotParam forceCurveColor:i :aColor];
    [plotParam adjustLineStyleMatrix:i :linetype];
    [plotParam adjustSymbolTypeMatrix:i :symtype];
    if (i==0)
      [curveColorWell setColor:aColor];
  }
  [plotParam redisplayLineStyleMatrix];
  [plotParam redisplaySymbolTypeMatrix];
  [curveNumber setIntValue:1];	/* the text field next to the curve color well */

  // Thickness:
  READ_ONE_FLOAT(f1);
  [lineThickness setFloatValue:ABS(f1)]; /* message slider and text field */
  [lineThicknessText setFloatValue:ABS(f1)];

  // Symbol size:
  READ_ONE_FLOAT(f1);
  [symbolSize setFloatValue:ABS(f1)]; /* message slider and text field */
  [symbolSizeText setFloatValue:ABS(f1)];

  // Grid on/off, dotted, and thickness:
  READ_ONE_INT(i);
  [gridOnOff setState:i];
  // Here is where the "new" (version 1.7) format file begins to differ
  // from the "old" (1.6); the new has "Grid dotted" here, the old has
  // "Border box thickness"
  //
  // Read in one character:
  i = NXGetc(formatStream);
  if (i == 'B') {		/* old */
    NXRunAlertPanel("Format File Read",
		    "Suspected \"old\" format file\n"
		    "Doing the best I can...",
		    "OK", NULL, NULL);
  //Border box thickness
    READ_ONE_FLOAT(f1);
    [borderBoxThickness setFloatValue:ABS(f1)];	/* message slider and text field */
  //Axes on/off
    READ_ONE_INT(i);
    [axesOnOff setState:i];
  //Axis thickness
    READ_ONE_FLOAT(f1);
    [axisThickness setFloatValue:ABS(f1)]; /* message slider and text field */
    [axisThicknessText setFloatValue:ABS(f1)];
  //Tic label font and size
    READ_STRING_AND_FLOAT(string, f1);
    [canvas forceTicLabelFont:string :f1];
  //Tic mark length
    READ_ONE_FLOAT(f1);
    [ticMarkLength setFloatValue:f1]; /* message slider and text field */
    [ticMarkLengthText setFloatValue:f1];
  //Minor tic marks on/off
    READ_ONE_INT(i);
    [minorTicMarksOnOff setState:i];
  }
  else {			/* new */
    READ_ONE_INT(i);
    [gridDotted setState:i];
    READ_ONE_FLOAT(f1);
    [gridThickness setFloatValue:ABS(f1)]; /* message slider and text field */
    [gridThicknessText setFloatValue:ABS(f1)];

    // Border box on/off and thickness:
    READ_ONE_INT(i);
    [borderBoxOnOff setState:i];
    READ_ONE_FLOAT(f1);
    [borderBoxThickness setFloatValue:ABS(f1)];	/* message slider and text field */
    [borderBoxThicknessText setFloatValue:ABS(f1)];

    // Frame box on/off and thickness:
    READ_ONE_INT(i);
    [frameBoxOnOff setState:i];
    READ_ONE_FLOAT(f1);
    [frameBoxThickness setFloatValue:ABS(f1)]; /* message slider and text field */
    [frameBoxThicknessText setFloatValue:ABS(f1)];

    // Axes on/off and thickness:
    READ_ONE_INT(i);
    [axesOnOff setState:i];
    READ_ONE_FLOAT(f1);
    [axisThickness setFloatValue:ABS(f1)]; /* message slider and text field */
    [axisThicknessText setFloatValue:ABS(f1)];

    // Tic label font and size:
    READ_STRING_AND_FLOAT(string, f1);
    [canvas forceTicLabelFont:string :f1];

    // Major tic marks on/off:
    READ_ONE_INT(i);
    [majorTicMarksOnOff setState:i];

    // Minor tic marks on/off:
    READ_ONE_INT(i);
    [minorTicMarksOnOff setState:i];

    // Tic mark length:
    READ_ONE_FLOAT(f1);
    [ticMarkLength setFloatValue:f1]; /* message slider and text field */
    [ticMarkLengthText setFloatValue:f1];

    // Tic mark thickness:
    READ_ONE_FLOAT(f1);
    [ticMarkThickness setFloatValue:f1]; /* message slider and text field */
    [ticMarkThicknessText setFloatValue:f1];

    // Tic mark location: 0=axes, 1=frame (2 sides), 2=frame (4 sides)
    READ_ONE_INT(i);
    if (i==0)
      [ticMarkLocation setTitle:"Axes"];
    else if (i==1)
      [ticMarkLocation setTitle:"Frame (2 sides)"];
    else
      [ticMarkLocation setTitle:"Frame (4 sides)"];
  }
  // Legend Box origin:
  if (cautious_read(formatStream) != 0) return self;
  (void) NXScanf(formatStream, "%f %f\n", &point.x, &point.y);
  [canvas forceLegendBoxOrigin:point];

  // Window origin and size:
  if (cautious_read(formatStream) != 0) return self;
  (void) NXScanf(formatStream, "%f %f %f %f\n", &windowframe.origin.x,
		 &windowframe.origin.y, &windowframe.size.width,
		 &windowframe.size.height);
  [canvas forceWindowFrame:&windowframe];

  // Text color and background color:
  if (cautious_read(formatStream) != 0) return self;
  (void) NXScanf(formatStream, "%f %f %f %f\n", &r, &g, &b, &a);
  aColor = NXConvertRGBAToColor(r, g, b, a);
  [plotParam forceTextColor:aColor];
  [textColorWell setColor:aColor];
  if (cautious_read(formatStream) != 0) return self;
  (void) NXScanf(formatStream, "%f %f %f %f\n", &r, &g, &b, &a);
  aColor = NXConvertRGBAToColor(r, g, b, a);
  [plotParam forceBackgroundColor:aColor];
  [backgroundColorWell setColor:aColor];
  NXPing();

  // Following is new for error bars:
  // Error bar base width
  READ_ONE_FLOAT(f1);
  [ebarBaseWidth setFloatValue:f1]; /* message slider and text field */
  [ebarBaseWidthText setFloatValue:f1];
  NXPing();

  // Following is new (should go with legend material above this,
  // but would be impossible to achieve backward compatibility of
  // format files.
  READ_ONE_INT(i);
  [legendOpaque setState:i];

  return self;
}

- getOpenFileName:(char *)name
{
  static const char *const fileTypes[2] = {NULL, NULL};
  id    openPanel = [[OpenPanel new] allowMultipleFiles:NO];

  [openPanel setAccessoryView:nil]; /* may have to clean out an accessory view */
  [openPanel setTitle:"Open"];	    /* make sure title is OK (cf. binary open) */
  if ([openPanel runModalForTypes:fileTypes]) {
    strncpy(name, [openPanel filename], 1024);
  }
  return self;
}

- getSaveFileName:(char *)name
{
  id savePanel = [[SavePanel new] setRequiredFileType:""];

  [savePanel setTitle:"Save Format"]; /* make sure title is correct */
  if ([savePanel runModal]) {
    strncpy(name, [savePanel filename], 1024);
  }
  return self;
}

- writeFormatFile:sender
{
  char  fname[1024];
  NXStream *formatStream;

  // Get a filename:
  [self getSaveFileName:fname];
  // Get a stream:
  if ((formatStream = NXOpenMemory(NULL, 0, NX_WRITEONLY)) == NULL)  {
    NXRunAlertPanel("Write Format", "Cannot open memory for %s",
		    "OK", NULL, NULL, fname);
    return self;
  }
  // Write the data:
  [self writeFormatData:formatStream];

  // Save the file which was memory-mapped:
  NXFlush(formatStream);
  NXSaveToFile(formatStream, fname);
  NXClose(formatStream);

  return self;
}

- readFormatFile:sender
{
  char  fname[1024];
  NXStream *formatStream;

  // Get a filename:
  [self getOpenFileName:fname];
  // Get a stream:
  if ((formatStream = NXMapFile(fname, NX_READONLY)) == NULL)  {
    return self;
  }
  // Read the data:
  [self readFormatData:formatStream];

  // Redraw automatically:
  [plotParam drawPlot:self];
  return self;
}


@end
