/*
 * PSP Software Development Kit - http://www.pspdev.org
 * -----------------------------------------------------------------------
 * Licensed under the BSD license, see LICENSE in PSPSDK root for details.
 *
 * main.c - Sample to demonstrate proper use of the mp3 library
 *
 * Copyright (c) 2008 Alexander Berl <raphael@fx-world.org>
 *
 * $Id: $
 */
#include <pspkernel.h>
#include <pspdebug.h>
#include <pspctrl.h>
#include <pspdisplay.h>
#include <stdio.h>
#include <pspaudio.h>
#include <pspmp3.h>
#include <psputility.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <mad.h>
#include "id3.h"
#include "player.h"
#include <stdio.h>
#include <string.h>
#include <pspkernel.h>
#include <stdlib.h>
#include <pspiofilemgr.h>
#include "mp3xing.h"
static float fadd(float a, float b)
{
   float result;
   __asm__ volatile (
       "mtv    %1, S000\n"
       "mtv    %2, S001\n"
       "vadd.s S002, S000, S001\n"
       "mfv    %0, S002\n"
       : "=r"(result) : "r"(a), "r"(b));
   return result;
}
/*static float fsub(float a, float b)
{
   float result;
   __asm__ volatile (
       "mtv    %1, S000\n"
       "mtv    %2, S001\n"
       "vsub.s S002, S000, S001\n"
       "mfv    %0, S002\n"
       : "=r"(result) : "r"(a), "r"(b));
   return result;
}*/
static float fmul(float a, float b)
{
   float result;
   __asm__ volatile (
       "mtv    %1, S000\n"
       "mtv    %2, S001\n"
       "vmul.s S002, S000, S001\n"
       "mfv    %0, S002\n"
       : "=r"(result) : "r"(a), "r"(b));
   return result;
}
static float fdiv(float a, float b)
{
   float result;
   __asm__ volatile (
       "mtv    %1, S000\n"
       "mtv    %2, S001\n"
       "vrcp.s  S001, S001\n"
       "vmul.s  S000, S000, S001\n"
       "mfv    %0, S000\n"
       : "=r"(result) : "r"(a), "r"(b));
   return result;
}
static char mp3Buf[16*1024]  __attribute__((aligned(64))); //32*1024
static short pcmBuf[16*(1152/2)]  __attribute__((aligned(64)));//32*
//static int fd;
static int playmp3;
static SceUID mp3thread = -1;
static int handle;
static int channel = -1;
static int EOS = 0;
static int paused;
static char *MP3FILE;
static int samplingRate;
static int numChannels;
static int lastDecoded = 0;
static int volume = PSP_AUDIO_VOLUME_MAX;
static int numPlayed = 0;
static short* buf;
static int posreal;
static int endpos;
static int loop = 0;
static int data_starthm;
static float perc;
static SceInt64 res;
static int kbit;
static int size;
static int fillStreamBuffer( int fd, int handle )
{
	char* dst;
	int write;
	int pos;
	// Get Info on the stream (where to fill to, how much to fill, where to fill from)
	sceMp3GetInfoToAddStreamData( handle, &dst, &write, &pos);
	// Seek file to position requested
	sceIoLseek32Async(fd, 0, PSP_SEEK_SET);
	sceIoWaitAsync(fd, &res);
	sceIoLseek32Async( fd, 0, SEEK_END );
	sceIoWaitAsync(fd, &res);
	endpos = res;
	sceIoLseek32Async( fd, posreal, SEEK_SET );
	sceIoWaitAsync(fd, &res);
	// Read the amount of data
	sceIoReadAsync( fd, dst, write );
	sceIoWaitAsync(fd, &res);
	posreal = fadd(posreal,res);
	// Notify mp3 library about how much we really wrote to the stream buffer
	sceMp3NotifyAddStreamData( handle, res );
	return (pos>0);
}
int decodeThread2(SceSize args, void *argp){
	fd = sceIoOpen(MP3FILE, PSP_O_RDONLY, 0777 );
	sceIoChangeAsyncPriority(fd, 0x10);
	data_starthm = ID3v2TagSize(MP3FILE);
	data_start = ID3v2TagSize(MP3FILE);
	sceIoLseek32Async(fd, data_start, PSP_SEEK_SET);
    	data_start = SeekNextFrameMP3(fd);	
	// Init mp3 resources
	sceMp3InitResource();
	sceIoLseek32Async(fd, 0, PSP_SEEK_SET);
	sceIoWaitAsync(fd, &res);
	sceIoLseek32Async( fd, 0, SEEK_END );
	sceIoWaitAsync(fd, &res);
	endpos = res;
	if(data_starthm > (endpos-10000)){
		data_starthm = 0;
	}
	size = (endpos - data_starthm);
	posreal = data_starthm;
	// Reserve a mp3 handle for our playback
	SceMp3InitArg mp3Init;
	mp3Init.mp3StreamStart = data_starthm;
	mp3Init.mp3StreamEnd = endpos;
	mp3Init.unk1 = 0;
	mp3Init.unk2 = 0;
	mp3Init.mp3Buf = mp3Buf;
	mp3Init.mp3BufSize = sizeof(mp3Buf);
	mp3Init.pcmBuf = pcmBuf;
	mp3Init.pcmBufSize = sizeof(pcmBuf);

	handle = sceMp3ReserveMp3Handle( &mp3Init );
	
	// Fill the stream buffer with some data so that sceMp3Init has something to work with
	fillStreamBuffer( fd, handle );
	
	sceMp3Init( handle );
	numChannels = sceMp3GetMp3ChannelNum( handle );
	kbit = sceMp3GetBitRate(handle);
	samplingRate = sceMp3GetSamplingRate(handle);
	//sceAudioSetFrequency(samplingRate);
	lastDecoded = 0;
	numPlayed = 0;
	perc = 0;
	//-----------------------------------
	struct xing xing;
	memset(&xing, 0, sizeof xing);
	struct mad_stream stream;
	if(parse_xing(&xing, stream.anc_ptr, stream.anc_bitlen))
	{
		if (xing.flags & XING_FRAMES)
		{
			kbit = fdiv(xing.bytes,xing.frames);
		}
	}
	if (kbit<0){
		EOS = 1;
		paused = 0;
	}
	//}
	//-----------------------------------
	while(playmp3)
	{
		while (paused)
		{
			perc = (fdiv(fmul(fadd(posreal,1),100),size));
			if(perc > 99.99f){
				EOS = 1;
				paused = 0;
			}
			kbit = sceMp3GetBitRate(handle);
			samplingRate = 44100;//sceMp3GetSamplingRate(handle);
			// Check if we need to fill our stream buffer
			if (sceMp3CheckStreamDataNeeded( handle )>0)
			{
				fillStreamBuffer( fd, handle );
			}
			// Decode some samples
			int bytesDecoded=0;
			int retries = 0;
			// We retry in case it's just that we reached the end of the stream and need to loop
			for (;retries<1;retries++)
			{
				bytesDecoded = sceMp3Decode( handle, &buf );
				if (bytesDecoded>0){
					break;
				}
				if(sceMp3CheckStreamDataNeeded( handle )<=0){
					break;
				}
				if(!fillStreamBuffer( fd, handle ))
				{
					numPlayed = 0;
					EOS = 1;
					paused = 0;
				}
			}
			// Reserve the Audio channel for our output if not yet done
			if (channel<0 || lastDecoded!=bytesDecoded)
			{
				if (channel>=0){
					sceAudioSRCChRelease();
					//numPlayed = 0;
					//EOS = 1;
					//paused = 0;
				}
	
				channel = sceAudioSRCChReserve( fdiv(bytesDecoded,fmul(2,numChannels)), samplingRate, numChannels );
			}
			// Output the decoded samples and accumulate the number of played samples to get the playtime
			numPlayed = sceAudioSRCOutputBlocking( volume, buf );
			if(numPlayed < 0){
				numPlayed = 0;
				EOS = 1;
				paused = 0;
			}
		}
	sceKernelDelayThread(10000);
	}
	return 0;
}
/* main routine */
int LoadMp3(char *MP3FILEpath)
{
	playmp3 = 1;
	paused = 0;
	EOS = 0;
	loop = 0;
	MP3FILE = MP3FILEpath;
	mp3thread = sceKernelCreateThread("decodeThread2", decodeThread2, 12, 256*1024, PSP_THREAD_ATTR_USER | PSP_THREAD_ATTR_VFPU, NULL);
	sceKernelStartThread(mp3thread, 0, NULL);
	return data_starthm;
				//sceMp3ResetPlayPosition( handle );
}
void PlayMp3()
{
	playmp3 = 1;
	paused = 1;
	EOS = 0;
}
void PauseMp3()
{
	if(paused == 1){
		paused = 0;
	}else{
		paused = 1;
	}
}
void LoopMp3(int num)
{
	loop = num;
}
void StopMp3(void)
{
	playmp3 = 0;
	paused = 0;
	EOS = 0;
	sceKernelWaitThreadEnd(mp3thread, NULL);
    	sceKernelDeleteThread(mp3thread);
	if (channel>=0)
		sceAudioSRCChRelease();
	sceMp3ReleaseMp3Handle( handle );
	sceMp3TermResource();
	sceIoClose( fd );
}
int getPos(void){
    return posreal;
}

int setPos(int value){
	posreal = value;
    return posreal;
}
int GetTime(void){
   int playTime = samplingRate>0?(posreal*8) / (kbit*1024):0;
   return playTime;
}
short *VisMp3(void) {
	return(pcmBuf);
}
int EOSMp3(void){
    return EOS;
}
int PercentMp3(void){
    return perc;
}
void setvolume(int num){
    volume = 327.68*num;
}
char *Mp3Filename(){
    return MP3FILE;
}
int rawlength(){
	return ((size*8) / (kbit*1024));
}
static void getMP3TagInfo(char *filename, struct fileInfo *targetInfo){
    //ID3:
    struct ID3Tag ID3;
    ID3 = ParseID3(filename);
    strcpy(targetInfo->title, ID3.ID3Title);
    strcpy(targetInfo->artist, ID3.ID3Artist);
    strcpy(targetInfo->album, ID3.ID3Album);
    strcpy(targetInfo->year, ID3.ID3Year);
    strcpy(targetInfo->genre, ID3.ID3GenreText);
    strcpy(targetInfo->trackNumber, ID3.ID3TrackText);
    targetInfo->encapsulatedPictureType = ID3.ID3EncapsulatedPictureType;
    targetInfo->encapsulatedPictureOffset = ID3.ID3EncapsulatedPictureOffset;
    targetInfo->encapsulatedPictureLength = ID3.ID3EncapsulatedPictureLength;
}
struct fileInfo Taginfo2(char *filename){
    struct fileInfo tempInfo;
    initFileInfo(&tempInfo);
    getMP3TagInfo(filename, &tempInfo);
    return tempInfo;
}
struct fileInfo Taginfo(char *filename){
    struct fileInfo tempInfo;
    initFileInfo(&tempInfo);
    getMP3TagInfo(filename, &tempInfo);
    tempInfo.kbit = kbit;
    tempInfo.hz = samplingRate;
	printf(tempInfo.layer,"III");
	if(numChannels < 2){
		sprintf(tempInfo.mode,"mono");
	}else{
		sprintf(tempInfo.mode,"stereo");
	}
	int secs = (size*8) / (kbit*1024);
	int h = secs / 3600;
	int m = (secs - h * 3600) / 60;
	int s = secs - h * 3600 - m * 60;
	snprintf(tempInfo.strLength, sizeof(tempInfo.strLength), "%2.2i:%2.2i:%2.2i", h, m, s);
	return tempInfo;
}
