/**************************************************
 * SynthBuilder
 * Copyright 1993 Nick Porcaro All Rights Reserved
 **************************************************/

/*
 * UGArgSlider.m
 */

#import <stdlib.h>
#import <stddef.h>
#import <string.h>
#import <appkit/Application.h>
#import <appkit/Control.h>
#import <appkit/Slider.h>
#import <appkit/Window.h>
#import <appkit/Panel.h>
#import "UGArgSlider.h"
#import "UGDef.h"
#import "FakeUG.h"
#import "FakeSynthPatch.h"

@implementation UGArgSlider

static char argValueStr[256];

+ initialize
 /* Set the version. This can be used in a later version to distinguish older
  * formats when unarchiving documents. 
  */
{
  [UGArgSlider setVersion:1];
  return self;
}

- init
{
  theFakeUG = nil;
  theArgField = nil;
  theArgNum = -1;
  inspector = nil;
  return self;
}

- initFromFakeUG: aFakeUG ArgNum:(int) argnum ArgField:aTextField ArgType:(int) type
/* Call this after allocating the object */
{
  double cur_val, max_val;
  NXRect virginRect;
  
  theFakeUG = aFakeUG;
  theArgNum = argnum;
  theArgField = aTextField;
  theArgType = type;

  if (! inspector)
    {
      [NXApp loadNibSection:"UGArgSlider.nib" owner:self withNames:YES];	
      [inspector getFrame:&virginRect];
    }

  if (! inspectorRectJustLoaded)
    {
      inspectorRect = virginRect;
    }

  cur_val =  [theArgField doubleValue];
  if (cur_val <= 0)
    {
      /* Make a wild ass guess for the max */
      max_val = 10.0;
    }
  else
    {
      /* Base the max on twice the current value */
      max_val = 2 * cur_val;
    }

  [self setMaxVal:max_val];
  [self setMinVal:0];
  if (theArgType == DOUBLE)
    {
      [theSlider setContinuous:YES];
      [theSliderValue setDoubleValue:cur_val];
      [theSlider setDoubleValue:cur_val];
    }
  else
    {
      [theSlider setContinuous:NO];
      [theSliderValue setIntValue:(int) cur_val];
      [theSlider setIntValue:cur_val];
    }

  sprintf(argValueStr, "%s %s", 
	  [aFakeUG getName],
	  [aFakeUG getArgName:theArgNum]);

  [inspector setTitle:argValueStr];
  return self;
}

- updateInspectorTitle
{
  sprintf(argValueStr, "%s %s", 
	  [theFakeUG getName],
	  [theFakeUG getArgName:theArgNum]);
  [inspector setTitle:argValueStr];
  return self;
}

- setMaxVal:(double) val
{
  int ival;

  [theSlider setMaxValue:val];
  if (theArgType == DOUBLE)
    {
      [theMaxField setDoubleValue:val];
    }
  else
    {
      ival = val;  /* Convert double to int */
      [theMaxField setIntValue:ival];
    }

  return self;
}

- setMinVal:(double) val
{
  int ival;

  [theSlider setMinValue:val];
  if (theArgType == DOUBLE)
    {
      [theMinField setDoubleValue:val];
    }
  else
    {
      ival = val;  /* Convert double to int */
      [theMinField setIntValue:ival];
    }

  return self;
}

- takeSliderValueFrom:aField
/* This can be called from another object to make theSlider
 * and theSliderValue reflect the new value.  It will also
 * adjust max and min fields if they are overblown
 */
{
  double val;
  double ival;

  if (theArgType == DOUBLE)
    {
      val = [aField doubleValue];
      if (val > [(Slider *) theSlider maxValue])
	{
	  [self setMaxVal:val];
	}
      else if (val < [(Slider *) theSlider minValue])
	{
	  [self setMinVal:val];
	}
      [theSlider setDoubleValue:val];
      [theSliderValue setDoubleValue:val];
    }
  else
    {
      ival = [aField intValue];
      if (ival > [(Slider *) theSlider maxValue])
	{
	  [self setMaxVal:ival];
	}
      else if (ival < [(Slider *) theSlider minValue])
	{
	  [self setMinVal:ival];
	}
      [theSlider setIntValue:ival];
      [theSliderValue setIntValue:ival];
    }

  return self;
}

- takeSliderMaxFrom:sender
{
  [self setMaxVal:[sender doubleValue]];
  return self;
}

- takeSliderMinFrom:sender
{
  [self setMinVal:[sender doubleValue]];
  return self;
}

- pingUGInspector
{
  sprintf(argValueStr, "%f", [theSlider doubleValue]);
  [theFakeUG setArg:theArgNum To:argValueStr];
  return self;
}
 
- dispatchSliderValue:sender
/* This gets called when the slider is moved,
 * or when a value is entered in theSliderValue
 */
{
  double dval;
  int ival;

  if (theArgType == DOUBLE)
    {
      dval = [sender doubleValue];
      sprintf(argValueStr, "%f", dval);
      if ([sender isKindOfClassNamed: "TextField"])
	{
	  [theSlider setDoubleValue:dval];
	}
      else
	{
	  [theSliderValue setStringValue:argValueStr];
	}
    }
  else /* Assume it's int for now */
    {
      ival = [sender intValue];
      sprintf(argValueStr, "%d", ival);
      if ([sender isKindOfClassNamed: "TextField"])
	{
	  [theSlider setIntValue:ival];
	}
      else
	{
	  [theSliderValue setStringValue:argValueStr];
	}
    }

  /* Send this back to the field on the inspector
   * and the theFakeUG
   */
  [theFakeUG setArg:theArgNum To:argValueStr];
  return self;
}


- displayInspector
{
  NXRect virginRect;

  if (! inspector)
    {
      [NXApp loadNibSection:"UGArgSlider.nib" owner:self withNames:YES];	
      [inspector getFrame:&virginRect];
    }

  if (inspectorRectJustLoaded)
    {
      if (useInspectorRect)
	{
	  [inspector placeWindowAndDisplay:&inspectorRect];
	  useInspectorRect = NO;
	}
      inspectorRectJustLoaded = NO;
    }
  else
    {
      inspectorRect = virginRect;
    }

  /* This line adds the inspector to the WindowsMenu 
   * This happens automatically for FakePatchPoints and FakeUGs
   * (maybe because they are separate Views?).
   */
  sprintf(argValueStr, "%s %s", 
	  [theFakeUG getName],
	  [theFakeUG getArgName:theArgNum]);
  [NXApp addWindowsItem: inspector title:argValueStr filename:NO];
  /* doHighlight toggle prevents the instance
   * from being highlighted twice
   */
  doHighlight = NO;
  [inspector makeKeyAndOrderFront:self];
  inspectorDisplayed = YES;
  doHighlight = YES;
  return self;
}

- highlightSelf
{
  if ( (![[theFakeUG getSP] windowMiniaturized]) && doHighlight)
    {
      [self pingUGInspector];
      [[theFakeUG inspector] orderFront:self];
    }

  return self;
}

- displayInspectorIfWasActive
{
  if (inspectorDisplayed)
    {
      [self displayInspector];
    }

  return self;
}

- inspectorClosed
/* Called by the inspector's delegate */
{
  inspectorDisplayed = NO;
  return self;
}

- closeInspector
{
  [inspector performClose:self];
  inspectorDisplayed = NO;
  return self;
}

- write:(NXTypedStream *) stream
{
  [super write:stream];
  if (inspector)
    {
      useInspectorRect = YES;
      [inspector getFrame:&inspectorRect];
    }
  else
    {
      useInspectorRect = NO;
      inspectorRect.origin.x = -1;
      inspectorRect.origin.y = -1;
      inspectorRect.size.width = -1;
      inspectorRect.size.height = -1;
    }

  NXWriteTypes(stream, "d", &inspectorRect.origin.x);
  NXWriteTypes(stream, "d", &inspectorRect.origin.y);
  NXWriteTypes(stream, "d", &inspectorRect.size.width);
  NXWriteTypes(stream, "d", &inspectorRect.size.height);
  NXWriteTypes(stream, "c", &useInspectorRect);
  NXWriteTypes(stream, "c", &inspectorDisplayed);
  
  return self;
}

- read:(NXTypedStream *) stream
{
  int version;
  [super read:stream];

  useInspectorRect = NO;
  inspectorRectJustLoaded = NO;
  version = NXTypedStreamClassVersion(stream, "UGArgSlider");

  switch (version)
    {
      case(1):
	{
	  NXReadTypes(stream, "d", &inspectorRect.origin.x);
	  NXReadTypes(stream, "d", &inspectorRect.origin.y);
	  NXReadTypes(stream, "d", &inspectorRect.size.width);
	  NXReadTypes(stream, "d", &inspectorRect.size.height);
	  NXReadTypes(stream, "c", &useInspectorRect);
	  NXReadTypes(stream, "c", &inspectorDisplayed);
	  inspectorRectJustLoaded = YES;
	  break;
	}
      default:
        {
	  /* Versions before 1 did not archive anything */
	  break;
	}
    }

  return self;
}

@end


