#include "main.h"

static char *Profile = CHIMEINI;
static char *Version = CHIMEVER;

CMyMainDialog::CMyMainDialog(CWnd *parentWnd)
{
	RECT dr;
	RECT pcr;
	RECT pwr;
	
	p_gotProfile = FALSE;
	
	Create("main", parentWnd);
	
	GetClientRect(&dr);
	parentWnd->GetWindowRect(&pwr);
	parentWnd->GetClientRect(&pcr);
	
	pwr.right += dr.right - (pcr.right - pcr.left);
	pwr.bottom += dr.bottom - (pcr.bottom - pcr.top);
	
	parentWnd->MoveWindow(&pwr);
		
}

void
CMyMainDialog::cleanup()
{
	// close MIDI
	if (p_hMidi) {
		midiOutClose(p_hMidi);
	}
	
	if (p_gotProfile) {
		saveDefaults();
	}
}

void
CMyMainDialog::saveDefaults()
{
	char *buf = new char[30];
	char *buf2 = new char[30];
	int i;
	
	wsprintf(buf, "%d", p_channel + 1);
	WritePrivateProfileString(Version, "channel",
		buf, Profile);
		
	for (i = 0; i < 16; i++) {
		wsprintf(buf, "program%d", i + 1);
		wsprintf(buf2, "%d", p_program[i]);
		WritePrivateProfileString(Version,
			buf, buf2, Profile);
	}
	wsprintf(buf, "%d", p_minVol);
	WritePrivateProfileString(Version, "minvol", buf, Profile);
	wsprintf(buf, "%d", p_maxVol);
	WritePrivateProfileString(Version, "maxvol", buf, Profile);
	
	wsprintf(buf, "%d", p_minLength);
	WritePrivateProfileString(Version, "minlength", buf, Profile);
	wsprintf(buf, "%d", p_maxLength);
	WritePrivateProfileString(Version, "maxlength", buf, Profile);

	wsprintf(buf, "%d", p_lowNote);
	WritePrivateProfileString(Version, "lownote", buf, Profile);
	wsprintf(buf, "%d", p_highNote);
	WritePrivateProfileString(Version, "highnote", buf, Profile);
	
	wsprintf(buf, "%d", p_restProb);
	WritePrivateProfileString(Version, "rest", buf, Profile);
	
	for (i = 0; i < 128; i++) {
		wsprintf(buf, "note%d", i + 1);
		wsprintf(buf2, "%d", p_noteProb[i]);
		WritePrivateProfileString(Version, buf, buf2, Profile);
	}
			
	delete buf;
	delete buf2;
}

BOOL
CMyMainDialog::OnInitDialog()
{
	UINT merr;
	int i;
	char *buf = new char[30];

	CDialog::OnInitDialog();
	
	p_playing = FALSE;
	p_note = -1;
	
	for (i = 0; i < 16; i++) {
		wsprintf(buf, "program%d", i + 1);
		p_program[i] = GetPrivateProfileInt(Version, buf, -1,
			Profile);
	}
	
	p_channel = GetPrivateProfileInt(Version, "channel", 1, Profile) - 1;
	p_lastChannel = 255;	
	channelSB()->SetScrollRange(0, 15);
	channelChange(-1);

	programSB()->SetScrollRange(-1, 127);
	programChange(p_program[p_channel]);
	
	p_minVol = GetPrivateProfileInt(Version, "minvol", 90, Profile);
	p_maxVol = GetPrivateProfileInt(Version, "maxvol", 120, Profile);
	minvolSB()->SetScrollRange(0, 127);
	minVolChange(p_minVol, FALSE);
	
	maxvolSB()->SetScrollRange(0, 127);
	maxVolChange(p_maxVol, FALSE);
	
	p_minLength = GetPrivateProfileInt(Version, "minlength", 100, Profile);
	p_maxLength = GetPrivateProfileInt(Version, "maxlength", 500, Profile);
	minlenSB()->SetScrollRange(1, 3000);
	minlenSB()->SetScrollPos(p_minLength);
	minLengthChange(p_minLength, FALSE);
		
	maxlenSB()->SetScrollRange(1, 3000);
	maxlenSB()->SetScrollPos(p_maxLength);
	maxLengthChange(p_maxLength, FALSE);
	
	p_lowNote = GetPrivateProfileInt(Version, "lownote", DMN_C_3, Profile);
	p_highNote = GetPrivateProfileInt(Version, "highnote", DMN_C_6, Profile);
	lownoteSB()->SetScrollRange(DMN_C_0, DMN_G_10);
	lownoteSB()->SetScrollPos(p_lowNote);
	lowNoteChange(p_lowNote, FALSE);
	
	highnoteSB()->SetScrollRange(DMN_C_0, DMN_G_10);
	highnoteSB()->SetScrollPos(p_highNote);
	highNoteChange(p_highNote, FALSE);
	
	p_restProb = GetPrivateProfileInt(Version, "rest", 10, Profile);
	restSB()->SetScrollRange(0, 100);
	restChange(p_restProb);

	merr = midiOutOpen(&p_hMidi, MIDIMAPPER, (DWORD)(UINT)m_hWnd, 0, CALLBACK_WINDOW);

	if (merr) {
		char mmesg[80];
		midiOutGetErrorText(merr, mmesg, sizeof(mmesg));
		MessageBox(mmesg, "MIDI Mapper",
			MB_OK | MB_ICONSTOP);
		PostQuitMessage(0);
	}

	srand((unsigned)time(NULL));	
	
	for (i = 0; i< 128 ; i++) {
		wsprintf(buf, "note%d", i + 1);
		p_noteProb[i] = GetPrivateProfileInt(Version, buf, 50, Profile);
	}
	
	p_gotProfile = TRUE;
	
	delete buf;

	return TRUE;
}

void
CMyMainDialog::OnPlayStop()
{
	playstop();
}

void
CMyMainDialog::playstop()
{
	if (p_playing) {
		playPB()->SetWindowText("Play");
		p_playing = FALSE;
	} else {
		playPB()->SetWindowText("Stop");
		p_playing = TRUE;
		program();
		nextNote();
	}
}

//
// program
//

void
CMyMainDialog::program()
{

	if (p_program[p_channel] >= 0 && p_program[p_channel] <= 127) {
		dmidiSendChannelMessage(p_hMidi, p_channel,
			DMM_PROGRAM, (BYTE)(p_program[p_channel]), 0);
	}
}

BYTE
CMyMainDialog::getVolume()
{
	int range;
	
	range = (p_maxVol - p_minVol) + 1;
	
	return (p_minVol + (rand() % range));
}

UINT
CMyMainDialog::getLength()
{
	int range;
	
	range = (p_maxLength - p_minLength) + 1;
	
	return (p_minLength + (rand() % range));
}

int
CMyMainDialog::getNote()
{
	int range;
	int rnum;
	int i;
	long sum;
	
	rnum = rand();
	
	if ((WORD)(rnum % 100) < p_restProb) {
		return -1;   // rest
	}
	
	range = p_highNote - p_lowNote;
	
	// sum the probabilities
	
	sum = 0;
	for (i = p_lowNote; i < (int)p_highNote; i++) {
		sum += p_noteProb[i];
	}
	
	if (sum == 0) {
		return -1;	// rest
	}
	
	// Now, find the slot that has that probability
	
	rnum %= sum;
	i = p_lowNote;
	while (rnum > 0) {
		rnum -= p_noteProb[i];
		i++;
	}

	return i;

}

//
// nextNote
//

void
CMyMainDialog::nextNote()
{
	BYTE volume;
	UINT length;
	
	p_note = getNote();
	volume = getVolume();
	length = getLength();
	
	if (p_note >= 0) {
		dmidiSendChannelMessage(p_hMidi, p_channel,
			DMM_NOTE_ON, p_note, volume);
	}
	SetTimer(99, length, NULL);
}

void
CMyMainDialog::OnTimer(UINT)
{
	BYTE sChannel;

	if (p_note >= 0) {
		if (p_lastChannel != 255) {
			sChannel = p_lastChannel;
			p_lastChannel = 255;
		} else {
			sChannel = p_channel;
		}
		dmidiSendChannelMessage(p_hMidi, sChannel,
			DMM_NOTE_OFF, p_note, 0);
	}
	if (p_playing) {
		nextNote();
	}
}

BOOL
CMyMainDialog::shifted()
{
	int ks;
	
	ks = GetKeyState(VK_SHIFT);
	if ((ks & 0x8000) != 0) {
		return TRUE;
	}
	return FALSE;
}

void
CMyMainDialog::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar *pSb)
{
	int minv, maxv, value;
	BOOL gang = shifted();
	
	pSb->GetScrollRange(&minv, &maxv);
	value = pSb->GetScrollPos();
	
	switch (nSBCode) {
	
	case SB_LINELEFT:
		if (value > minv) {
			value--;
		}
		break;
		
	case SB_LINERIGHT:
		if (value < maxv) {
			value++;
		}
		break;
		
	case SB_LEFT:
		value = minv;
		break;
		
	case SB_RIGHT:
		value = maxv;
		break;
		
	case SB_PAGELEFT:
		value -= (maxv - minv) / 10;
		if (value < minv) {
			value = minv;
		}
		break;
		
	case SB_PAGERIGHT:
		value += (maxv - minv) / 10;
		if (value > maxv) {
			value = maxv;
		}
		break;
		
	case SB_THUMBPOSITION:
		value = nPos;
		break;
		
	default:
		return;
	}
	
	// handle the operation
	
	if (pSb == channelSB()) {
		channelChange(value);
		return;
	}
	
	if (pSb == programSB()) {
		programChange(value);
		return;
	}
	
	if (pSb == minvolSB()) {
		minVolChange(value, gang);
		return;
	}
	
	if (pSb == maxvolSB()) {
		maxVolChange(value, gang);
		return;
	}
	
	if (pSb == minlenSB()) {
		minLengthChange(value, gang);
		return;
	}
		
	if (pSb == maxlenSB()) {
		maxLengthChange(value, gang);
		return;
	}
	
	if (pSb == lownoteSB()) {
		lowNoteChange(value, gang);
		return;
	}
	
	if (pSb == highnoteSB()) {
		highNoteChange(value, gang);
		return;
	}
	
	if (pSb == restSB()) {
		restChange(value);
		return;
	}
}

void
CMyMainDialog::channelChange(int val)
{
	char *buf = new char[30];
	
	if (val >= 0) {
		if (p_lastChannel == 255) {
			p_lastChannel = p_channel;
		}
		p_channel = val;
	}

	wsprintf(buf, "%d", p_channel + 1);
	channelST()->SetWindowText(buf);
	
	channelSB()->SetScrollPos(p_channel);
	
	programChange(p_program[p_channel]);

	delete buf;
}

void
CMyMainDialog::programChange(int val)
{
	char *buf = new char[30];
	
	p_program[p_channel] = val;

	if (p_program[p_channel] > -1) {
		wsprintf(buf, "%d", p_program[p_channel]);
		programST()->SetWindowText(buf);
	} else {
		programST()->SetWindowText("none");
	}
	
	programSB()->SetScrollPos(p_program[p_channel]);
	
	if (p_playing) {
		program();
	}
	
	delete buf;
}

void
CMyMainDialog::restChange(int val)
{
	char *buf = new char[15];
	
	p_restProb = val;
	
	wsprintf(buf, "%d", p_restProb);
	restST()->SetWindowText(buf);
	
	restSB()->SetScrollPos(p_restProb);
	
	delete buf;
}

void
CMyMainDialog::minVolChange(int val, BOOL gang)
{
	char *buf = new char[15];
	int delta = 0;
	
	if (val < 0) {
		val = 0;
	}
	
	if (gang) {
		delta = val - p_minVol;
	}
	
	p_minVol = val;
	if (!gang && p_minVol > p_maxVol) {  // if gang, maxVol will change, too
		p_minVol = p_maxVol;
	}
	
	wsprintf(buf, "%d", p_minVol);
	minvolST()->SetWindowText(buf);
	
	minvolSB()->SetScrollPos(p_minVol);
	
	if (delta) {
		maxVolChange(p_maxVol + delta, FALSE);
	}
	
	delete buf;
}

void
CMyMainDialog::maxVolChange(int val, BOOL gang)
{
	char *buf = new char[15];
	int delta = 0;
	
	if (val > 127) {
		val = 127;
	}
	
	if (gang) {
		delta = val - p_maxVol;
	}
	
	p_maxVol = val;
	
	if (!gang && p_maxVol < p_minVol) {
		p_maxVol = p_minVol;
	}
	
	wsprintf(buf, "%d", p_maxVol);
	maxvolST()->SetWindowText(buf);
	
	maxvolSB()->SetScrollPos(p_maxVol);
	
	if (delta) {
		minVolChange(p_minVol + delta, FALSE);
	}
	
	delete buf;
}

void
CMyMainDialog::minLengthChange(int val, BOOL gang)
{
	char *buf = new char[15];
	int delta = 0;
	
	if (val < 0) {
		val = 0;
	}
	
	if (gang) {
		delta = val - p_minLength;
	}
	
	p_minLength = val;
	if (!gang && p_minLength > p_maxLength) {
		p_minLength = p_maxLength;
	}
	
	wsprintf(buf, "%d", p_minLength);
	minlenST()->SetWindowText(buf);
	
	minlenSB()->SetScrollPos(p_minLength);
	
	if (delta) {
		maxLengthChange(p_maxLength + delta, FALSE);
	}
	
	delete buf;
}

void
CMyMainDialog::maxLengthChange(int val, BOOL gang)
{
	char *buf = new char[15];
	int delta = 0;
	
	if (val > 3000) {
		val = 3000;
	}
	
	if (gang) {
		delta = val - p_maxLength;
	}
	
	p_maxLength = val;
	
	if (!gang && p_maxLength < p_minLength) {
		p_maxLength = p_minLength;
	}
	
	wsprintf(buf, "%d", p_maxLength);
	maxlenST()->SetWindowText(buf);
	
	maxlenSB()->SetScrollPos(p_maxLength);
	
	if (delta) {
		minLengthChange(p_minLength + delta, FALSE);
	}
	
	delete buf;
}

//
// Convert note number to note name
//

static void
shownote(CStatic *st, int val)
{
	int octave;
	int nwi;
	char *nbuf = new char[3];
	char *obuf = new char[3];
	char *sbuf = new char[10];
	
	nbuf[1] = nbuf[2] = '\0';

	octave = val / 12;
	nwi = val % 12;
	
	switch (nwi) {
	case 1:
		nbuf[1] = '#';
	case 0:
		nbuf[0] = 'C';
		break;
		
	case 3:
		nbuf[1] = '#';
	case 2:
		nbuf[0] = 'D';
		break;
		
	case 4:
		nbuf[0] = 'E';
		break;
		
	case 6:
		nbuf[1] = '#';
	case 5:
		nbuf[0] = 'F';
		break;
		
	case 8:
		nbuf[1] = '#';
	case 7:
		nbuf[0] = 'G';
		break;
		
	case 10:
		nbuf[1] = '#';
	case 9:
		nbuf[0] = 'A';
		break;
		
	case 11:
		nbuf[0] = 'B';
		break;
	}
	
	// What a mess.  See if you can do better.  Then you will
	// know why I did it this way.
	
	wsprintf(obuf, "%d", octave);
	strcpy(sbuf, nbuf);
	strcat(sbuf, obuf);
	
	st->SetWindowText(sbuf);
	
	delete nbuf;
	delete obuf;
	delete sbuf;
}

void
CMyMainDialog::lowNoteChange(int val, BOOL gang)
{
	int delta = 0;
	
	if (val < DMN_C_0) {
		val = DMN_C_0;
	}
	
	if (gang) {
		delta = val - p_lowNote;
	}
	
	p_lowNote = val;
	if (!gang && p_lowNote > p_highNote) {
		p_lowNote = p_highNote;
	}
	
	shownote(lownoteST(), p_lowNote);
	
	lownoteSB()->SetScrollPos(p_lowNote);
	
	if (delta) {
		highNoteChange(p_highNote + delta, FALSE);
	}
}

void
CMyMainDialog::highNoteChange(int val, BOOL gang)
{
	int delta = 0;
	
	if (val > DMN_G_10) {
		val = DMN_G_10;
	}
	
	if (gang) {
		delta = val - p_highNote;
	}
	
	p_highNote = val;
	
	if (!gang && p_highNote < p_lowNote) {
		p_highNote = p_lowNote;
	}
	
	shownote(highnoteST(), p_highNote);

	highnoteSB()->SetScrollPos(p_highNote);
	
	if (delta) {
		lowNoteChange(p_lowNote + delta, FALSE);
	}
}

// Randomize

void
CMyMainDialog::OnRandomize()
{
	int i;
	int low, high;
	
	if (shifted()) {
		low = 0;
		high = 127;
	} else {
		low = p_lowNote;
		high = p_highNote;
	}
		
	for (i = low; i <= high; i++) {
		p_noteProb[i] = rand() % 100;
	}
}

// Randomly shift probabilities from -5 to 5

void
CMyMainDialog::OnWiggle()
{
	int i;
	int low, high;
	
	if (shifted()) {
		low = 0;
		high = 127;
	} else {
		low = p_lowNote;
		high = p_highNote;
	}

	for (i = low; i <= high; i++) {
		p_noteProb[i] += (rand() % 10) - 5;
		if (p_noteProb[i] < 0) {
			p_noteProb[i] = 0;
		}
		if (p_noteProb[i] > 100) {
			p_noteProb[i] = 100;
		}
	}
}

// Major Scale based on lowest note

static int major[12] = { 50, 0, 50, 0, 50, 50, 0, 50, 0, 50, 0, 50 };

void
CMyMainDialog::OnMajor()
{
	int i;
	int base;
	int nwo;	// note within octave
	int low, high;
	
	if (shifted()) {
		low = 0;
		high = 127;
	} else {
		low = p_lowNote;
		high = p_highNote;
	}
	
	base = low % 12;
	nwo = base;
	for (i = low; i <= high; i++) {
		p_noteProb[i] = major[nwo];
		nwo = (nwo + 1) % 12;
	}
}

// Minor Scale based on lowest note

static int minor[12] = { 50, 0, 50, 50, 0, 50, 0, 50, 0, 50, 0, 50 };

void
CMyMainDialog::OnMinor()
{
	int i;
	int base;
	int nwo;	// note within octave
	int low, high;
	
	if (shifted()) {
		low = 0;
		high = 127;
	} else {
		low = p_lowNote;
		high = p_highNote;
	}
	
	base = low % 12;
	nwo = base;
	for (i = low; i <= high; i++) {
		p_noteProb[i] = minor[nwo];
		nwo = (nwo + 1) % 12;
	}
}

// Fourths based on lowest note

static int fourths[12] = { 50, 0, 0, 0, 0, 0, 50, 0, 0, 0, 0, 0 };

void
CMyMainDialog::OnFourths()
{
	int i;
	int base;
	int nwo;	// note within octave
	int low, high;
	
	if (shifted()) {
		low = 0;
		high = 127;
	} else {
		low = p_lowNote;
		high = p_highNote;
	}
	
	base = low % 12;
	nwo = base;
	for (i = low; i <= high; i++) {
		p_noteProb[i] = fourths[nwo];
		nwo = (nwo + 1) % 12;
	}
}

// Fifths based on lowest note

static int fifths[12] = { 50, 0, 0, 0, 0, 0, 0, 50, 0, 0, 0, 0 };

void
CMyMainDialog::OnFifths()
{
	int i;
	int base;
	int nwo;	// note within octave
	int low, high;
	
	if (shifted()) {
		low = 0;
		high = 127;
	} else {
		low = p_lowNote;
		high = p_highNote;
	}
	
	base = low % 12;
	nwo = base;
	for (i = low; i <= high; i++) {
		p_noteProb[i] = fifths[nwo];
		nwo = (nwo + 1) % 12;
	}
}

// Reset to 50

void
CMyMainDialog::OnReset()
{
	int i;
	int low, high;
	
	if (shifted()) {
		low = 0;
		high = 127;
	} else {
		low = p_lowNote;
		high = p_highNote;
	}
	
	for (i = low; i <= high; i++) {
		p_noteProb[i] = 50;
	}
}

// Increase all nonzero by 5

void
CMyMainDialog::OnIncrease()
{
	int i;
	int low, high;
	
	if (shifted()) {
		low = 0;
		high = 127;
	} else {
		low = p_lowNote;
		high = p_highNote;
	}
	
	for (i = low; i <= high; i++) {
		if (p_noteProb[i] > 0) {
			p_noteProb[i] += 5;
			if (p_noteProb[i] > 100) {
				p_noteProb[i] = 100;
			}
		}
	}
}

// Decrease all nonzero by 5

void
CMyMainDialog::OnDecrease()
{
	int i;
	int low, high;
	
	if (shifted()) {
		low = 0;
		high = 127;
	} else {
		low = p_lowNote;
		high = p_highNote;
	}
	
	for (i = low; i <= high; i++) {
		if (p_noteProb[i] > 0) {
			p_noteProb[i] -= 5;
			if (p_noteProb[i] < 0) {
				p_noteProb[i] = 0;
			}
		}
	}
}

// Zero probabilities

void
CMyMainDialog::OnZero()
{
	int i;
	int low, high;
	
	if (shifted()) {
		low = 0;
		high = 127;
	} else {
		low = p_lowNote;
		high = p_highNote;
	}
	
	for (i = low; i <= high; i++) {
		p_noteProb[i] = 0;
	}
}

// Invert all probabilities

void
CMyMainDialog::OnInvert()
{
	int i;
	int low, high;
	
	if (shifted()) {
		low = 0;
		high = 127;
	} else {
		low = p_lowNote;
		high = p_highNote;
	}
	
	for (i = low; i <= high; i++) {
		p_noteProb[i] = 100 - p_noteProb[i];
	}
}

// Swap all probabilities

void
CMyMainDialog::OnSwap()
{
	WORD save;
	int low, high;
	
	if (shifted()) {
		low = 0;
		high = 127;
	} else {
		low = p_lowNote;
		high = p_highNote;
	}
	
	while (low < high) {	// < is correct here, not <=
		save = p_noteProb[low];
		p_noteProb[low] = p_noteProb[high];
		p_noteProb[high] = save;
		low++;
		high--;
	}
}

BEGIN_MESSAGE_MAP(CMyMainDialog, CDialog)
	ON_WM_DESTROY()
	ON_COMMAND(PB_RANDOMIZE, OnRandomize)
	ON_COMMAND(PB_PLAYSTOP, OnPlayStop)
	ON_COMMAND(PB_WIGGLE, OnWiggle)
	ON_COMMAND(PB_MAJOR, OnMajor)
	ON_COMMAND(PB_MINOR, OnMinor)
	ON_COMMAND(PB_FOURTHS, OnFourths)
	ON_COMMAND(PB_FIFTHS, OnFifths)
	ON_COMMAND(PB_RESET, OnReset)
	ON_COMMAND(PB_INCREASE, OnIncrease)
	ON_COMMAND(PB_DECREASE, OnDecrease)
	ON_COMMAND(PB_ZERO, OnZero)
	ON_COMMAND(PB_INVERT, OnInvert)
	ON_COMMAND(PB_SWAP, OnSwap)
	ON_WM_TIMER()
	ON_WM_HSCROLL()
END_MESSAGE_MAP()
