/* -----------------------------------------------------------------------------------	*/
/* FrontEnd.m -- Copyright (c) 1990 Ed Hill						*/
/*											*/
/*   This program is free software; you can redistribute it and/or modify it under the 	*/		
/*   terms of the GNU General Public License as published by the Free Software		*/		
/*   Foundation; either version 1, or (at your option) any later option.		*/		
/*											*/										
/*   This program is distributed in the hope that it will be useful, but WITHOUT ANY	*/
/*   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A	*/		
/*   PARTICULAR PURPOSE.  See the GNU General Public License for more details.		*/			
/*											*/
/*   You should have received a copy of the GNU General Public License along with this 	*/	
/*   this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, 	*/
/*   Cambridge, MA 02139, USA, or send electronic mail to the author.			*/	
/* ------------------------------------------------------------------------------------	*/
/*  										 	*/
/* ------------------------------------------------------------------------------------	*/
/* I am aware that this program does nothing that hasn't been done before.  I simply	*/
/* wanted to see how easy it was to add a simple user interface to an existing non-user	*/
/* friendly program.  I would like to add functionality to this program in the future	*/
/* If you have suggestions or ideas that would be useful in future releases plese feel	*/
/* to write me at edhill@shumun.weeg.uiowa.edu (NeXT).  I also welcome any programming	*/
/* advice (I would have done this different, etc...).					*/
/* ------------------------------------------------------------------------------------	*/

#import "GNULicense.h"		
#import "FrontEnd.h"		
#import <appkit/appkit.h>
#import <appkit/publicWraps.h>	
#import <appkit/nextstd.h>	
#import <soundkit/soundkit.h>
#import <sound/sound.h>
#import <string.h>		
#import <stdio.h>

@implementation FrontEnd

/* ------------------------------------------------------------------------------------	*/
/* The following methods set up the necessary outlets. 					*/
/* Which are connected via the Interface Builder.					*/
/* ------------------------------------------------------------------------------------	*/
-setFrontEndWindow:anObject
{
    FrontEndWindow = anObject;
    return self;
}

-setNameView:anObject
{
    NameView = anObject;
    return self;
}

-setNameView2:anObject
{
    NameView2 = anObject;
    return self;
}

-setIconBox:anObject
{
    IconBox = anObject;
    return self;
}

-setIconBox2:anObject
{
    IconBox2 = anObject;
    return self;
}

-setSampleButton:anObject
{
    SampleButton = anObject;
    popup = [PopUpList new];	  
    [popup addItem:"Sample Rate = 22 KHz"];
    [popup addItem:"Sample Rate = 11 KHz"];
    [[popup setTarget:self] setAction:@selector(changeSampleRate:)];
    NXAttachPopUpList (SampleButton, popup);    
    return self;
}

-setConvertButton:anObject
{
    ConvertButton = anObject;
    return self;
}

-setErrorText:anObject
{
    ErrorText = anObject;
    [ErrorText setStringValue:""];
    return self;
}

/* ------------------------------------------------------------------------------------	*/
/* The following four methods are delegate methods that add functionality to the 	*/
/* existing methods are already defined by the Application class.  I believe that I  	*/
/* am doing something wrong because I have been told that I shouldn't have to redefine	*/
/* the -unhide method.  If you can tell what I am doing wrong I would greatly 		*/
/* appreciate it.									*/
/* ------------------------------------------------------------------------------------	*/
- appDidInit:sender
{
    id		 	theListener;
    id		 	theSpeaker;
    unsigned int	windowNum;
    
    theSpeaker = [NXApp appSpeaker];
    NXConvertWinNumToGlobal([FrontEndWindow windowNum], &windowNum);
    theListener = [NXApp appListener];
    [theListener setDelegate:self];
    [theListener privatePort];
    [theListener addPort];
    [theSpeaker setSendPort: NXPortFromName(NX_WORKSPACEREQUEST, NULL)];
    [theSpeaker registerWindow: windowNum toPort:[theListener listenPort]];
    isPaused = FALSE;
    sampleRate = 1;
    NX_FREE( nextSoundName );
    NX_MALLOC( nextSoundName, char, strlen( "" ) + 1 );
    strcpy( nextSoundName, "" );
    NX_FREE( macSoundName );
    NX_MALLOC( macSoundName, char, strlen( "" ) + 1 );
    strcpy( macSoundName, "" );
    NX_FREE( iconPath );
    NX_MALLOC( iconPath, char, strlen( "" ) + 1 );
    strcpy( iconPath, "" );
    NothingHere = TRUE;
    [ConvertButton setEnabled:NO];
    
    return self;
}


- (int)iconEntered:(int)windowNum 
	at:(double)x 
	:(double)y 
	iconWindow:(int)iconWindowNum 
	iconX:(double)iconX 
	iconY:(double)iconY 
	iconWidth:(double)iconWidth 
	iconHeight:(double)iconHeight 
	pathList:(const char *)pathList
{
    NX_FREE( iconPath );
    NX_MALLOC( iconPath, char, strlen( pathList ) + 1 );
    strcpy( iconPath, pathList );
    return 0;
}

- unhide:sender
{
    [NXApp unhide:self];
    return self;
}

- (int)iconReleasedAt:(double)x :(double)y ok:(int *)flag
{
    NXPoint	iconViewOrigin;
    id		currentIcon;
    BOOL	OKflag;
    char 	*fileName;
    
    iconViewOrigin.x = iconViewOrigin.y = 0.0;
    NX_FREE( nextSoundName );
    NX_MALLOC( nextSoundName, char, strlen( "" ) + 1 );
    strcpy( nextSoundName, "" );    	
    OKflag = [self setUpDraggedInFiles:self];

    if( OKflag )
    {
        NothingHere = FALSE;
	[NXApp activateSelf:YES]; 
	[FrontEndWindow makeKeyAndOrderFront:self];   
	*flag = 1;
	[ErrorText setStringValue:""];
	[ConvertButton setEnabled:YES];

	[IconBox lockFocus];
	currentIcon = [Bitmap findBitmapFor:"Macsnd"];
	[currentIcon composite:NX_COPY toPoint:&iconViewOrigin];
	[IconBox unlockFocus];
	fileName = strrchr(macSoundName, '/');
	fileName++;
	[NameView setStringValue:fileName];
	
	/* This is probably a stupid way to do this but its quick */
	[IconBox2 lockFocus];
	currentIcon = [Bitmap findBitmapFor:"Blank"];
	[currentIcon composite:NX_COPY toPoint:&iconViewOrigin];
	[IconBox2 unlockFocus];
	[NameView2 setStringValue:""];
    }
    else
    {
	/* This is probably a stupid way to do this but its quick */
        NothingHere = TRUE;
	[IconBox lockFocus];
	currentIcon = [Bitmap findBitmapFor:"Blank"];
	[currentIcon composite:NX_COPY toPoint:&iconViewOrigin];
	[IconBox unlockFocus];
	[NameView setStringValue:""];
	[IconBox2 lockFocus];
	currentIcon = [Bitmap findBitmapFor:"Blank"];
	[currentIcon composite:NX_COPY toPoint:&iconViewOrigin];
	[IconBox2 unlockFocus];	
	[NameView2 setStringValue:""];
	[ConvertButton setEnabled:NO];
	
	NX_FREE( macSoundName );
	NX_MALLOC( macSoundName, char, strlen( "" ) + 1 );
	strcpy( macSoundName, "" );    	
	*flag = 0;
    }
    return 0;
}

/* ------------------------------------------------------------------------------------	*/
/* This method checks to make sure that the file that has been dragged from the 	*/
/* workspace is the proper type (ending with a ".macsnd" suffix).			*/
/* ------------------------------------------------------------------------------------	*/
-setUpDraggedInFiles:sender
{
    char	*tab;
    char	*extension;
    BOOL	hadInvalid;
    
    hadInvalid = FALSE;
    if( iconPath )
    {
	tab = strchr(iconPath,'\t');
	if (tab) 
	    *tab = '\0';
	extension = strrchr(iconPath,'.');
	if (extension)
	{
   	    if (!strcmp(extension,".macsnd"))
	    {
		NX_FREE( macSoundName );
		NX_MALLOC( macSoundName, char, strlen( iconPath ) + 1 );
		strcpy( macSoundName, iconPath );
	    }
	    else
	    {
	        hadInvalid = TRUE;
		[ErrorText setStringValue:"NOT '.macsnd' Extension"];
	    }
	} 
	else 
	{
	    hadInvalid = TRUE;
	    [ErrorText setStringValue:"NOT '.macsnd' Extension"];
  	}
    }
    else
        [ErrorText setStringValue:"Workspace Error"];
    
    if( hadInvalid )
        return FALSE;
    else
        return TRUE;
}

/* ------------------------------------------------------------------------------------	*/
/* The following three methods perform functions on the converted sound.  I added this	*/
/* functionality so that the user can instantly tell if the conversion was completed	*/
/* succesfully.										*/
/* ------------------------------------------------------------------------------------	*/
-playsound:sender
{
    if( strcmp( nextSoundName, "" ) )
    {
        if( currentSound )
            [currentSound play];
	else
	    [ErrorText setStringValue:"Sound Not Initialized"];
    }
    else
        [ErrorText setStringValue:"No NeXT Sound to Play"];
    
    return self;	    
}

-stopsound:sender
{
    if( strcmp( nextSoundName, "" ) )
    {
        if( currentSound )
            [currentSound stop];
	else
	    [ErrorText setStringValue:"Sound Not Initialized"];
    }
    else
        [ErrorText setStringValue:"No NeXT Sound to Stop"];
    
    return self;	    
}

-pausesound:sender
{
    if( strcmp( nextSoundName, "" ) )
    {
	if( currentSound )
	{
	    if( isPaused )
	    {
		isPaused = FALSE;
		[currentSound resume:self];
	    }
	    else
	    {
		isPaused = TRUE;
		[currentSound pause:self];
	    } 
	}
	else
	    [ErrorText setStringValue:"Sound Not Initialized"]; 
    }
    else
        [ErrorText setStringValue:"No NeXT Sound to Pause"];
    
    return self;	        
}

/* ------------------------------------------------------------------------------------	*/
/* This method is activated when the user activates the PopUpList.  It simply sets 	*/
/* the sampleing rate for the user.							*/
/* ------------------------------------------------------------------------------------	*/
-changeSampleRate:sender;
{
    if( NothingHere )
        [ConvertButton setEnabled:NO];
    else
        [ConvertButton setEnabled:YES];
    
    if( strcmp( [[sender selectedCell] title], "Sample Rate = 22 KHz" ) )
        sampleRate = 0;
    else
        sampleRate = 1;
    return self;
}

/* ------------------------------------------------------------------------------------	*/
/* This function takes care of most of the error checking and visual effects that take	*/
/* take place before the sound is converted.  It changes the name of the original file	*/
/* and places the NeXT Sound icon in its respective dock.				*/
/* ------------------------------------------------------------------------------------	*/
-convertMactoNeXT:sender
{
    NXPoint	iconViewOrigin;
    id		currentIcon;
    char	*fileName;
    char 	*tempName;
    
    iconViewOrigin.x = iconViewOrigin.y = 0.0;

    if( strcmp( macSoundName, "" ) )
    {    
	[IconBox2 lockFocus];
	currentIcon = [Bitmap findBitmapFor:"Sound"];
	[currentIcon composite:NX_COPY toPoint:&iconViewOrigin];
	[IconBox2 unlockFocus];
    
	NX_FREE( nextSoundName );
	NX_MALLOC( nextSoundName, char, strlen( macSoundName ) + 1 );
	strcpy( nextSoundName, macSoundName );    
	fileName = strrchr(nextSoundName, '/');
	fileName++;
	tempName = fileName;
	while( *tempName++ != '.' );
	strcpy( tempName, "snd\0" );
	[NameView2 setStringValue:fileName];
	[self _actualMactoNeXTcode:sampleRate];
	currentSound = [Sound newFromSoundfile:nextSoundName];
	[ErrorText setStringValue:""];
	[ConvertButton setEnabled:NO];
	return self;
    }
    else
       [ErrorText setStringValue:"No Sound to Convert"];
}

/* ------------------------------------------------------------------------------------	*/
/* This is the actual code that converts the Macintosh sound to a NeXT sound.  I did 	*/
/* not write this code it was written by Robert Hood.  I honestly haven't even looked 	*/
/* at this code.  I hope to rewrite this function in a future release so that I can fix	*/
/* a known bug in it.									*/
/* ------------------------------------------------------------------------------------	*/
-_actualMactoNeXTcode:(int)theSampleRate
{
    FILE *input, *output;
    char filename [256], outname [256];
    int temp,last;
    int fast;
    char temp2;
    unsigned char ch;
    
    strcpy (filename, macSoundName);
    fast = theSampleRate;
    
    
    if ((input = fopen (filename, "rb")) == 0)
	    {
	    perror (filename);
	    exit (-1);
	    }
    strcpy (outname, nextSoundName);
    if ((output = fopen (outname, "wb")) == 0)
	    {
	    perror (outname);
	    exit (-1);
	    }
    temp = 0x2E736E64;  /* magic */
    fwrite (&temp, 4, 1, output);
    temp = 0x0000001C;  /* start of sound */
    fwrite (&temp, 4, 1, output);
    temp = 0x98 + 1;   /* end of sound...doesn't seem to matter */
    fwrite (&temp, 4, 1, output);
    temp = 0x00000003; /* ?? */
    fwrite (&temp, 4, 1, output);
    temp = 0x00005622;  /* sample frequency */
    fwrite (&temp, 4, 1, output);
    temp = 0x00000001; /* ?? */
    fwrite (&temp, 4, 1, output);
    temp = 0x00000000;  /* space filler? */
    fwrite (&temp, 4, 1, output);
    temp = 0x00000000;  /* space filler? */
    fwrite (&temp, 4, 1, output);

    last = 0;
    while (!feof (input))
	    {
	    ch = getc (input);

	    temp = ch;
	    temp = (128 - temp) * (int)64;

	    if (!fast)   /* 11 kHz is simulated by averaging the input */
		    {
		    last = (last + temp) / 2;
		    putc ((last >> 8) & 0xFF, output);
		    putc ((last) & 0xFF, output);
		    last = temp;
		    }
	    putc ((temp >> 8) & 0xFF, output);
	    putc ((temp) & 0xFF, output);
	    }
    fclose (input);
    fclose (output);
}


/* ------------------------------------------------------------------------------------	*/
/* This brings up the GNU Panel which shows the Public GNU License.			*/
/* ------------------------------------------------------------------------------------	*/
-showGNULicense:sender
{
   if (myGNULicense == nil)
      myGNULicense = [GNULicense new];

   /* Not really necessary, but kinda cutesy. */
   if ([sender isKindOf:[Control class]] == YES) {
      [sender setTarget:myGNULicense];
      [sender setAction:@selector (showLicense:)];
   }

   [myGNULicense showLicense:self];
   return self;
}









@end
