#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fmcfrb.h>
#include <winb.h>
#include <te.h>
#include <fntb.h>
#include <gui.h>
#include <egb.h>
#include <guidbg.h>
#include <file_dlg.h>
#include <eintm.h>	// EIN(TM)関連ライブラリのヘッダ
#include "music.h"

/* EUPﾌｧｲﾙﾍｯﾀﾞ構造体 */
typedef	struct	{
	char	title[32];
	char	dummy[52+512+256];
	char	trk_mute[32];
	char	trk_port[32];
	char	trk_midi_ch[32];
	char	trk_key_bias[32];
	char	trk_transpose[32];
	char	trk_play_filter[7][32];
	char	instruments_name[128][4];
	char	fm_midi_ch[6];
	char	pcm_midi_ch[8];
	char	fm_file_name[8];
	char	pcm_file_name[8];
	char	reserve[260];
	char	appli_name[8];
	char	version[2];
	int		size;
	char	signature;
	char	first_tempo;
}	EUPHEAD ;
typedef	struct	{
	char	title[33];
	char	trk_mute[32];
	char	trk_port[32];
	char	trk_midi_ch[32];
	char	trk_key_bias[32];
	char	trk_transpose[32];
	char	fm_midi_ch[6];
	char	pcm_midi_ch[8];
	char	fm_file_name[9];
	char	pcm_file_name[9];
	int		size;
	char	signature;
	char	first_tempo;
	char	*eupData;
}	EUPINFO ;

char musPathName[MP_PATHLEN] ;
char fmbPathName[MP_PATHLEN] ;
char pmbPathName[MP_PATHLEN] ;
int musicLot ;
static EUPINFO *eupInfo ;

// ダミー関数
int tmmInit(){return NOERR ;} ;
int tmmEnd(){return NOERR ;} ;
int midLoad(char *path){return NOERR ;} ;

int dispStatus(int stat){
	extern int statusMesId ;
	extern int panelStatusMesId ;
	static char statusMes[6] ;

	switch(stat){
	case MPLAY_STOP:
		strcpy(statusMes, "STOP") ;
		break;
	case MPLAY_PLAY:
		strcpy(statusMes, "PLAY") ;
		break;
	case MPLAY_PAUSE:
		strcpy(statusMes, "PAUSE") ;
		break;
	}

	if (dispMode == DISPMODE_PANEL){
		MMI_SendMessage(panelStatusMesId, MM_SETMSG, 1, "     ") ;
		updateObj(panelStatusMesId) ;
	} else {
		MMI_SendMessage(statusMesId, MM_SETMSG, 1, "     ") ;
		updateObj(statusMesId) ;
	}

	MMI_SendMessage(panelStatusMesId, MM_SETMSG, 1, statusMes) ;
	MMI_SendMessage(statusMesId, MM_SETMSG, 1, statusMes) ;

	if (dispMode == DISPMODE_PANEL)
		updateObj(panelStatusMesId) ;
	else
		updateObj(statusMesId) ;
	return NOERR;
}

static int oldMeas = 0 ;
static char measMes[8] ="  0/  0" ;
int dispMeas(int x){
	extern int panelMeasMesId ;
	extern int measMesId ;
	oldMeas = x;
	measMes[2] = '0' + x % 10 ;
	x /= 10 ;
	if (x == 0)
		measMes[1] = ' ' ;
	else
		measMes[1] = '0' + x % 10 ;
	x /= 10 ;
	if (x == 0)
		measMes[0] = ' ' ;
	else
		measMes[0] = '0' + x % 10 ;
	MMI_SendMessage(panelMeasMesId, MM_SETMSG, 1, measMes) ;
	MMI_SendMessage(measMesId, MM_SETMSG, 1, measMes) ;
	if (dispMode == DISPMODE_PANEL)
		updateObj(panelMeasMesId) ;
	else
		updateObj(measMesId) ;
	return NOERR;
}

static int oldTempo = 0 ;
static char tempoMes[4] ="   " ;
int dispTempo(int x){
	extern int tempoMesId ;
	oldTempo = x;
	tempoMes[2] = '0' + x % 10 ;
	x /= 10 ;
	if (x == 0)
		tempoMes[1] = ' ' ;
	else
		tempoMes[1] = '0' + x % 10 ;
	x /= 10 ;
	if (x == 0)
		tempoMes[0] = ' ' ;
	else
		tempoMes[0] = '0' + x % 10 ;
	MMI_SendMessage(tempoMesId, MM_SETMSG, 1, tempoMes) ;
	if (dispMode == DISPMODE_PANEL)
		updateObj(tempoMesId) ;
	return NOERR;
}

int dispMaxMeas(int x){
	extern int panelMeasMesId ;
	extern int measMesId ;
	measMes[6] = '0' + x % 10 ;
	x /= 10 ;
	if (x == 0)
		measMes[5] = ' ' ;
	else
		measMes[5] = '0' + x % 10 ;
	x /= 10 ;
	if (x == 0)
		measMes[4] = ' ' ;
	else
		measMes[4] = '0' + x % 10 ;

	MMI_SendMessage(panelMeasMesId, MM_SETMSG, 1, measMes) ;
	MMI_SendMessage(measMesId, MM_SETMSG, 1, measMes) ;
	if (dispMode == DISPMODE_PANEL)
		updateObj(panelMeasMesId) ;
	else
		updateObj(measMesId) ;
	return NOERR;
}

int updateMeas(){
	int meas, tempo ;
	if ((mplayStatus & MPLAY_PLAY) == 0) return NOERR ;
	if (SND_eup_stat_flag() == 0){
		return musPlayStop() ;
	}
	if ((mplayStatus & MPLAY_PAUSE) != 0) return NOERR ;
	meas = SND_eup_stat_meas() + 1;
	if (oldMeas != meas) dispMeas(meas) ;
	tempo = SND_eup_stat_tempo() + 30;
	if (oldTempo != tempo) dispTempo(tempo) ;
	return NOERR ;
}

int getMaxMeas(char *data,int size){
	int meas = 0 ;
	int i = 0 ;
	while(i < size){
		//	小説マーカー
		if (data[i] == 0xF2) meas += 1 ;
		////	エクスクルーシブメッセージ
		//if (data[i] == 0xF0) {
		//	while (data[++i] != 0xf7){} ;
		//	i -= i % 6 ;
		//}
		i+= 6 ;
	}
	return meas + 1;
}

void fnameCnv(char *fname){
	int i = 0 ;
	while (i<strlen(fname)){
		if (FNT_islower(fname[i]) !=0)
			fname[i] -= 32 ;
		if (FNT_iskanji(fname[i]) !=0)
			i++ ;
		i++ ;
	}		
}

static int fmBankLoad(char *path){
	extern int fmbNameMesId ;
	static char fmbNameMes[13] ;
	static char fmBankLoadWork[8] ;
	int i ;
	int				cur ;		//	マウスカーソルタイプ保存用変数
	char fname[9] ;
	MG_PushPtr(MB_WAIT, &cur) ;
	EIN_fnameNonExt(path, fname) ;
	if (fname[0] == '\0' || fname == NULL){
		strcpy(fmbNameMes, "NO FILE .FMB") ;
		MMI_SendMessage(fmbNameMesId, MM_SETMSG, 1, fmbNameMes) ;
		if (dispMode == DISPMODE_PANEL)
			updateObj(fmbNameMesId) ;
	}else if (SND_fm_bank_load(path, fmBankLoadWork) != NOERR){
		strcpy(fmbNameMes, "NOTFOUND.FMB") ;
		MMI_SendMessage(fmbNameMesId, MM_SETMSG, 1, fmbNameMes) ;
		if (dispMode == DISPMODE_PANEL)
			updateObj(fmbNameMesId) ;
	}else{
		strcpy(fmbPathName, path) ;
		EIN_fnameNonExt(path, fmbNameMes) ;
		i = 8 - strlen(fmbNameMes) ;
		while (i-- > 0) strcat(fmbNameMes, " ") ;
		strcat(fmbNameMes, ".FMB") ;
		fnameCnv(fmbNameMes) ;
		MMI_SendMessage(fmbNameMesId, MM_SETMSG, 1, fmbNameMes) ;
		if (dispMode == DISPMODE_PANEL)
			updateObj(fmbNameMesId) ;
	}
	MG_PopPtr(cur) ;
	return NOERR ;
}

static int pcmBankLoad(char *path){
	extern int pmbNameMesId ;
	static char pmbNameMes[13] ;
	static char pcmBankLoadWork[8] ;
	int i ;
	int				cur ;		//	マウスカーソルタイプ保存用変数
	char fname[9] ;
	MG_PushPtr(MB_WAIT, &cur) ;
	EIN_fnameNonExt(path, fname) ;
	if (fname[0] == '\0' || fname == NULL){
		strcpy(pmbNameMes, "NO FILE .PMB") ;
		MMI_SendMessage(pmbNameMesId, MM_SETMSG, 1, pmbNameMes) ;
		if (dispMode == DISPMODE_PANEL)
			updateObj(pmbNameMesId) ;
	}else if (SND_pcm_bank_load(path, pcmBankLoadWork) != NOERR){
		strcpy(pmbNameMes, "NOTFOUND.PMB") ;
		MMI_SendMessage(pmbNameMesId, MM_SETMSG, 1, pmbNameMes) ;
		if (dispMode == DISPMODE_PANEL)
			updateObj(pmbNameMesId) ;
	}else{
		strcpy(pmbPathName, path) ;
		EIN_fnameNonExt(path, pmbNameMes) ;
		i = 8 - strlen(pmbNameMes) ;
		while (i-- > 0) strcat(pmbNameMes, " ") ;
		strcat(pmbNameMes, ".PMB") ;
		fnameCnv(pmbNameMes) ;
		MMI_SendMessage(pmbNameMesId, MM_SETMSG, 1, pmbNameMes) ;
		if (dispMode == DISPMODE_PANEL)
			updateObj(pmbNameMesId) ;
	}
	MG_PopPtr(cur) ;
	return NOERR ;
}

int eupLoad(char *path){
	extern int fileNameMesId ;
	extern int titleMesId ;
	static char fileNameMes[13] ;
	static char titleMes[33] ;
	char eupFmbPathName[MP_PATHLEN] ;
	char eupPmbPathName[MP_PATHLEN] ;
	char ext[4] ;
	int workLot, trk ;
	EUPHEAD *eupHeader ;
	FILE *fp ;
	int				cur ;		//	マウスカーソルタイプ保存用変数
	// 演奏を停止する
	musPlayStop() ;

	if ((mplayStatus & MPLAY_LOAD) != 0){
		mplayStatus &= (~MPLAY_LOAD) ;
		TL_freeLot(musicLot) ;
	}

	MG_PushPtr(MB_WAIT, &cur) ;

	if ((workLot = TL_getLot()) < NOERR){
		return -2 ;
	}

	if ((eupHeader = (EUPHEAD *)TL_mallocMemory(workLot, sizeof(EUPHEAD))) == NULL){
		TL_freeLot(workLot) ;
		return -2 ;
	}

	if((fp = fopen(path, "rb")) == NULL){
		TL_freeLot(workLot) ;
		return -2 ;
	}
	// if(fread(eupHeader, sizeof(EUPHEAD), 1, fp) < sizeof(EUPHEAD)){
	if(fread(eupHeader, sizeof(EUPHEAD), 1, fp) < 1){
		fclose(fp);
		MG_PopPtr(cur) ;
		TL_freeLot(workLot) ;
		return -2 ;
	}

	if ((musicLot = TL_getLot()) < NOERR){
		fclose(fp);
		MG_PopPtr(cur) ;
		TL_freeLot(workLot) ;
		return -2 ;
	}

	if ((eupInfo = (EUPINFO *)TL_mallocMemory(musicLot, sizeof(EUPINFO))) == NULL){
		fclose(fp);
		MG_PopPtr(cur) ;
		TL_freeLot(workLot) ;
		TL_freeLot(musicLot) ;
		return -2 ;
	}
	eupInfo->title[32] = '\0' ;
	eupInfo->fm_file_name[8] = '\0' ;
	eupInfo->pcm_file_name[8] = '\0' ;
	for (trk = 0 ;trk < 32 ;trk++){
		eupInfo->title[trk] = eupHeader->title[trk] ;
		eupInfo->trk_mute[trk] = eupHeader->trk_mute[trk] ;
		eupInfo->trk_port[trk] = eupHeader->trk_port[trk] ;
		eupInfo->trk_midi_ch[trk] = eupHeader->trk_midi_ch[trk] ;
		eupInfo->trk_key_bias[trk] = eupHeader->trk_key_bias[trk] ;
		eupInfo->trk_transpose[trk] = eupHeader->trk_transpose[trk] ;
	}
	for (trk = 0 ;trk < 6 ;trk++)
		eupInfo->fm_midi_ch[trk] = eupHeader->fm_midi_ch[trk] ;
	for (trk = 0 ;trk < 8 ;trk++){
		eupInfo->pcm_midi_ch[trk] = eupHeader->pcm_midi_ch[trk] ;
		eupInfo->fm_file_name[trk] = eupHeader->fm_file_name[trk] ;
		eupInfo->pcm_file_name[trk] = eupHeader->pcm_file_name[trk] ;
	}
	eupInfo->signature = eupHeader->signature ;
	eupInfo->first_tempo = eupHeader->first_tempo ;
	eupInfo->size = eupHeader->size ;
	TL_freeLot(workLot) ;
	if ((eupInfo->eupData = TL_mallocMemory(musicLot, eupInfo->size)) == NULL){
		fclose(fp);
		MG_PopPtr(cur) ;
		TL_freeLot(musicLot) ;
		return -2 ;
	}
	if(fread(eupInfo->eupData, 1, eupInfo->size, fp) < eupInfo->size){
		fclose(fp);
		MG_PopPtr(cur) ;
		TL_freeLot(musicLot) ;
		return -2 ;
	}
	fclose(fp) ;
	MG_PopPtr(cur) ;
	strcpy(musPathName, path) ;
	EIN_fnameNonExt(path, fileNameMes) ;
	EIN_fnameExt(path, ext) ;
	trk = 8 - strlen(fileNameMes) ;
	while (trk-- > 0) strcat(fileNameMes, " ") ;
	strcat(fileNameMes, ".") ;
	strcat(fileNameMes, ext) ;
	fnameCnv(fileNameMes) ;
	MMI_SendMessage(fileNameMesId, MM_SETMSG, 1, fileNameMes) ;
	if (dispMode == DISPMODE_PANEL) updateObj(fileNameMesId) ;
	MMI_SendMessage(titleMesId, MM_SETMSG, 1,
	// 32BYTE(TITLE) 12345678901234567890123456789012
					"                                ") ;
	if (dispMode == DISPMODE_PANEL) updateObj(titleMesId) ;
	strcpy(titleMes, eupInfo->title) ;
	MMI_SendMessage(titleMesId, MM_SETMSG, 1, titleMes) ;
	if (dispMode == DISPMODE_PANEL) updateObj(titleMesId) ;
	EIN_fnameDirectory(path, eupFmbPathName) ;
	strcat(eupFmbPathName, eupInfo->fm_file_name) ;
	strcat(eupFmbPathName, ".FMB") ;
	fmBankLoad(eupFmbPathName) ;
	EIN_fnameDirectory(path, eupPmbPathName) ;
	strcat(eupPmbPathName, eupInfo->pcm_file_name) ;
	strcat(eupPmbPathName, ".PMB") ;
	pcmBankLoad(eupPmbPathName) ;
	mplayStatus |= MPLAY_LOAD ;
	dispMaxMeas(getMaxMeas(eupInfo->eupData, eupInfo->size)) ;
	dispMeas(0) ;
	dispTempo(eupInfo->first_tempo + 30) ;
	return MPLAY_PLAY_OK ;
}

int musPlayStart(void){
	int i ;
	if ((mplayStatus & MPLAY_LOAD) == 0) return NOERR ;
	//  以下全32ﾄﾗｯｸについて設定
	for(i=0 ; i<32 ;i++){
		//  ミュートの設定
		SND_eup_mute_set(i,(int)eupInfo->trk_mute[i]) ;
		//  出力ポートの設定
		SND_eup_port_set(i,(int)eupInfo->trk_port[i]) ;
		//  出力MIDIchの設定
		SND_eup_midi_ch_set(i,(int)eupInfo->trk_midi_ch[i]) ;
		//  キーバイアスの設定
		SND_eup_bias_set(i,(int)eupInfo->trk_key_bias[i]) ;
		//  トランスポーズの設定
		SND_eup_transpose_set(i,(int)eupInfo->trk_transpose[i]) ;
	}
	for(i = 0;i < 6;i++)	// FM音源のMIDIチャンネル設定
		SND_midi_ch_assign(i,(int)eupInfo->fm_midi_ch[i]) ;
	for(i = 0;i < 8;i++)	// PCM音源のMIDIチャンネル設定
		SND_midi_ch_assign(i+64,(int)eupInfo->pcm_midi_ch[i]) ;
	// 曲始めのテンポをセット
	SND_eup_tempo_set(eupInfo->first_tempo) ;
	SND_eup_play_start(eupInfo->eupData, eupInfo->size, eupInfo->signature) ;
	SND_eup_loop_set(loopMode ^ 1) ;	// 念のため
	mplayStatus |= MPLAY_PLAY ;
	dispStatus(MPLAY_PLAY);
	return NOERR ;
}

int musPlayStop(void){
	if ((mplayStatus & MPLAY_PLAY) != 0){
		mplayStatus &= (~MPLAY_PLAY) ;
		if ((mplayStatus & MPLAY_PAUSE) != 0){
			mplayStatus &= (~MPLAY_PAUSE) ;
			SND_eup_play_restart() ;
		}
		SND_eup_play_stop() ;
		dispStatus(MPLAY_STOP) ;
		dispMeas(0) ;
	}
	return NOERR;
}

int musPlayPause(void){
	if ((mplayStatus & MPLAY_PLAY) == 0) return -1 ;
	if ((mplayStatus & MPLAY_PAUSE) != 0){
		SND_eup_play_restart() ;
		mplayStatus &= (~MPLAY_PAUSE) ;
		dispStatus(MPLAY_PLAY) ;
	} else {
		SND_eup_play_pause() ;
		mplayStatus |= MPLAY_PAUSE ;
		dispStatus(MPLAY_PAUSE) ;
	}
	return NOERR ;
}

int musPlayFastSpeed(void){
	extern int fastBtnId ;
	MTL_resetAtrObj(fastBtnId, (~MS_UNHLTL40)) ;
	SND_eup_relative_tempo_set(+250) ;
	MMI_SendMessage(fastBtnId, MM_HLT, 1, TRUE) ; 
	MTL_setAtrObj(fastBtnId, MS_UNHLTL40) ;
	return NOERR ;
}

int musPlayNormalSpeed(void){
	extern int fastBtnId ;
	MTL_resetAtrObj(fastBtnId, (~MS_UNHLTL40)) ;
	SND_eup_relative_tempo_set(0) ;
	MMI_SendMessage(fastBtnId, MM_HLT, 1, FALSE) ; 
	MTL_setAtrObj(fastBtnId, MS_UNHLTL40) ;
	return NOERR ;
}

int musLoopMode(int mode){
	extern int loopModeBtnId ;
	SND_eup_loop_set(mode ^ 1) ;
	if (mode != LOOPMODE_LOOP)
		MTL_setFlagObj(loopModeBtnId, MS_TOGGLE) ;
	else
		MTL_resetFlagObj(loopModeBtnId, (~MS_TOGGLE)) ;
	updateObj(loopModeBtnId) ;
	return NOERR ;
}

int musMuteOff(void){
	int val ;
	SND_get_elevol_mute(&val) ;
	SND_elevol_mute(val|3) ;
	return NOERR ;
}

int noResumeFlag = 0 ;
int playAfterLoadFlag = -1 ;
int musLoad(char *path){
	int ret ;
	ret = NOERR;
	if (_stricmp(path, "-NORESUME") == 0){
		noResumeFlag = 1 ;
	} else if (_stricmp(path, "-EINRESUME") == 0){
		noResumeFlag = 0 ;
	} else if (_stricmp(path, "-LOADONLY") == 0){
		playAfterLoadFlag = 0 ;
	} else if (_stricmp(path, "-PLAYAFTERLOAD") == 0){
		playAfterLoadFlag = -1 ;
	} else {
		char ext[4] ;
		EIN_fnameExt(path, ext) ;
		if (_stricmp(ext, "EUP") == 0) ret = eupLoad(path) ;
		else if (_stricmp(ext, "MID") == 0) ret = midLoad(path) ;
		else if (_stricmp(ext, "FMB") == 0) ret = fmBankLoad(path) ;
		else if (_stricmp(ext, "PMB") == 0) ret = pcmBankLoad(path) ;
	}
	return (ret & playAfterLoadFlag) ;
}

static int eupfix(){
	SND_eup_fixed_int(10000);
	SND_eup_fixed_sub();
	return 0;
}

static int initFlag = 0 ;

static int elevolMute ;
static int smsgMode ;

static int commonSndInit(void){
	if ((initFlag & SND_COMMON) != 0) return NOERR ;
	initFlag ^= SND_COMMON ;
	if (SMSG_chk() != 0) {
		SMSG_getMode(&smsgMode) ;
		SMSG_setMode(0) ;
	}
	SND_get_elevol_mute(&elevolMute) ;
	musMuteOff() ;
	return NOERR;
}

static int commonSndEnd(void){
	if ((initFlag & SND_COMMON) == 0) return NOERR ;
	initFlag ^= SND_COMMON ;
	SND_elevol_mute(elevolMute) ;
	if (SMSG_chk() != 0) SMSG_setMode(smsgMode) ;
	return NOERR;
}

static void (*int_ent)();
static void (*sub_ent)();

static TIM_TIME timer;
static int timerno;

static int pcmMode ;
static int normalSndInit(void){
	if ((initFlag & SND_NORMAL) != 0) return NOERR ;
	initFlag ^= SND_NORMAL ;
	pcmMode = SND_pcm_mode_get() ;
	SND_pcm_mode_set(0) ;
	SND_eup_fixed_init(&int_ent,&sub_ent) ;
	timer.mode = 0;
	timer.inf  = 0;
	BSETCODEADR((char *)&timer.adr, eupfix);
	timer.hcycle = 0;
	timer.lcycle = 1;
	TIM_settime(&timer, &timerno);
	SND_rs_midi_init() ;
	return NOERR ;
}

static int normalSndEnd(void){
	if ((initFlag & SND_NORMAL) == 0) return NOERR ;
	initFlag ^= SND_NORMAL ;
	SND_rs_midi_end() ;
	TIM_clrtime(timerno);
	SND_eup_fixed_end() ;
	SND_pcm_sound_delete(-1) ;
	SND_pcm_mode_set(pcmMode) ;
	return NOERR ;
}

int musInit(void){
	commonSndInit() ;
	if (tmmMode == TMM_AVAILABLE)
		tmmInit() ;
	else
		normalSndInit() ;
	return NOERR;
}

int musEnd(void){
	if (tmmMode == TMM_AVAILABLE)
		tmmEnd() ;
	else
		normalSndEnd() ;
	commonSndEnd() ;
	return NOERR;
}
