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

#import <stdlib.h>
#import <math.h>
#import <stddef.h>
#import <ctype.h>
#import <string.h>
#import <objc/NXBundle.h>
#import <appkit/Application.h>
#import <appkit/Panel.h>
#import <appkit/Control.h>
#import <musickit/Envelope.h>
#import "Utilities.h"
#define EOS '\0'

char *getAppName()
{
  /* this is the only place where the name of the program should be mentioned */
  return "SynthBuilder";
}

@implementation Utilities

static char TempString[1024];

- init
{
  [super init];
  [NXApp loadNibSection:"Utilities.nib" owner:self withNames:YES];
  return self;
}

- sortList:(List *) aList sortFunction:(ptrToIntFcn) fcn
{
  int count;

  /* Is it worth sorting? */
  if (! fcn)
    {
      return self;
    }

  count = [aList count];
  if (count <= 1)
    {
      return self;
    }

  /* Sort the array.  Hopefully the
   * List object will act properly after this!!
   */
  qsort((void *)aList->dataPtr, count, sizeof(id), fcn);
  return self;
}

- (BOOL) checkName: (char *) aName
/* The names of FakeUGs and FakePatchPoints must be a legal
 * C identifier
 */
{
  int len, i;
  
  if (! aName)
    {
      NXRunAlertPanel(getAppName(), "Invalid name", NULL, NULL, NULL);
      return NO;
    }

  len = strlen(aName);
  if (len > 0)
    {
      if (! isalpha(aName[0]))
	{
	  NXRunAlertPanel(getAppName(), "Name must start with alpha character",
			  NULL, NULL, NULL);
	  return NO;
	  }
      
      for (i=1; i<len; i++)
	{
	  if ( ! ( isalnum(aName[i]) || aName[i] == '_'))
	    {
	      sprintf(TempString, "Illegal character in %s: %c",
		      aName, aName[i]);
	      NXRunAlertPanel(getAppName(), TempString, NULL, NULL, NULL);
	      return NO;
	    }
	}
    }

  return YES;
}

- (char *) getAppDir
{
  char *dir;
  int len;
  static char temp[1024];

  dir = (char *) [[NXBundle mainBundle] directory];
  if (! dir)
    {
      NXRunAlertPanel(getAppName(), "Error getting bundle directory", NULL, NULL, NULL);
      return NULL;
    }

  /* Nightmare:  If the directory does not end in .app
   * it probably means we are under GDB. Add
   * the .app dir name to the end of the dir if this is the case
   */
  if (getenv("NICKGDB"))
      {
	len = strlen(dir);
	if ( ! ((dir[len-1] == 'p') && 
		(dir[len-2] == 'p') &&
		(dir[len-3] == 'a') &&
		(dir[len-4] == '.'))  )
	  {
	    sprintf(temp, "%s/%s.app", dir, getAppName());
	    dir = temp;
	  }
      }

  return dir;
}

- displayInspector:sender
{
  [theInspector makeKeyAndOrderFront:self];
  return self;
}

- getPhaseIncrement:sender
{
  double freq;
  double phase_inc;
  double mag;
  double srate;
  double table_len;

  freq = [sender floatValue];
  table_len = [theTableLength doubleValue];
  srate = [theSrate doubleValue];
  if (srate <= 0)
    {   /* bogus */
      srate = 44100;
    }

/*******************
   From /Net/cmn14/projects/musicKit/mk/unitgenerators/OscgafUGs.m
   The formula is:

       phase_inc = (freq * _mag) / MK_OSCFREQSCALE

   where _mag = (double)tableLength / [orchestra samplingRate];
   and MK_OSCFREQSCALE = 256.0,

*********************/

  mag = table_len / srate;
  phase_inc = (freq * mag)/256.0;
  [toPhaseInc setFloatValue:phase_inc];
  return self;
}

- getFrequency:sender
{
  double freq;
  double phase_inc;
  double table_len;
  double mag;
  double srate;

  phase_inc = [sender floatValue];
  table_len = [theTableLength doubleValue];
  srate = [theSrate doubleValue];
  if (srate <= 0)
    {   /* bogus */
      srate = 44100;
    }

/*******************
   From /Net/cmn14/projects/musicKit/mk/unitgenerators/OscgafUGs.m
   The formula is:

       phase_inc = (freq * _mag) / MK_OSCFREQSCALE

       freq = (phase_inc/_mag) * MK_OSCFREQSCALE

   where _mag = (double)tableLength / [orchestra samplingRate];
   and MK_OSCFREQSCALE = 256.0,

   substituting:

*********************/

  mag = table_len / srate;
  freq = (phase_inc/mag) * 256.0;
  [toFreq setFloatValue:freq];
  return self;
}

- (char *) envelopeToText:anEnvelope
{
  int i, count;
  double *x, *y;
  unsigned int env_size;
  int stick;
  BOOL do_stick = NO;
  char *envString;

  /* Do something to the envelope so the File's Owner
   * object can do something cool with it
   */
  if (! anEnvelope)
    {
      return NULL;
    }

  count = [anEnvelope pointCount];
  if (count <= 0)
    {
      return NULL;
    }

  x = [anEnvelope xArray];
  y = [anEnvelope yArray];
  stick = [anEnvelope stickPoint];
  if (stick != MAXINT)
    {
      do_stick = YES;
    }

  if (! (x && y) )
    {
      return NULL;
    }
  
  /* The max a coordinate of an envelope can be is %03.5 = 9 characters
   * add a comma and paren for each coordinate (make it 4 to be safe)
   * Then we have 9+5 = 14 characters for each coordinate
   * or 28 characters for each point
   */
  env_size = count * 28 + 256; /* add 256 for extra fudge */
  envString = (char *) NXZoneCalloc([self zone], env_size, sizeof(char));
  if (! envString)
    {
      NXRunAlertPanel(getAppName(), 
		      "Grave danger -- malloc error", 
		      NULL, NULL, NULL);
      return NULL;
    }

  strcpy(envString, "");
  for (i=0; i<count; i++)
    {
      sprintf(envString, "%s(%4.4f, %f)",
	      envString, x[i], y[i]);
      if (do_stick && (stick == i))
	{
	  strcat(envString, "|");
	}
    }

  return envString;
}


- textToEnvelope: (char *) aString
{
  int i, j;
  int pointCount = 0;
  int stickPoint = -1;
  double *xArray, *yArray, *smoothingArray;
  id newEnv;

  if (! aString)
    {
      return nil;
    }

  for (i=0; aString[i] != '\0'; i++)
    {
      if (aString[i]=='|')
	{
	  stickPoint = pointCount-1;
	}
      if (aString[i]=='(')
	pointCount++;
    }

  if (pointCount <= 0)
    {
      return nil;
    }

  xArray = (double *) NXZoneCalloc([self zone], pointCount, sizeof(double));
  yArray = (double *) NXZoneCalloc([self zone], pointCount, sizeof(double));
  smoothingArray =  (double *) NXZoneCalloc([self zone], pointCount, sizeof(double));
  
  j=0;
  for (i=0; i<pointCount; i++)
    {
      xArray[i] = yArray[i] = 0.0;
      smoothingArray[i] = 1.0;
      while (aString[j]!='(') 
	{
	  j++;
	}
      sscanf(aString+j, "(%lf,%lf,%lf)",
	     &xArray[i], &yArray[i], &smoothingArray[i]);
      j++;
    }

  newEnv = [[[Envelope alloc] init]
            setPointCount: pointCount
	    xArray: xArray
	    orSamplingPeriod: 1.0
	    yArray: yArray
	    smoothingArray: smoothingArray
	    orDefaultSmoothing: 1.0
	    ];

  if (stickPoint != -1)
    {
      [newEnv setStickPoint:stickPoint];
    }
  
  return newEnv;
}

#define MAX_PATHLEN 1024
#define RETURN_LEAF(Path) \
    strcpy(buffer, Path); \
    len = strlen(buffer); \
    if (len <= 0 || len >= MAX_PATHLEN) \
        return NULL; \
    i = len; \
    while (buffer[i] != '/' && i >= 0) i--; \
    return &buffer[i+1];

- (char *) getPathLeaf: (char *) path
{
  static char buffer[1024];   /* Character buffer */
  static char leaf[256];   /* Buffer to hold leaf directory */
  
  int len;   /* Length of path                                                 */
  int i, j;   /* Counter                                                       */
  int position;   /* Position in string                                        */
  int leading_slash = 0;   /* Position of a leading slash                      */
  int slash_count = 0;   /* Number of slashes in a path                        */

  strcpy(leaf,path);
  len = strlen(leaf);
  if (len <= 0 || len >= MAX_PATHLEN)
    { /* Crap */
      return NULL;
    }
  
  if (len > 1)
    {
      if (strcmp(leaf,"..") == 0)
	{
	  RETURN_LEAF((char *)"..")
	  }
      if (strcmp(leaf,"//") == 0)
	{
	  return leaf;
	}
      
      position = len - 1;
      for (i = len - 1 ; i >= 0; i--)
	{
	  if (leaf[i] == '/')
	    {
	      position = i;
	      slash_count++;
	    }
	  else
	    {
	      break;
	    }
	  leaf[position] = EOS;
	  position--;
	}
      
      /* Special case: all characters are slashes */
      
      if (slash_count == len)
	{
	  strcpy(buffer,"//");
	  return buffer;
	}
      
      /* Begining of string */
      
      if (leaf[0] == '/' )
	{
	  i = 0;
	  while (leaf[leading_slash] != '/' && leading_slash <= len)
	    {
	      if (leaf[i] == '/')
		leading_slash++;
	      else
		break;
	    }
	  if (i > 2)
	    { /* Extra slashes at begining of string */
	      j = 0;
	      for (i = leading_slash; i <= len; i++)
		{
		  buffer[j++] = leaf[i];
		}
	      buffer[j] = EOS;
	      strcpy(leaf,"//");
	      strcat(leaf,buffer);
	    }
	}
      
      /* Position to the first non-extraneous slash */
      while (leaf[position] != '/' && position >= 0) position--;
      
      /* Deal with case where slash is first character or no slashes in path */
      if (position == 0)
	{
	  if (leaf[position] == '/')
	    { /* Case where something line '/foo' was entered */
	      return &leaf[position+1];
	    }
	  else
	    { /* Case where a leaf like 'my_file' was entered (no slashes (/'s)) */
	      return leaf;
	    }
	}
      else
	{
	  /* Regular leafy path */
	  return &leaf[position+1];
	}
    }
  else
    {
      /* Single character paths. All special cases */
      if ( (strcmp(leaf,"/") == 0) ||
	  (strcmp(leaf,".") == 0) ||
	  (strcmp(leaf,"~") == 0) )
	{
	  RETURN_LEAF((char *)leaf)
        }
        { /* A single non-special character */
	return leaf;
        }
    }
}

@end
  
  
