#include "../include/types.h"

void CreateWidget(uint16_t x, uint16_t y, 
		 uint16_t w, uint16_t h, 
		 uint32_t color, uint32_t color2, float shade,
		 bool active,
		 bool stretchWidth, bool stretchHeight,
		 bool offsetX, bool offsetY,
		 bool offsetW, bool offsetH,
		 uint8_t dividex, uint8_t dividey,
		 uint8_t dividew, uint8_t divideh,
		 char* text, uint16_t textx, uint16_t texty,
		 char* image, char* image2, 
		 uint8_t actionType, uint8_t sliderType,
		 SDL_Renderer* renderer,
		 Widget* arr[256], uint8_t* widgetNum) {

	//create widget
	Widget* widget = malloc(sizeof(Widget));
	widget->x = x;
	widget->y = y;
	widget->w = w;
	widget->h = h;

	widget->dividex = (float)dividex/100.0;
	widget->dividey = (float)dividey/100.0;
	widget->dividew = (float)dividew/100.0;
	widget->divideh = (float)divideh/100.0;

	widget->savex = x;
	widget->savey = y;
	widget->savew = w;
	widget->saveh = h;

	//select color
	widget->r =  (color >> 16);
	widget->g = ((color >> 8) & 0xff);
	widget->b = (color        & 0xff);
	widget->a = 0 + (255 * (color > 0));
	widget->r2 =  (color2 >> 16);
	widget->g2 = ((color2 >> 8) & 0xff);
	widget->b2 = (color2        & 0xff);
	widget->a2 = 0 + (255 * (color2 > 0));
	
	widget->shade = shade;

	if (image != NULL) {

		SDL_Surface* surface = SDL_LoadBMP(image);	
		widget->texture = SDL_CreateTextureFromSurface(renderer, surface);
		SDL_FreeSurface(surface);
	} else {
		widget->texture = NULL;
	}
	free(image);

	
	if (image2 != NULL) {

		SDL_Surface* surface = SDL_LoadBMP(image2);	
		widget->texture2 = SDL_CreateTextureFromSurface(renderer, surface);
		SDL_FreeSurface(surface);

		SDL_QueryTexture(widget->texture2, NULL, NULL, &(widget->texture2_w), &(widget->texture2_h));
	} else {
		widget->texture2 = NULL;
		widget->texture2_w = 0;
		widget->texture2_h = 0;
	}
	free(image2);

	
	widget->text = text;
	widget->textx = textx;
	widget->texty = texty;
	widget->actionType = actionType;
	widget->sliderType = sliderType;
	widget->slide = 0.0;

	widget->active = active;
	widget->hover = false;

	//set drawing options
	widget->stretchWidth = stretchWidth;
	widget->stretchHeight = stretchHeight;
	widget->offsetX = offsetX;
	widget->offsetY = offsetY;
	widget->offsetW = offsetW;
	widget->offsetH = offsetH;

	widget->list = false;
	widget->listNum = 0;
	widget->listOffset = 0;

	//add to array
	arr[*widgetNum] = widget;
	*widgetNum += 1;
}


void DestroyWidget(uint8_t deleteIndex, Widget* arr[256], uint8_t* widgetNum) {

	Widget* widget = arr[deleteIndex];
	
	if (widget->text != NULL) { free(widget->text); }
	if (widget->texture != NULL) { SDL_DestroyTexture(widget->texture); }
	if (widget->texture2 != NULL) { SDL_DestroyTexture(widget->texture2); }

	arr[deleteIndex] = NULL;
	free(widget);

	for (int i = deleteIndex; i < (*widgetNum)-1; i++) {
	
		arr[i] = arr[i+1];
	}
	*widgetNum -= 1;
}


void DestroyWidgets(Widget* arr[256], uint8_t* widgetNum) {

	for (int i = 0; i < *widgetNum; i++) {
	
		if (arr[i] != NULL) {
		
			if (arr[i]->text != NULL) { free(arr[i]->text); }
			if (arr[i]->texture != NULL) { SDL_DestroyTexture(arr[i]->texture); }
			if (arr[i]->texture2 != NULL) { SDL_DestroyTexture(arr[i]->texture2); }
			free(arr[i]);
		}
	}
}


AlsaData* InitAlsaData() {

	AlsaData* data = malloc(sizeof(AlsaData));

	data->PCM_DEVICE = "default";
	data->rate = 44100;
	data->channels = 1;
	data->format = SND_PCM_FORMAT_FLOAT;

	data->buff = NULL;
	data->buff_size = 1;
	data->readfd = 0;
	data->readval = 0;
	
	data->rms = 0;

	return data;
}

void DestroyAlsaData(AlsaData* data) {

	free(data);
}


PlayerData* InitPlayerData() {

	PlayerData* player = malloc(sizeof(PlayerData));
	
	player->w = 640;
	player->h = 360;
	player->quit = false;
	player->stop = false;
	player->playing = false;
	player->delay = 7;
	player->font = NULL;
	player->bgimage = NULL;
	player->background = NULL;


	player->fileIndex = 0;
	player->fileSize = 0;
	player->fileCount = 0;
	player->fileTarget = 0;
	player->fileSearchIndex = 0;
	player->fileCountCopy = 0;
	player->dragFile = NULL;

	player->imageNum = 0;

	player->key = 0;
	player->x = 0;
	player->y = 0;

	player->widgetTarget = NULL;
	player->waveFreq = 1;
	player->waveAmp = 1;
	player->waveVert = false;
	player->waveFade = true;

	player->volume = 1.0;
	player->rate = 44100;
	player->rateInputIndex = 0;

	for (int i = 0; i < 1024; i++) { player->fileList[i] = NULL; }
	for (int i = 0; i < 1024; i++) { player->fileListCopy[i] = NULL; }
	for (int i = 0; i < 64; i++) { player->fileSearch[i] = '\0'; }
	for (int i = 0; i < 6; i++) { player->rateStr[i] = '\0'; }
	
	for (int i = 0; i < 128; i++) { player->imageList[i] = NULL; }
	for (int i = 0; i < 128; i++) { player->imageStr[i] = NULL; }


	return player;
}

void DestroyPlayerData(PlayerData* data) {

	for (int i = 0; i < 1024; i++) { 
		
		if (data->fileList[i] != NULL) { free(data->fileList[i]); }
	}


	for (int i = 0; i < 128; i++) {
		
		if (data->imageList[i] != NULL) { free(data->imageList[i]); }
		if (data->imageStr[i] != NULL) { free(data->imageStr[i]); }
	}

	if (data->font != NULL) { free(data->font); }
	if (data->bgimage != NULL) { free(data->bgimage); }
	if (data->background != NULL) { SDL_DestroyTexture(data->background); }

	free(data);
}




TimelineData* InitTimelineData(AlsaData* alsa_data) {

	TimelineData* timeline = malloc(sizeof(TimelineData));

	//0 is normal dragging mode
	//1 is cutting mode
	//2 is copying mode
	//3 is deleting mode
	timeline->mode = 0;

	//file name to load 
	//project assets and settings
	for (int i = 0; i < 128; i++) { timeline->projectFile[i] = '\0'; }
	timeline->projectFileIndex = 0;

	//default bpm
	timeline->bpm = 120;
	timeline->bpmStr[0] = 'B';
	timeline->bpmStr[1] = 'P';
	timeline->bpmStr[2] = 'M';
	timeline->bpmStr[3] = ':';
	timeline->bpmStr[4] = ' ';
	timeline->bpmStr[5] = '1';
	timeline->bpmStr[6] = '2';
	timeline->bpmStr[7] = '0';
	timeline->spb = 0.5;

	//timeline position
	timeline->beatLength = 0;
	timeline->currentBeat = 0;
	timeline->previousBeat = 0;
	timeline->targetBeat = 0;
	timeline->intrabeatOffset = 0;
	
	//default scroll level is 16 bars
	timeline->projectLength = 0;
	timeline->scrollLength = 64;
	timeline->scrollOffset = 0;
	timeline->autoScroll = true;

	//current audio track (0-255)
	timeline->currentTrack = 0;
	timeline->previousTrack = 0;
	
	//seperate frame for writing 
	//appropriate audio and sending to card
	timeline->frame = NULL;

	//audio data to be loaded
	for (int i = 0; i < 256; i++) {
	
		timeline->samples[i] = NULL;
		timeline->files[i] = NULL;
	}
	timeline->fileNum = 0;
	timeline->sampleNum = 0;
	timeline->sampleTarget = NULL;
	timeline->sampleLastClicked = NULL;

	//timeline->active = false;
	timeline->active = true;

	return timeline;
}

void DestroyTimelineData(TimelineData* data) {

	//free samples and file names
	for (int i = 0; i < data->sampleNum; i++) {

		if (data->samples[i] != NULL) {	DestroySample(data->samples[i]); }
	}
	
	for (int i = 0; i < data->fileNum; i++) {
		
		if (data->files[i] != NULL) { free(data->files[i]); }
	}
		
		
	if (data->frame != NULL) { free(data->frame); }
	
	free(data);
}



Sample* CreateSample(char* file, TimelineData* timeline, Sample* copySample, 
		uint32_t beatOffset, uint32_t cropOffset, 
		uint32_t beatCropFront, uint32_t beatCropBack, 
		uint8_t track) {

	if (timeline->sampleNum >= 256) {
	
		printf("ERROR: Maximum namuber of sample allocated, delete some.\n");
		return NULL;
	}

	int readfd = 0;

	if (file != NULL && copySample == NULL) {
	
		//check if sample exists
		readfd = open(file, O_RDONLY);
	
		if (readfd < 0) {
	
			printf("ERROR: The file \"%s\" can't be read for sample creation.", file);
			return NULL;
		}

		//check if wave format
		char waveCheck[5];
		pread(readfd, &(waveCheck), sizeof(char)*4, 8);
		waveCheck[4] = '\0';
		if (strcmp(waveCheck, "WAVE") != 0) {
	
			close(readfd);
			printf("ERROR: We only accept wave files currently, sorry :/.\n", file);
			return NULL;
		}
	}

	Sample* sample = malloc(sizeof(Sample));
	sample->playing = false;
	sample->copy = false;
	sample->copySource = false;
	sample->track = timeline->currentTrack;
	sample->channel = 0;
	sample->flags = 0x00;	
	sample->beatOffset = timeline->currentBeat;
	sample->dataOffset = 0;
	sample->cropOffset = cropOffset;
	sample->beatCropFront = beatCropFront;
	sample->beatCropBack = beatCropBack;
	sample->fileIndex = timeline->fileNum;
	
	sample->color = 0;


	if (beatOffset != 0) { sample->beatOffset = beatOffset; }
	if (track != 0) { sample->track = track; }

	

	//make copy of previous sample and point to it
	if (copySample != NULL) {

		copySample->copySource = true;
		sample->copy = true;	
		sample->fileIndex = copySample->fileIndex;
		sample->data = copySample->data;
		sample->dataSize = copySample->dataSize;
		sample->seconds = copySample->seconds;
		sample->beatLength = copySample->beatLength;
		//sample->cropOffset = copySample->cropOffset;
		//sample->beatCropFront = beatCropFront;
		//sample->beatCropBack = beatCropBack;
		//sample->track = track;
		sample->color = copySample->color;
		sample->dataPoints = copySample->dataPoints;
		sample->dataSizeGUI = copySample->dataSizeGUI;
		return sample;
	}


	//allocate audio data from file
	FILE* ftype;
	ftype = fopen(file, "r");
	fseek(ftype, 0, SEEK_END);
	
	//replace this with actual wave file reader
	//and functions to handle different audio encodings
	pread(readfd, &(sample->dataSize), sizeof(uint32_t), 40);

	//some wave formats are dumb 
	//and dont tell you the size 
	//of the data chunk so just
	//assume rest of file is data
	if (sample->dataSize < 4096) {
	
		sample->dataSize = (ftell(ftype)-44);
	}


	
	sample->data = malloc(sample->dataSize);
	fseek(ftype, 0, SEEK_SET);
	fclose(ftype);


	//duration = fileSize/byteRate
	//byteRate = (sampleRate*bitsPerSample*channels)/8
	sample->seconds = ((double)sample->dataSize)/((double)((44100*16*2)/8));
	
	//beat lengths in 16ths
	sample->beatLength = (uint32_t)(((double)timeline->bpm)*(sample->seconds/60.0)*16.0) + 1;


	printf("Sample used %d bytes in memory.\n", sample->dataSize);
	printf("Sample is %f seconds long.\n", sample->seconds);
	printf("Sample is %f beats long.\n", (double)sample->beatLength/16.0);


	//import wav file data
	pread(readfd, sample->data, sample->dataSize, 44);
	close(readfd);


	//generate audio data visualization
	int dataDiv = 256;
	sample->dataPoints = malloc(sizeof(uint8_t)*(sample->dataSize/dataDiv));
	sample->dataSizeGUI = sample->dataSize/dataDiv;

	for (int i = 0; i < sample->dataSize/dataDiv; i++) {
	
		//uint32_t soundAvg = (sample->data[(i*dataDiv)] << 8) | (sample->data[(i*dataDiv)+1]);
		uint32_t soundAvg = 0;

		for (int j = 0; j < 4; j += 2) {
		
			soundAvg += (sample->data[(i*dataDiv)+j] << 8) | (sample->data[(i*dataDiv)+j+1]);
		}
		//52 is size of track height in pixels
		//65536 over max size of 2 byte values
		//sample->dataPoints[i] = (soundAvg / dataDiv) / (65536/52);
		sample->dataPoints[i] = (soundAvg / 4) / (65536/52);
	}

	
	printf("Sample \"%s\" created.\n", file);

	return sample;
}

void DestroySample(Sample* sample) {

	if (sample->data != NULL && sample->copy == false && sample->copySource == false) { free(sample->data); }
	if (sample->dataPoints != NULL && sample->copy == false && sample->copySource == false) { free(sample->dataPoints); }
	free(sample);
}
