/* ======================================
     同期コマンドの後処理(チョー手抜き)
   ====================================== */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "MThdINFO.h"
#include "structs.h"
#include "VarLen.h"
#include "etc.h"

typedef struct {
	int abstime;	/* 絶対時間 */
	int vtrk;	/* table[]の添え字 */
} SORT;

#include "config.h"
void do_sync(UBYTE *MTrk[],DWORD syncs, SYNCDAT *sync,
		SYNCDAT **wsync,DWORD midc_all[]);
/* wsync は syncの中身をを書き換えるためのポインタ: *wsync == sync */

DWORD sync_main(UBYTE *MTrkTrk,SYNCDAT *sync,DWORD midc_all[],
		DWORD recpos,DWORD recposmax,DWORD abstime,DWORD recwait,
		DWORD rectrk,DWORD midc);
void shifttrk(UBYTE *MTrkTrk,SYNCDAT *sync,DWORD midc_all[],
		DWORD byte1,DWORD byte2,DWORD recpos,DWORD recposmax,
		DWORD rectrk,DWORD midc);
void _sort(SORT queue[],DWORD start,DWORD end);	/* end には+1した値を入れる */


void do_sync(UBYTE *MTrk[],DWORD syncs, SYNCDAT *sync,
		SYNCDAT **wsync,DWORD midc_all[])
{
	DWORD i,j,k,l=0,last=syncs;
#ifdef __GCC__
	SORT queue[syncs];		/* ソート用のバッファ */
	DWORD trkstat[TRKMAX],sendstat[TRKMAX];
#else
	SORT *queue;
	DWORD *trkstat,*sendstat;
#endif
					/* 添え字が本当のトラック番号、
					 * 変数の値がtable[vtrk][0] のvtrkに
					 */
					/* trkstat[trk]:trkが受信待ちなら=vtrk,
					 *		さもなくば-1
					 * sendstat[trk]:trkに送信が既に来ていれば
							送信元vtrk
					 */
	DWORD tableend,tableendnow;	/* 同期に使用するトラック数 */
#ifdef __GCC__
	DWORD table[syncs][3];		/* 同期処理用、トラックに見立てる */
#else
	DWORD **table;
#endif
					/* table[][0] : 今どのsync配列をみているか
					 * table[][1] : トラック末尾+1
					 * table[vtrk][2] : vtrkの総待機時間
					 */

#ifndef __GCC__
	queue = (SORT*)emalloc(sizeof(SORT) * syncs, "queue");
	trkstat = (DWORD*)emalloc(sizeof(DWORD) * TRKMAX, "trkstat");
	sendstat = (DWORD*)emalloc(sizeof(DWORD) * TRKMAX, "sendstat");
	//table = (DWORD**)emalloc(sizeof(DWORD) * syncs, "table");//修正 by Kobarin(64bitビルドで不具合発生)
	table = (DWORD**)emalloc(sizeof(DWORD*) * syncs, "table");//修正 by Kobarin(64bitビルドで不具合発生)
	for (i = 0; i < syncs; i++) {
		table[i] = (DWORD*)emalloc(sizeof(DWORD) * 3, "table[i]");
	}
#endif

	for (i = 0; i < TRKMAX; i++) {
		trkstat[i] = sendstat[i] = -1;
	}

	table[0][0] = table[0][2] = 0;		/* まず、table[][]を作成 */
	j = ( sync[0].midc < 0)? -sync[0].midc-1 : sync[0].trk;	/* j == 自分のtrk */
	for (i = 1; i < syncs; i++) {		/* 同期列を使用トラックごとに分ける */
		k = ( sync[i].midc < 0)? -sync[i].midc-1 : sync[i].trk;
		if (k != j) {
			j = k;
			table[l++][1] = i;
			table[l][0] = i;
			table[l][2] = 0;
		}
	}
	table[l++][1] = syncs;
	tableend = tableendnow = l;

	for (i = 0; i < tableend; i++) {	/* 次にqueueを作成し */
		queue[i].abstime = 0 + sync[ table[i][0] ].total;
		queue[i].vtrk    = i;
	}
	_sort(queue,0,tableendnow);		/* ソートする */


	while (last > 0) {		/* 未処理の同期がある間ループ */
		DWORD i;			/* ,last; */
		DWORD flag = 0;
		DWORD vtrk = queue[0].vtrk;
		DWORD now = table[vtrk][0];
		DWORD totrk = trkstat[ sync[now].trk ];
		const DWORD last_backup = last;
		static DWORD last_count = 0;

		if (sync[now].midc < 0 ) {	/* 送信なら */

			if (totrk == -1) {	/* 受信待ちされてない */
#ifdef AA
#ifndef ENGLISH
				fprintf(stderr,"\ntrk%dのw%dは受信に先行しています。",
					-sync[now].midc-1+1,sync[now].trk+1);
#else
				fprintf(stderr,"\nWarning: w%d in trk%d: sending is earlier than receiving.\n",
					sync[now].trk+1,-sync[now].midc-1+1);
#endif
#endif
				sendstat[ sync[now].trk ] = vtrk;
				last--;
				flag = 1;

				if ( ++table[vtrk][0] >= table[vtrk][1] ) {
								/* もうtable[vtrk]に
								 * 残りがないなら */
					tableendnow--;
				}
			} else {
				/* int send,rec; */
				DWORD abstime,recwait,midc;
				DWORD sendpos,recpos,recposmax;
				DWORD targettrk = sync[now].trk;
				UBYTE *MTrkTrk = &MTrk[targettrk+1][0];

				sendpos   = table[vtrk][0];
				recpos    = table[totrk][0];
				recposmax = table[totrk][1];
				abstime   = table[vtrk][2]
						 + sync[sendpos].total;
				recwait   = table[totrk][2];
				midc = sync[recpos].midc;

/*同期処理メイン*/		table[totrk][2] = sync_main(MTrkTrk,sync,
							midc_all,
							recpos,recposmax,
							abstime,recwait,
							targettrk,midc);
				(*wsync)[recpos].offset = abstime;

				last--;
				trkstat[ sync[now].trk ] = sendstat[ sync[now].trk ] = -1;
				if ( ++table[vtrk][0] >= table[vtrk][1] ) {
								/* もうtable[vtrk]に
								 * 残りがないなら */
					tableendnow--;
				}
				if ( ++table[totrk][0] >= table[totrk][1] ) {
								/* もうtable[totrk]に
								 * 残りがないなら */
					tableendnow--;
				}
			}

		} else {			/* 受信なら */

			if (totrk != -1) {	/* 既に受信待ち */
				flag = 1;
			} else {
				if ( sendstat[ sync[now].trk ] != -1 ) {  /* 既に送信
									   * されてれば */

					/* int send,rec; */
					DWORD midc;
					DWORD recpos,recposmax;
					DWORD targettrk = sync[now].trk;
					DWORD gate1,gate_old,byte1,byte2;
					UBYTE *MTrkTrk = &MTrk[targettrk+1][0];

#ifdef AA
#ifndef ENGLISH
					fprintf(stderr,"\ntrk%dのwは先行した送信の受信です。",
						sync[now].trk+1);
#else
					fprintf(stderr,"\nWarning: w in trk%d: receiving is later than sending.",
						sync[now].trk+1);
#endif
#endif
					totrk     = queue[0].vtrk;
					recpos    = table[totrk][0];
					recposmax = table[totrk][1];
					midc      = sync[recpos].midc;
					byte1 = GetVarLen(&MTrkTrk[midc],&gate1);
							/* 受信側のデルタタイム */
					gate_old = gate1 + sync[recpos].offset;
					byte2 = get_VarLen_bytes(gate_old);
							/* 最終δtimebyte数 */

/*同期処理メイン*/			shifttrk(MTrkTrk,sync,
						midc_all,
						byte1,byte2,
						recpos,recposmax,
						targettrk,midc);
					PutVarLen(&MTrkTrk[midc],gate_old);
							/* gateのセット */

					(*wsync)[recpos].offset = table[totrk][2]
						+ sync[ sendstat[ sync[now].trk ] ].total;

					last--;
					sendstat[ sync[now].trk ] = -1;
					if ( ++table[totrk][0] >= table[totrk][1] ) {
									/* もうtable[totrk]に
									 * 残りがないなら */
						tableendnow--;
					}
				} else {	/* 受信待ちでなく、送信もまだない==通常 */
					trkstat[ sync[now].trk ] = vtrk;
					last--;
					flag = 1;
					/* table[vtrk][0]++; */
				}
			}
		}

		if (!flag) {		/* SMFに書き換えが起これば */
			DWORD i,j=0;

			for (i = 0; i < tableend; i++) {	/* 次のqueueを作成し直す */
				if ( table[i][0] < table[i][1] ) {	/* まだtable[i]に
									 * 残りがあるなら */
					queue[j].abstime = table[i][2] +
							sync[ table[i][0] ].total;
					queue[j++].vtrk  = i;
				}
			}
			_sort(queue,0,j);			/* だっせぇ */
		} else {
			/* 単にqueueから今処理した内容を削除 */
			/* for (i = 0; i < tableendnow-1; i++) { */
			for (i = 0; i < tableendnow-1; i++) {
				queue[i] = queue[i+1];
			}
		}

		flag = 0;
		for (i = 0; i < tableend; i++) {	/* 送信が残っているか調べる */
			if ( table[i][0] < table[i][1] &&	/* まだtable[i]に
								 * 残りがあるなら */
				sync[ table[i][0] ].midc < 0 ) {
					flag = 1;
			}
		}
		if (!flag) {			/* 送信がなければ、残りの受信は不要 */
			for (i = 0; i < tableend; i++) {
				if ( table[i][0] < table[i][1] ) {	/* まだtable[i]に
									 * 残りがあるなら */
#ifdef AA
#ifndef ENGLISH
					fprintf(stderr,"\ntrk%dに不要なwがあります。",
						sync[ table[i][0] ].trk+1);
#else
					fprintf(stderr,"\nWarning: last w in trk%d is unnecessary.(you can omit it)",
						sync[ table[i][0] ].trk+1);
#endif
#endif
				}
			}
			break;
		}
		if (last_backup == last) {		/* 変換に進展が無い */
			if (++last_count > tableend) {
#ifndef ENGLISH
				fatal(1,"\n同期変換に失敗しました。\nzmusic -C でzmsをコンパイルし直して下さい。\n        ^(大文字)");
#else
				fatal(1,"\nfailed to converting Synchronouses.\nPlease re-compile zms with zmusic -C.\n                                  ^^ in capital");
#endif
			}
		} else {
			last_count = 0;
		}
	}

#ifndef __GCC__
	efree(queue, "queue");
	efree(trkstat, "trkstat");
	efree(sendstat, "sendstat");
	for (i = 0; i < syncs; i++) {
		efree(table[i], "table[i]");
	}
	efree(table, "table");
#endif


}





DWORD sync_main(UBYTE *MTrkTrk,SYNCDAT *sync,
		DWORD midc_all[],
		DWORD recpos,DWORD recposmax,
		DWORD abstime,DWORD recwait,
		DWORD rectrk,
		DWORD midc)
{
	DWORD gate_old,gate1,gate2 = sync[recpos].offset;
	DWORD byte1,byte2;

	byte1 = GetVarLen(&MTrkTrk[midc],&gate1);	/* 受信側のデルタタイム */

	gate_old = abstime - recwait - sync[recpos].total + gate1 + gate2;

	/* で、結果を書き込む前に、今後のために待機時間
	 * を保存しておく */

	recwait += gate_old - gate1 - gate2;		/* wの待機時間を付け足す */
	byte2 = get_VarLen_bytes(gate_old);		/* 最終δtimebyte数 */

	if (byte1 != byte2) {				/* gateのバイト数が違う時 */
		shifttrk(MTrkTrk,sync,
			midc_all,
			byte1,byte2,
			recpos,recposmax,
			rectrk,
			midc);
	}
	PutVarLen(&MTrkTrk[midc],gate_old);	/* gateのセット */

	return recwait;
}






void shifttrk(UBYTE *MTrkTrk,SYNCDAT *sync,
		DWORD midc_all[],
		DWORD byte1,DWORD byte2,
		DWORD recpos,DWORD recposmax,
		DWORD rectrk,
		DWORD midc)
{
	DWORD k;
						/* 所謂メモリのブロック転送 */
/*
	for (k = midc_all[rectrk+1]; k >= midc+byte1; k--) {
		MTrkTrk[k+byte2-byte1] = MTrkTrk[k];
	}
*/
	/* _rmemcpy */
	memmove(&MTrkTrk[midc + byte2], &MTrkTrk[midc + byte1],
		midc_all[rectrk + 1] - midc - byte1 + 1);

	midc_all[rectrk+1] += byte2 - byte1;
						/* byte数が変わると、これ以降の
						   sync[].midcが全部すれるので
						   それを補正する。 */
	for (k = recpos+1; k < recposmax; k++) {
		if (sync[k].trk == rectrk && sync[k].midc >= 0) {
			sync[k].midc += byte2-byte1;
		}
	}

	/* *(int *)(MTrkTrk + 4) += byte2 - byte1; */		/* rewrite header bytes */
	k = getDwordAlign(MTrkTrk + 4);
	putDwordAlign(MTrkTrk + 4, k + byte2 - byte1);		/* rewrite header bytes */
}


/* ===================================================
     SORT[] の指定された範囲を、totalでソートする
   =================================================== */

void _sort(SORT queue[],DWORD start,DWORD end)	/* end には+1した値を入れる */
{
	DWORD j;

	for (j = start + 1; j < end; j++) {	/* 挿入ソート(安定) */
		DWORD i = j - 1;
		const DWORD key = queue[j].abstime;
		const SORT temp = queue[j];

		while (i >= start && queue[i].abstime > key) {
			queue[i+1] = queue[i];
			i--;
		}
		queue[i+1] = temp;
	}
}
