/** \file general.c:
 * Realizes complete initialization of the "MIDI Plank" through the MIOS
 * Init-Function, including global variables. Other Functions from the C-Skeleton
 * are the MIOS Tick, which only contains routines for initializing the Bankstick
 * (for debugging/developing purposes only), the MIOS Timer, and all unused
 * functions from the C-Skeleton.
 * Contains Init functions for the different Menu Layers (also called modes:
 * device, modul, parameter).
 * Handles reading and writing all data relevant to the menus from and to
 * bankstick.
 * Handles edit buffer dumps.
 */

void sendEditBuffer();

/**
 * function that copies one string to another until string is zero-terminated
 * \param in sourcestring
 * \param out string to copy to
 */
void strcpy(char *in,char *out) {
	char i;
	for(i=0;in[i]!=0;i++)
		out[i]=in[i];
}

/**
 * loads current device (the one from the global variable 'device') from bankstick into device struct
 */
void loadDevice() {
	unsigned long addr,k;
	for(addr=0;addr<DEVICE_MENU_ENTRY;addr++) {
		((char*)selected_device)[addr]=readBank(BASE+DEVICE_MENU_ENTRY*device+addr);
	}
	if(selected_device.dump_addr!=0) {
		for(k=0;k<7;k++)
			((char*)(selected_device.dump))[k]=readBank(((long)selected_device.dump_addr)+k+BASE_DYNMEM+device*DYNBLOCK_LENGTH);
	}
}

/**
 * loads current modul (the one from the global variable 'modul') from bankstick into modul struct
 */
void loadModul() {
	unsigned long addr,k;
	for(addr=0;addr<MODUL_MENU_ENTRY;addr++)
		((char*)selected_modul)[addr]=readBank(BASE+DEVICE_MENU_LENGTH+16*MODUL_MENU_ENTRY*bank+64*MODUL_MENU_ENTRY*device+addr);
	if(selected_modul.dump_addr!=0) {
		for(k=0;k<7;k++)
			((char*)(selected_modul.dump))[k]=readBank(((long)selected_modul.dump_addr)+k+BASE_DYNMEM+device*DYNBLOCK_LENGTH);
	}
}


/**
 * calls next element into eb_request, in case more than one sysex messages are needed to request edit buffer (e.g. MIDI cross bar)
 *	\return 1 if succesful 0 if unsuccesful, eb_request.active is set to false
 */
char nextDump() {
	unsigned long i,addr=((long)(eb_request.dump_addr))+((long)7)+eb_request.dump.length+eb_request.dump.header_length+BASE_DYNMEM+DYNBLOCK_LENGTH;
	if(eb_request.dump.typ&0x01) {
		eb_request.data_counter=0;
		eb_request.timer=0;
		eb_request.error=0;
		for(i=0;i<7;i++)
			((char*)(eb_request.dump))[i]=readBank(i+addr);
		if((eb_request.dump.typ&0x01)==0)
			eb_request.checksum_func=eb_request.dump.checksum_func;
		//eb_request
		return 1;
	} else {
		eb_request.active=0;
		return 0;
	}

}

/**
 * transmit current sysex string from eb_request (stored in eb_request.dump)
 */
void requestDump() {
	unsigned long i,dl=eb_request.dump.length,addr=((long)(eb_request.dump_addr))+7+BASE_DYNMEM+DYNBLOCK_LENGTH*device;
	if((eb_request.dump.typ&0x01)==0) {
		eb_request.active=1;

		//debugc((eb_request.dump.length));

		for(i=0;i<eb_request.dump.header_length;i++)
			edit_buffer_header[i]=(readBank(addr+i+dl));

		MIOS_MIDI_BeginStream;
		MIOS_MIDI_TxBufferPut(0xF0);
		for(i=0;i<dl;i++) {
			MIOS_MIDI_TxBufferPut(readBank(addr+i));
		}
		MIOS_MIDI_TxBufferPut(0xF7);
		MIOS_MIDI_EndStream;
	}

//addr=((long)(eb_request.dump_addr))+7+BASE_DYNMEM+DYNBLOCK_LENGTH*device+selected_modul.dump.length;

	/*
	if((eb_request.dump.typ>>6)==0) {
		for(i=0;i<eb_request.dump.header_length;i++)
			edit_buffer_header[i]=readBank(i+addr+eb_request.dump.length);
		MIOS_MIDI_BeginStream;
		for(i=0;i<eb_request.dump.length;i++) {
			MIOS_MIDI_TxBufferPut(readBank(addr+i));
		}
		MIOS_MIDI_EndStream;
	}else if((eb_request.dump.typ>>6)==1) {
		MIOS_MIDI_BeginStream;
		for(i=0;i<eb_request.dump.length;i++) {
			MIOS_MIDI_TxBufferPut(readBank(addr+i));
		}
		MIOS_MIDI_EndStream;
	}
	*/
}

/**
 * executes nextDump and requestDump if nextDump was succesful
 */
void requestNextDump() {
	if(nextDump())
		requestDump();
}

/**
 * This function reads the data of one parameter page out of the eerom into the global arrays parametersleft[] and parametersright[]
 */
void loadParameterPage() {
	unsigned long i,k,offset,x;
	int value,min,max;

	for (i=0;i<8;i++)	{
    	offset = (i*PARAMETER_LENGTH+BASE+DEVICE_MENU_LENGTH+MODUL_MENU_LENGTH+device*DEVICE_LENGTH+bank*BANK_LENGTH+modul*MODUL_LENGTH);
		for (k=0;k<PARAMETER_LENGTH;k++) {
        	(((char*)(&(parametersleft[i])))[k]) = readBank (k+offset);
        }

        value=(((int)(parametersleft[i].value[0]))<<(7*(((parametersleft[i].modeSelect)&0x02)>>1))) | ((int)(parametersleft[i].value[1])*(((parametersleft[i].modeSelect)&0x02)>>1));
	    potis[i]=value;

		if(parametersleft[i].messageChannel&0x70 && (parametersleft[i].number[0]!=0 || parametersleft[i].number[1]!=0)) {
			x=(parametersleft[i].number[0]<<8|parametersleft[i].number[1]);
			x+=BASE_DYNMEM+DYNBLOCK_LENGTH*device;
			for(k=0;k<(long)sizeof(SysexStruct);k++) {
				((char*)(parametersleft[i].sysex))[k]=readBank(x+k);
			}
		}

	}

	for (i=8;i<16;i++) {
    	offset = (i*PARAMETER_LENGTH+BASE+DEVICE_MENU_LENGTH+MODUL_MENU_LENGTH+device*DEVICE_LENGTH+bank*BANK_LENGTH+modul*MODUL_LENGTH);
		for (k=0;k<PARAMETER_LENGTH;k++) {
    		(((char*)(&(parametersright[i%8])))[k]) = readBank (k+offset);
        }
 		value=(((int)(parametersright[i%8].value[0]))<<(7*(((parametersright[i%8].modeSelect)&0x02)>>1))) | ((int)(parametersright[i%8].value[1])*((parametersright[i%8].modeSelect)&0x02)>>1);
	  	potis[i]=value;

		if(parametersright[i%8].messageChannel&0x70 && (parametersright[i%8].number[0]!=0 || parametersright[i%8].number[1]!=0)) {
			x=(parametersright[i%8].number[0]<<8|parametersright[i%8].number[1]);
			x+=BASE_DYNMEM+DYNBLOCK_LENGTH*device;
			for(k=0;k<(long)sizeof(SysexStruct);k++)
				((char*)(parametersright[i%8].sysex))[k]=readBank(x+k);
		}

    }

}

/**
 * This function writes the values of one parameter page into the eerom
 */
void saveParameterValues() {
	unsigned long i,offset;
	char k,value;
	for (i=0;i<16;i++)	{
		//offset=((i*PARAMETER_LENGTH+18)+BASE+DEVICE_MENU_LENGTH+MODUL_MENU_LENGTH+device*DEVICE_LENGTH+bank*BANK_LENGTH+modul*MODUL_LENGTH);
		//debugl    (addr);
		/*unsigned char x;
		x=0;
		PRODL = x;
		__asm
				movf 0xFFC, w
				movwf _PRODL
		__endasm;

		debugc(x);*/
		//x1=(potis[i])&0x7F;
		//x2=(potis[i]>>7);
		//writeBank (addr,(char)x1);
		//writeBank (addr+1,(char)x2);

		offset=(BASE+DEVICE_MENU_LENGTH+MODUL_MENU_LENGTH+device*DEVICE_LENGTH+bank*BANK_LENGTH+modul*MODUL_LENGTH);

		if (i<=7) {
			k=(((parametersleft[i].modeSelect)&0x02)>>1);
		} else {
			k=(((parametersright[i%8].modeSelect)&0x02)>>1);
		}

		if (i<=7) {
			if ((parametersleft[i].edit)&0x1000) {
				value=((((char)(potis[i]))>>(7*k))&0x7F);
				writeBank (((i*PARAMETER_LENGTH+18)+offset),value);
				value=((((char)(potis[i]))>>(7*(1-k)))&0x7F);
				writeBank (((i*PARAMETER_LENGTH+19)+offset),value);
			}

		} else {
			if ((parametersright[i%8].edit)&0x1000) {
				value=((((char)(potis[i]))>>(7*k))&0x7F);
				writeBank (((i*PARAMETER_LENGTH+18)+offset),value);
				value=((((char)(potis[i]))>>(7*(1-k)))&0x7F);
				writeBank (((i*PARAMETER_LENGTH+19)+offset),value);
			}
		}
	}

		//writeBank ((i*PARAMETER_LENGTH+18)+BASE+DEVICE_MENU_LENGTH+MODUL_MENU_LENGTH+device*DEVICE_LENGTH+bank*BANK_LENGTH+modul*MODUL_LENGTH,(char)(potis[i])&0x7F);
		//writeBank ((i*PARAMETER_LENGTH+19)+BASE+DEVICE_MENU_LENGTH+MODUL_MENU_LENGTH+device*DEVICE_LENGTH+bank*BANK_LENGTH+modul*MODUL_LENGTH,(char)(potis[i]>>7));
	//}
}

/**
 * sets LEDs and initiates display refresh
 */
void initDeviceModus() {
	char i;
	preset_flag=0x00;
	bank=0;
	update|=0x01;
	for(i=0;i<4;i++)
		MIOS_DOUT_PinSet(i,1);
	modus=MOD_DEVICE;
}


/**
 *  sets LEDs and initiates display refresh
 */
void initModulModus() {
	char i;
	for(i=0;i<4;i++)
		MIOS_DOUT_PinSet(i,bank==i?1:0);
	if(modus==MOD_DEVICE)
		loadDevice();
	update|=0x01;
	modus=MOD_MODUL;
}


/**
 * sets LEDs and initiates display refresh
 */
void initParameterModus() {
	char i;
	loadModul();
	loadParameterPage();
	if(selected_modul.dump_addr) {
		if(!(selected_modul.dump.typ&0x01)) {
			if(eb_request.active==0) {
				eb_request.timer=0;
				eb_request.data_counter=0;
				eb_request.error=0;
				eb_request.dump_addr=selected_modul.dump_addr;
				for(i=0;i<7;i++)
					((char*)eb_request.dump)[i]=((char*)selected_modul.dump)[i];
				eb_request.checksum_func=eb_request.dump.checksum_func;
				requestDump();
				//display "load EB"
				update|=0x04;
			}
		}
	} else if(selected_device.dump_addr) {
		if(!(selected_device.dump.typ&0x01)) {
			if(eb_request.active==0) {
				eb_request.timer=0;
				eb_request.data_counter=0;
				eb_request.error=0;
				eb_request.dump_addr=selected_device.dump_addr;
				for(i=0;i<7;i++)
					((char*)eb_request.dump)[i]=((char*)selected_device.dump)[i];
				eb_request.checksum_func=eb_request.dump.checksum_func;
				//eb_request.dump=selected_device.dump;
				requestDump();
				//display "load EB"
				update|=0x04;
			}
		}
	} else {
		update|=0x03;
	}
	modus=MOD_PARAMETER;
	potis[16]=0xFFFF;
}

/**
 * This function is called by MIOS after startup to initialize the
 * application
 */
void Init(void) __wparam
{
	unsigned char i;

	device=0;
	bank=0;
	modul=0;
	knobs=0;
	update=0;
	bankstick=0;
	bankchip=0;
	display=CURENT_DISPLAY;

	send_editbuffer=0;
	led_time=0;
	get_userdata=0x00;
	buffer_view=0x0000;
	storage_view=0x80000L;

	update=0;
	mem_adress=0;
	record_flag=0;
	modus=MOD_DEVICE;
	bankinitcount=0;
	preset_flag=0x00;
	preset_encoder=0x00;
	stepwidth=0x7F;
	sysex=0xFF;
	check_sum=0;
	receive_escape=0;
	transfer_counter=0;
	eb_request.active=0;
	eb_request.error=0;
	eb_request.checksum_func=0;
	eb_request.data_counter=0;
	eb_request.timer=0;
	eb_request.dump_addr=0;
	for (i=0;i<16;i++)
		preset_number[i]=0x00;
	for (i=0;i<18;i++)
		potis[i]=0x00;



	//stat.bankchip=0;
	//stat.display=CURENT_DISPLAY;
	//stat.update=0;

	//MIOS_IIC_CtrlSet(0x01);					//von holle

	MIOS_TIMER_Init(0x00, 100000);


	//--- Display initialisieren---//

	MIOS_LCD_TypeSet(7,0,0);



	//--- Encoder initialisieren---//



	// set shift register update frequency
	MIOS_SRIO_UpdateFrqSet(1); // ms

	// we need to set at least one IO shift register pair
	MIOS_SRIO_NumberSet(8); // for 128 pins

	// set speed mode for 16 encoders
	for(i=0; i<16; ++i)
		// available speed modes: SLOW, NORMAL and FAST
		MIOS_ENC_SpeedSet(i, MIOS_ENC_SPEED_NORMAL, 1); // encoder, speed mode, divider

	initDeviceModus();

	//init_dynamic_cont();


}

void init_static() {
	char *buffer;
	unsigned long addr;
	unsigned long i,k;
	unsigned long j;
	unsigned long x;
	parameter param;
	Device d;


	if(bankinitcount==0) {
		//device
		//buffer="12345     1234      --                                                                                                                                                                                                                                                                                                                                        ";
		//buffer="1                   --         2          --3                   --         4          --5                   --         6          --7                   --         8          --9                   --        10          --11                  --        12          --13                  --        14          --15                  --        16          ";
		buffer="                    --                    --                    --                    --                    --                    --                    --                    --                    --                    --                    --                    --                    --                    --                    --                    ";
		for(addr=BASE;addr<DEVICE_MENU_LENGTH+BASE;addr++) {
			if(buffer[addr-BASE]=='-')
				writeBank(addr,0);
			else
				writeBank(addr,buffer[addr-BASE]);
		}
		//writeBank(0x80001,5);
		//debugc(readBank(0x80001));
		bankinitcount++;
	}  else if(0<bankinitcount && bankinitcount<=64) {
		//modul
		k=bankinitcount-1;
		buffer="osc1      hihi      --      osc2      hihi--osc3      hihi      --      osc4      hihi--osc5      hihi      --      osc6      hihi--osc7      hihi      --      osc8      hihi--osc9      hihi      --     osc10      hihi--osc11     hihi      --     osc12      hihi--osc13     hihi      --     osc14      hihi--osc15     hihi      --     osc16      hihi";
		for(addr=BASE+DEVICE_MENU_LENGTH;addr<BASE+DEVICE_MENU_LENGTH*2;addr++) {
			if(buffer[addr-(BASE+DEVICE_MENU_LENGTH)]=='-')
				writeBank(addr+k*DEVICE_MENU_LENGTH,0);
			else
				writeBank(addr+k*DEVICE_MENU_LENGTH,buffer[addr-(BASE+DEVICE_MENU_LENGTH)]);
		}
		bankinitcount++;
	} else if(64<bankinitcount && bankinitcount<=((16*4)+65)) {							//for all parameters 1079

		for(i=0;i<16;i++) {

			strcpy("knob      ",param.name);
			param.messageChannel = 0x7F;
			param.modeSelect = 0x00;
			param.number[0] = 0x01;
			param.number[1] = 0x00;
			param.valueMin[0] = 0x00;
			param.valueMin[1] = 0x00;
			param.valueMax[0] = 0x7F;		//MSB
			param.valueMax[1] = 0x00;		//LSB
			param.value[0] = 0x01;
			param.value[1] = 0x00;
			param.scale = 0x01;
			param.edit =  (0x0000|(((bankinitcount-65)*16)+i));
			param.sswitch_addr = 0;

			//setCursor(0,0,2);
			//MIOS_LCD_PrintHex1(addr>>16);

			addr=i*PARAMETER_LENGTH+BASE+DEVICE_MENU_LENGTH+MODUL_MENU_LENGTH+((bankinitcount-65)*MODUL_LENGTH);
			for(k=0;k<PARAMETER_LENGTH;k++) {
				writeBank(addr+k,((char*)(&param))[k]);
			}
		}
		bankinitcount++;

	} else if(bankinitcount==1025+65) {
		//setCursor(0,0,1);
		//printString(0,"InitComplet");
		bankinitcount++;
	} else
	return;
}

void init_dynamic() {
	long addr,i;
	if(bankinitcount<255000) {
		for(addr=0;addr<8;addr++)
			writeBank(addr+bankinitcount+0x80000L,addr);
		bankinitcount+=8;
	}
}

/*
typedef struct SysexStruct_s {
	unsigned char convert_func;			//!< points to convert function to convert the data from the potis[] array to sysex string
	unsigned char convert_func_addr;	//!< adress where the converted data has to be put into the sysex string
	unsigned char length;				//!< length of the sysexstring that follows up in bankstick
}SysexStruct;
*/

void init_dynamic_cont() {
	long addr,i,k;
	parameter param;
	SysexStruct sys;
	DumpStruct ds;
	unsigned char buffer[10],*buffer1;
	strcpy("abcdefghij",buffer);
	//buffer[0]=0xf0;
	//buffer[9]=0xf7;
	if(bankinitcount==0) {
		sys.convert_func=1;
		sys.convert_func_addr=3;
		sys.length=10;
		for(i=0,addr=BASE_DYNMEM+1;i<3;i++,addr++) {
			//debugl(addr);
			writeBank(addr,((char*)sys)[i]);
		}
		for(i=0;i<10;i++,addr++)
			writeBank(addr,buffer[i]);
		bankinitcount++;
	}
	if(bankinitcount==1) {
		strcpy("holle6",buffer);
		for(i=0,addr=BASE_DYNMEM+14;i<6;i++,addr++) {
			writeBank(addr,buffer[i]);
		}
		strcpy("minus ",buffer);
			for(i=0;i<6;i++,addr++) {
				writeBank(addr,buffer[i]);
		}
		strcpy("mariin",buffer);
			for(i=0;i<6;i++,addr++) {
				writeBank(addr,buffer[i]);
		}
		strcpy("gertiu",buffer);
			for(i=0;i<6;i++,addr++) {
				writeBank(addr,buffer[i]);
		}
		strcpy("jacjac",buffer);
			for(i=0;i<6;i++,addr++) {
				writeBank(addr,buffer[i]);
		}
		strcpy("flokke",buffer);
			for(i=0;i<6;i++,addr++) {
				writeBank(addr,buffer[i]);
		}
		bankinitcount++;
	}
	if(bankinitcount==2) {
		strcpy("parameterX",param.name);
		param.messageChannel = 0x7F;
		param.modeSelect = 0x00; //((bankinitcount%4)<<4);
		param.number[0] = 0x00;
		param.number[1] = 0x01;
		param.valueMin[0] = 0x00;
		param.valueMin[1] = 0x00;
		param.valueMax[0] = 0x7F;
		param.valueMax[1] = 0x00;
		param.value[0] = 0x01;
		param.value[1] = 0x00;
		param.scale = 0x01;
		param.edit = 0x101A;
		param.sswitch_addr = 0;

		addr=BASE+DEVICE_MENU_LENGTH+MODUL_MENU_LENGTH;   //2*PARAMETER_LENGTH+
		for(k=0;k<PARAMETER_LENGTH;k++) {
			writeBank(addr+k,((char*)(&param))[k]);
		}
		bankinitcount++;

	}
	if(bankinitcount==3) {
		buffer1="synthX    hihi      +-";
		for(addr=0;addr<22;addr++) {
			if(buffer1[addr]=='+') {
				writeBank(addr,1);
			} else if(buffer1[addr]=='-')
				writeBank(addr,0);
			else
				writeBank(addr,buffer1[addr]);
		}
		bankinitcount++;

	}
	if(bankinitcount==4) {
			int i;
			char buffer2[]={0x10,0x06,0x04,0x04,0x00};
			char buffer3[]={0x10,0x06,0x01,0x01};

			ds.typ=0x3C;
			ds.checksum_func=1;
			ds.wrong_header_length=4;
			ds.edit_buffer_length=268;
			ds.length=5;
			ds.header_length=4;
			for(i=0;i<7;i++)
				writeBank(i+0x80001L,((char*)ds)[i]);
			for(i=0;i<ds.length;i++)
				writeBank(7+i+0x80001L,buffer2[i]);
			for(i=0;i<ds.header_length;i++)
				writeBank(7+i+0x80001L+((long)ds.length),buffer3[i]);
			bankinitcount++;
	}
}

/**
 * This function is called by MIOS in the mainloop when nothing else is to do
 */
void Tick(void) __wparam
{

unsigned char i=0x00;

	//init_static();
	//init_dynamic_cont();
	//init_dynamic();

	if(eb_request.error) {


		i=led_time/50;
		MIOS_DOUT_PinSet(0,i==1?1:0);
		MIOS_DOUT_PinSet(1,i==1?1:0);
		MIOS_DOUT_PinSet(3,i==1?1:0);
		MIOS_DOUT_PinSet(2,i==1?1:0);
		if (led_time>99)
			led_time=0;

		return;
	}



	if (get_userdata) {
		if (get_userdata==0x01){
			initDeviceModus();
			get_userdata=0x00;
			return;
		}
		i=led_time/50;
		MIOS_DOUT_PinSet(0,i==0?1:0);
		MIOS_DOUT_PinSet(1,i==1?1:0);
		MIOS_DOUT_PinSet(3,i==2?1:0);
		MIOS_DOUT_PinSet(2,i==3?1:0);

		if (led_time>199) {
			get_userdata--;
			led_time=0;
		}

		return;
	}

	if (send_editbuffer==0x01) {
			sendEditBuffer();
			send_editbuffer=0x00;
	}

}

/**
 *  This function is called by MIOS when a complete MIDI event has been received
 */
void MPROC_NotifyReceivedEvnt(unsigned char evnt0, unsigned char evnt1, unsigned char evnt2) __wparam
{
}

/**
 * This function is called by MIOS when a MIDI event has been received
 * which has been specified in the MIOS_MPROC_EVENT_TABLE
 */
void MPROC_NotifyFoundEvent(unsigned entry, unsigned char evnt0, unsigned char evnt1, unsigned char evnt2) __wparam
{
}

/**
 * This function is called by MIOS when a MIDI event has not been completly
 * received within 2 seconds
 */
void MPROC_NotifyTimeout(void) __wparam
{
}


/**
 * This function is called by MIOS before the shift register are loaded
 */
void SR_Service_Prepare(void) __wparam
{
}

/**
 * This function is called by MIOS after the shift register have been loaded
 */
void SR_Service_Finish(void) __wparam
{
}

/**
 * This function is called by MIOS when a pot has been moved
 */
void AIN_NotifyChange(unsigned char pin, unsigned int pin_value) __wparam
{
}
