
//2012/2/18
#define STRICT
#include <windows.h>
#include <windowsx.h>
#include <string.h> //memset, strcat, strcpy
#include <stdio.h> //sprintf
#include <math.h> //atof
#include <stdlib.h> //atof
#include "resource.h"
#include<mmsystem.h>

#define MAX_NOTE 512
#define MIDIMSG(status,channel,data1,data2) ( (DWORD)((status<<4) | channel | (data1<<8) | (data2<<16)) )

class Note{
private://メンバ変数はprivateにして、カプセル化
	int StartX[MAX_NOTE];//
	int StartY[MAX_NOTE];//初期座標
	int EndX[MAX_NOTE];
	int EndY[MAX_NOTE];//終了座標
	int ExistFlag[MAX_NOTE];//存在フラグ
	int note[MAX_NOTE];//ノート番号
	int program[MAX_NOTE];//プログラム番号
	int length[MAX_NOTE];//長さ
public:
	//カプセル化
	int StartXRtn(int i)
	{
		return StartX[i];//初期座標Xを返すメソッド
	}
	int StartYRtn(int i)
	{
		return StartY[i];//初期座標Yを返すメソッド
	}
	int EndXRtn(int i)
	{
		return EndX[i];//終了座標Xを返すメソッド
	}
	int EndYRtn(int i)
	{
		return EndY[i];//終了座標Yを返すメソッド
	}
	int ExistFlagRtn(int i)
	{
		return ExistFlag[i];//存在フラグを返すメソッド
	}
	int NoteRtn(int i)
	{
		return note[i];//ノート番号を返すメソッド
	}
	Note()
	{
		//コンストラクタ(クラス生成時（インスタンス化した時）に呼び出される戻り値のない特殊なメソッド
		int i;
		for(i=0;i<MAX_NOTE;i++)
		{
			ExistFlag[i]=0;//0にセットしないと、何故か、描画されない
		}
	}
	void Search(int x,int y,int ex,int ey,int n)
	{
		//メンバ関数（Searchメソッド)
		int i;

		for(i=0;i<MAX_NOTE;i++)
		{
			//線形探索でExistFlag[i]が0の配列を探す
			if(ExistFlag[i]==0)
			{
				ExistFlag[i]=1;
				StartX[i]=x;
				StartY[i]=y;
				EndX[i]=ex;
				EndY[i]=ey;
				note[i]=n;
				break;
			}
		}
	}
	void PlayNoteOfMidi()
	{
	}
};

class Mouse{
public:
	int ClickX;//クリックした座標X
	int ClickY;//クリックした座標Y
	int MoveX;//動いている座標X
	int MoveY;//動いている座標Y
	int UpX;//クリックUpした座標X
	int UpY;//クリックUpした座標Y
	int DragStartFlag;//ドラッグスタートフラグ
	int MoveFlag;//動いているかのフラグ
	int ClickFlag;//クリックフラグ
	int StartX;//クリック初期の座標X
	int StartY;//クリック初期の座標Y
	int EndX;//クリック終了の座標X
	int EndY;//クリック終了の座標Y
	POINTS clc;//POINTS型のclc

	POINT ClickOfPic2(HWND hWnd)
	{
		POINT pt;
		RECT rc;
		POINT xy;
		GetCursorPos(&pt); //マウスポインタの位置取得
		ScreenToClient(hWnd,&pt); //スクリーン座標→クライアント座標
		xy.x = pt.x;
		xy.y = pt.y;
		GetWindowRect(hWnd,&rc); //コントロールの位置取得
		pt.x = rc.left;
		pt.y = rc.top;
		ScreenToClient(hWnd,&pt); //スクリーン座標→クライアント座標
		xy.x-=pt.x;
		xy.y-=pt.y;
		return xy;
	}
};

Mouse M;//クラスのインスタンス化
Note N;//クラスのインスタンス化

HINSTANCE hInst;
LRESULT CALLBACK MyDlgProc(HWND, UINT, WPARAM, LPARAM);

int WINAPI WinMain(HINSTANCE hCurInst, HINSTANCE hPrevInst,
                   LPSTR lpsCmdLine, int nCmdShow)
{
	hInst=hCurInst;
    DialogBox(hCurInst, TEXT("MYDLG"), NULL, (DLGPROC)MyDlgProc);//ダイアログボックス"MYDLG"を呼び出す
    return 0;
}


LRESULT CALLBACK MyDlgProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp)
{
	static HMIDIOUT hMidi;
    static HBITMAP hBmp,hBmp2;//HBITMAP型のhBmp,hBmp2を宣言
	static BITMAP bmp_info,bmp_info2;
	static int w,h,w2,h2;//画像の高さ、幅を宣言
	static HDC hdc,hdc_mem1,hdc_mem2;//デバイスコンテキストハンドルhdc,hdc_mem1,hdc_mem2を宣言
	static PAINTSTRUCT ps;
	static HWND hPic,hPic2,hScroll1,vScroll1;//ウィンドウハンドルの宣言
	static BYTE note=0x3C,velocity=0x40;//note番号、べろシティを宣言
    static BYTE program=0;//プログラム番号は0(ピアノ）
	static nPos=371,vPos=0;//スクロール変数nPosと、vPos
	int i;//iを宣言
	HPEN hPen1,hPen2;//HPEN型宣言
	static int length=0;//♪の長さ
	
    int tbl3[]={0,2,  4 ,5, 7,  9,11,12,14,16,17,19,21,23,24,26,28,29,31,33,35,
							36,38,40, 41,43, 45, 47, 48,  50,52, 53, 55,  57, 59,
							60,62,64,65,67,69,71,72,74,76,77,79,81,83,84,86,88,89,91,93,95,
			            //  c3 d3 e3 f3 g3 a3 b3 c4 d4 e4 f4 g4 a4 b4 c5 d5 e5 f5 g5 a5 b5
							96,98,100,101,103,105,107,108,110,112,113,115,117,119,
						//	c6 d6 e6  f6  g6  a6  b6   c7  d7  e7 f7   g7  a7  b7
							120,122,124,125,127};//＃♭なしの音
				int tbl4[]={1,3,-1,6,8,10,-1,13,15,-1,18,20,22,-1,25,27,-1,30,32,34,-1,
							37,39,42,44,46,-1,49,51,-1,54,56,58,-1,
							61,63,-1,66,68,70,-1,73,75,-1,78,80,82,-1,85,87,-1,90,92,94,-1,
							97,99,-1,102,104,106,-1,109,111,-1,114,116,118,-1,
							121,123,-1,126};//＃の音
	
	switch (msg) {
		case WM_HSCROLL://横スクロールバーのイベント
			if(lp!=(LPARAM)hScroll1)
				return FALSE;
			switch(LOWORD(wp))
			{
				case SB_LINELEFT:
					nPos--;
					if(nPos<0) nPos=0;
					break;
				case SB_LINERIGHT:
					nPos++;
					if(nPos>743)
					{
						nPos=743;
					}
					break;
				case SB_PAGELEFT:
					nPos-=16;
					if(nPos<0) nPos=0;
					break;
				case SB_PAGERIGHT:
					nPos+=16;
					if(nPos>743) nPos=743;
					break;
				case SB_THUMBTRACK:
					nPos=HIWORD(wp);
					break;
				case SB_THUMBPOSITION:
					nPos=HIWORD(wp);
					break;
				}
			ScrollBar_SetPos(hScroll1,nPos,TRUE);//スクロール値をセットする
			InvalidateRect(hPic,NULL,TRUE);
			InvalidateRect(hPic2,NULL,TRUE);
			return TRUE;
		case WM_VSCROLL://縦スクロールバーのイベント
			if(lp!=(LPARAM)vScroll1)
				return FALSE;
			switch(LOWORD(wp))
			{
				case SB_LINELEFT:
					vPos--;
					if(vPos<0) vPos=0;
					break;
				case SB_LINERIGHT:
					vPos++;
					if(vPos>743)
					{
						vPos=743;
					}
					break;
				case SB_PAGELEFT:
					vPos-=16;
					if(vPos<0) vPos=0;
					break;
				case SB_PAGERIGHT:
					vPos+=16;
					if(vPos>743) vPos=743;
					break;
				case SB_THUMBTRACK:
					vPos=HIWORD(wp);
					break;
				case SB_THUMBPOSITION:
					vPos=HIWORD(wp);
					break;
				}
			ScrollBar_SetPos(vScroll1,vPos,TRUE);//スクロール値をセットする
			InvalidateRect(hPic2,NULL,TRUE);
			return TRUE;
        case WM_INITDIALOG:
			
			//ＩＤからウィンドウハンドルを得る
			hPic=GetDlgItem(hWnd,IDC_STATIC01);
			hPic2=GetDlgItem(hWnd,IDC_STATIC02);
			hScroll1=GetDlgItem(hWnd,IDC_SCROLLBAR1);
			vScroll1=GetDlgItem(hWnd,IDC_SCROLLBAR2);
			
			//リソースＢＭＰをロード
            hBmp=LoadBitmap(hInst,TEXT("MYBMP"));
			hBmp2=LoadBitmap(hInst,TEXT("MYBMP2"));
			
			//タイマーをセット
			SetTimer(hWnd,100,100,NULL);
			
			//デバイスコンテキストを得る
			hdc=GetDC(hWnd);
			hdc_mem1=CreateCompatibleDC(hdc);//互換性のあるBMPを作成
			SelectObject(hdc_mem1,hBmp);//オブジェクトを選択
			DeleteObject(hBmp);//削除
			hdc_mem2=CreateCompatibleDC(hdc);//互換性のあるBMPを作成
			SelectObject(hdc_mem2,hBmp2);//オブジェクトを選択
			DeleteObject(hBmp2);//削除
			ReleaseDC(hWnd,hdc);//デバイスコンテキストをリリース

			midiOutOpen(&hMidi , MIDIMAPPER , 0 , 0 , 0);//MIDIMAPPERをオープン
			midiOutShortMsg(hMidi,MIDIMSG(0xC,0,program,0));//最初の初期化

			ScrollBar_SetRange(hScroll1,0,743,TRUE);//範囲を選択
			ScrollBar_SetPos(hScroll1,nPos,TRUE);//位置を選択
			ScrollBar_SetRange(vScroll1,0,16*4*80,TRUE);
			ScrollBar_SetPos(vScroll1,vPos,TRUE);


            return TRUE;
		case WM_DESTROY://ウィンドウが破棄されたら
			DeleteDC(hdc_mem1);
			DeleteDC(hdc_mem2);
			midiOutReset(hMidi);
            midiOutClose(hMidi);
			
			PostQuitMessage(0);
			break;

		case WM_CLOSE:
			DeleteDC(hdc_mem1);
			DeleteDC(hdc_mem2);

			midiOutReset(hMidi);
			midiOutClose(hMidi);

			DestroyWindow(hWnd);
			break;
		case WM_TIMER://SetTimerでタイマーがセットされる
			InvalidateRect(hWnd,NULL,TRUE);//WM_PAINTを呼び出す(無効領域を作り出す)
			break;
		case WM_RBUTTONUP://デバッグ用イベント
			{
			POINT xy;
			xy=M.ClickOfPic2(hPic2);
			
			char str[100];
			sprintf(str,"x=%d y=%d",xy.x,xy.y);
			MessageBox(hWnd,"OK",str,MB_OK);
			}
				break;
		case WM_LBUTTONDOWN:    //鍵盤を押す
			{
			POINT xy;
			xy=M.ClickOfPic2(hPic2);
				
			M.clc=MAKEPOINTS(lp);
			M.clc.x=((M.clc.x+5)/16)*16;
			note=tbl3[(M.clc.x+nPos)/16-2];
			if(M.clc.y<64)
			{
				note=tbl4[(M.clc.x+nPos-8)/16-1];
			}
			if(note!=-1)//-1じゃなければ音を出す
				midiOutShortMsg(hMidi,MIDIMSG(0x9,0x0,note,velocity));
            if(M.clc.y>128)//clcが128以上なら
			{	M.ClickFlag=1;//クリックしているフラグをON
				M.StartX=xy.x/16*16;
				M.StartY=xy.y/16*16;
			}
			InvalidateRect(hPic2,NULL,TRUE);
			}return 0;
        case WM_LBUTTONUP:    //鍵盤を離す
			{
			POINT xy;
			xy=M.ClickOfPic2(hPic2);

            midiOutShortMsg(hMidi,MIDIMSG(0x9,0x0,note,0));
            M.ClickFlag=0;//クリックしているフラグをOFF
			M.EndX=xy.x;
			M.EndY=xy.y;
			InvalidateRect(hPic2,NULL,TRUE);
			M.DragStartFlag=0;//
			M.MoveFlag=0;
			N.Search(M.StartX+nPos,M.StartY+vPos,M.StartX+16+nPos,M.EndY+vPos,note);
			}
			return 0;
		
		case WM_MOUSEMOVE:
			{
			POINT xy;
			xy=M.ClickOfPic2(hPic2);
			
			M.MoveY=xy.y/16*16;//16で割って、16をかける
			M.MoveFlag=1;//MoveFlagをON
				
				M.clc=MAKEPOINTS(lp);///clcにマウスカーソルの座標が入る
				M.clc.x=((M.clc.x+5)/16)*16;//
				note=tbl3[((M.clc.x+nPos)/16)-2];
				if(M.clc.y<64)
				{
					note=tbl4[(M.clc.x+nPos-8)/16];
				}
				if(note!=-1)
					if(M.ClickFlag==1 && M.clc.y<128)
						midiOutShortMsg(hMidi,MIDIMSG(0x9,0x0,note,velocity));
				InvalidateRect(hPic2,NULL,TRUE);
			}
			return 0;
		case WM_PAINT:
			{
				HPEN hPen,hOldPen;
				HBRUSH hBrush,hOldBrush;
				HBRUSH hBrush2,hOldBrush2;
				
				int tbl[]={2,2,1,2,2,2,1};
			int X0=nPos/16,X1=(nPos+440)/16;
			hdc=BeginPaint(hPic,&ps);
				GetObject(hBmp,(int)sizeof(BITMAP),&bmp_info);
				w=bmp_info.bmWidth;
				h=bmp_info.bmHeight;
				for(i=X0;i<X1;i++)
					BitBlt(hdc,i*16-nPos,0,w,h,hdc_mem1,0,0,SRCCOPY);
				
				GetObject(hBmp2,(int)sizeof(BITMAP),&bmp_info2);
				w=bmp_info2.bmWidth;
				h=bmp_info2.bmHeight;
				for(i=X0;i<X1;i++)
					BitBlt(hdc,i*16+tbl[i%7]*16-8-nPos,0,w,h,hdc_mem2,0,0,SRCCOPY);
				
				
				
			EndPaint(hPic,&ps);
			

			hdc=BeginPaint(hPic2,&ps);
				hBrush=CreateSolidBrush(RGB(245,245,245));
				hOldBrush=(HBRUSH)SelectObject(hdc,hBrush);
				Rectangle(hdc,0,0,440,120);
				hPen1=CreatePen(PS_SOLID,1,RGB(0,0,0));
				SelectObject(hdc,hPen1);
				for(i=X0;i<X1;i++)
				{
					MoveToEx(hdc,i*16-nPos,0,NULL);
					LineTo(hdc,i*16-nPos,120);
				}
				hBrush2=CreateSolidBrush(RGB(0,120,255));
				hOldBrush2=(HBRUSH)SelectObject(hdc,hBrush2);
				if(M.ClickFlag==1 && M.MoveFlag==1)
					Rectangle(hdc,M.StartX,M.StartY,M.StartX+16,M.MoveY);
				else{
					if(M.StartY-vPos>0)
						Rectangle(hdc,M.StartX-nPos+371,M.StartY-vPos,M.StartX+16-nPos+371,M.EndY-vPos);
					
				}

				for(i=0;i<MAX_NOTE;i++)
				{
					if(N.ExistFlagRtn(i)==1)
					{
						if(N.StartYRtn(i)-vPos>0)
						Rectangle(hdc,N.StartXRtn(i)-nPos+371,N.StartYRtn(i)-vPos,
							N.EndXRtn(i)-nPos+371,N.EndYRtn(i)-vPos);
					}
				}
				DeleteObject(hPen1);
				DeleteObject(hBrush);
				DeleteObject(hBrush2);
			EndPaint(hPic2,&ps);
			}
			return TRUE;
        case WM_COMMAND:
            switch (LOWORD(wp)) {
                case IDOK:
                    EndDialog(hWnd, 0);
                    return TRUE;
                case IDCANCEL:
                    
                    return TRUE;
            }
            break;
		
    }
    return FALSE;
}

