// WAVES.C - Program to draw summations of sinewaves under user control
#include <stdio.h>
#include <ctype.h>
#include <math.h>
#include <string.h>
#include "n:/course/elec/include/gfxlib1.h"
// define number of data points to calculate (= no of pixels on graphs)
#define DATAPOINTS 460


// main linked-list structure made global
typedef struct wave_parameters {

	double amplitude;
	double frequency;
	double phase;
	struct wave_parameters *next;
	struct wave_parameters *previous;

} WAVE;

// function to draw axes and other stuff on the screen
void draw_screen(void) {

	cleardevice(); // clear the screen
	
	// draw screen divisions, in green
	setcolor(BLUE);
	line(0,240,640,240);
	line(500,0,500,480);
	
	// draw axes
	setcolor(YELLOW);
	line(20,120,480,120);
	line(20,10,20,230);

	line(20,360,480,360);
	line(20,250,20,469);

	// Write graph labels and control panel options
	setcolor(WHITE);
	outtextxy(200,5, "Current Wave:");
	outtextxy(200,245, "Sum of all waves:");
	outtextxy(510,245, "Control Panel");
	outtextxy(510,260, "A= add wave");
	outtextxy(510,280, "D= delete wave");
	outtextxy(510,300, "E= edit wave");
	outtextxy(510,320, "N= next wave");
	outtextxy(510,340, "P= previous wave");
	outtextxy(510,450, "Q= quit program");
	


	return;
}

void print_wave_parameters(WAVE *current, char title[20]) {
	char tempstring[40];
		
	// clear the "wave parameters" area of the screen, using a flood fill 
	setfillstyle(SOLID_FILL,BLACK);
	floodfill(510,20,BLUE);
	
	setcolor(WHITE);
	outtextxy(510,5,title);
	sprintf(tempstring,"Amplitude=%.2lf", current->amplitude);
	outtextxy(510,40, tempstring);
	sprintf(tempstring,"Frequency=%.2lf", current->frequency);
	outtextxy(510,60, tempstring);
	sprintf(tempstring,"Phase=%.2lf", current->phase);
	outtextxy(510,80, tempstring);
	

	return;
}


void add_wave(WAVE **first, WAVE **current) {
	WAVE *new_wave;
	WAVE *tempstore;

	new_wave=malloc(sizeof(WAVE));
	new_wave->amplitude=0;
	new_wave->frequency=0;
	new_wave->phase=0;
	new_wave->next=NULL;
	new_wave->previous=NULL;


	if (*first==NULL) {
		*first=new_wave;
		*current=new_wave;

		return;
	}
	tempstore=NULL;
	if (current!=NULL) {
		tempstore=(*current)->next;
	}

	(*current)->next=new_wave;
	new_wave->next=tempstore;
	if (new_wave->next) new_wave->next->previous=new_wave;
	new_wave->previous=(*current);
	(*current)=new_wave;
return;
}
/* graphical_input_number allows the user to enter numbers whilst in graphics mode: it takes a string array and x and y co-ordinates; and returns the double-precision floating-point value entered. */

double graphical_input_number(char* string,int xpos,int ypos) {
	char tempstring[40],kbchar,kbstring[4], numberstring[40];
	double return_val;


	strcpy(tempstring,string);
	outtextxy(xpos,ypos,tempstring);

	/* get characters from keyboard, if they're valid, add to string and print to screen */
	do {
		kbchar=getch();
		if (isdigit(kbchar) || kbchar=='.') {
			sprintf(kbstring,"%c",kbchar);
			strcat(tempstring, kbstring);
		}
		outtextxy(xpos,ypos,tempstring);

	} while (kbchar!=13);

	strcpy(numberstring,tempstring+strlen(string));
	return_val=atof(numberstring); // convert string to floating-point number
	return(return_val);

}

void edit_wave(WAVE *current) {

	
	print_wave_parameters(current,"Editing wave");
	setcolor(RED);
	outtextxy(510,100,"New values:");
	
	current->amplitude=graphical_input_number("A=",510,125);
	current->frequency=graphical_input_number("F=",510,145);
	current->phase=graphical_input_number("P=",510,165);
	setcolor(WHITE);
	
return;
}


void delete_wave(WAVE **first, WAVE **current) {
	WAVE *item;
	item=*current;
	if (item==NULL) return;
		if (item->next ==NULL) {   // end of list
			(*current)=item->previous;
			(*current)->next=NULL;
			if (item->previous==NULL) { // only item in list
				(*first)=(*current)=NULL;
			}
		} else if (item->previous == NULL) {  // start of list
			(*current)=item->next;
			(*current)->previous=NULL;
			(*first)=(*current);
		} else {
			item->previous->next=item->next;
			item->next->previous=item->previous;
			(*current)=item->next;
		}


		free(item);
return;
}

double calculate_yval(WAVE *wave_data, double xval, double xscale) {
	double return_val;

	// formula is A*cos(2*pi*F*t+P) NB- xval is normalised to give t between 0 and 1
	return_val=wave_data->amplitude *
			cos((2 * M_PI * wave_data->frequency * xval / xscale) +
				(wave_data->phase / 180 * M_PI));
	return(return_val); 

}

double findscalefactor(WAVE *first) {
	WAVE *this_wave;
	double lowfreq=HUGE_VAL;
	
	this_wave=first;
	
	while (this_wave) {
		if (this_wave->frequency<lowfreq && this_wave->frequency>0) lowfreq=this_wave->frequency;
	this_wave=this_wave->next;		
	}

	return(0.5*lowfreq*DATAPOINTS);
	

}
void calculate_single(WAVE *first, WAVE *current, double *single_wave) {
	int i;
	double xscale;
	
	if (current->frequency==0) return;
	xscale=findscalefactor(first);
	for(i=0; i<DATAPOINTS; i++) {
		single_wave[i]=calculate_yval(current,i,xscale);
		
	}

}

void calculate_total(WAVE *first, double *total_wave) {
	int i;
	WAVE *this_wave;
	double xscale;
	this_wave=first;
	
	for(i=0; i<DATAPOINTS; i++) {
		total_wave[i]=0;
	}
	xscale=findscalefactor(first);
	while (this_wave) {
		if(this_wave->frequency==0) { this_wave=this_wave->next; continue;}
		for(i=0; i<DATAPOINTS; i++) {
			total_wave[i]+=calculate_yval(this_wave,i,xscale);
		
		}
	this_wave=this_wave->next;
	}
}

void plot_waves(double *single_wave, double *total_wave, WAVE *first) {
	int i;
	double plotx,ploty;
	double maxval=0;
	double xscale=0;
	double lowfreq=0;
	double time=0;
	char label[20];
	
	xscale=findscalefactor(first);
	lowfreq=2*xscale/DATAPOINTS;
	time=1/lowfreq;
	
	for(i=0; i<DATAPOINTS; i++) {
		if(maxval<total_wave[i]) maxval=total_wave[i];	
	}
	moveto(20,120);
	setcolor(GREEN);
	for(i=0; i<DATAPOINTS; i++) {
		plotx=20+i;
		ploty=120-(single_wave[i]/ maxval *110);
		if (i>0) lineto(plotx, ploty);
			else moveto(plotx, ploty);
	}
	setcolor(WHITE);
	moveto(20,360);
	setcolor(GREEN);
	for(i=0; i<DATAPOINTS; i++) {
		plotx=20+i;
		ploty=360-(total_wave[i]/ maxval *110);
		if (i>0) lineto(plotx, ploty);
			else moveto(plotx, ploty);
	}

	setcolor(WHITE);
	sprintf(label, "%2.2E", maxval);
	outtextxy(5,10, label);
	outtextxy(5,120, "0");
	outtextxy(5,250, label);
	outtextxy(5,360,"0");

	sprintf(label, "%2.2E", time);
	outtextxy(DATAPOINTS/2,125, label);
	outtextxy(DATAPOINTS/2,365, label);
	sprintf(label, "%2.2E", time*2);
	outtextxy(DATAPOINTS,125, label);
	outtextxy(DATAPOINTS,365, label);
	

}

// main function
int main(void) {
	WAVE *first=NULL;
	WAVE *current=NULL;
	char keypress;
	
	double single_wave[DATAPOINTS];
	double total_wave[DATAPOINTS];	

	graphics_on();
	add_wave(&first, &current);
	draw_screen();
	print_wave_parameters(current, "Wave parameters");
	
	do {
		//printf("F=%lf C=%lf\n",first->frequency, current->frequency);
		keypress=getch();
		switch(keypress) {
			case 'a':
			case 'A':
				add_wave(&first, &current); edit_wave(current);break;
			case 'e':
			case 'E':
				edit_wave(current); break;
			case 'n':
			case 'N':
				if(current->next) current=current->next; break;
			case 'p':
			case 'P':
				if(current->previous) current=current->previous; break;
			case 'd':
			case 'D':
				delete_wave(&first, &current); break;
		}
		draw_screen();
		
		if (current!=NULL && first!=NULL) {

			print_wave_parameters(current, "Wave parameters");
		
			if (current->frequency!=0) {
				calculate_single(first, current, single_wave);
		
				calculate_total(first, total_wave);
		
				plot_waves(single_wave, total_wave, first);
			}
		}
		

	} while (keypress!='q' && keypress!='Q');
	graphics_off();
	
	

return(0);
}

