
#include <WinSock.h>
#include <stdio.h>
#include <string.h>
#include <memory.h>
#include <process.h>
#include <windows.h>
#include <mmsystem.h>

#pragma comment(lib, "winmm.lib") 
#pragma comment(lib,"Ws2_32.lib")

#define WM_SOCKET     (WM_USER+1)                       //ソケット用メッセージ
#define PORT          5555                             //通信ポート番号
#define MAX_MESSAGE   30000                              //メッセージサイズ

#define IDE_HOSTNAME  1000                              //ホスト名入力用
#define IDB_CONNECT   1001                              //[接続]ボタン
#define IDB_ACCEPT    1002                              //[接続待ち]ボタン
#define IDB_REJECT    1003                              //[切断]
#define IDE_SENDMSG   1004                              //送信文入力用
#define IDE_RECVMSG   1005                              //受信文表示用
#define IDB_SEND      1006                              //[送信]ボタン
#define SRATE         11025


HINSTANCE hInstance;                                    //インスタンスハンドル
LPSTR lpszClassName="VoiceChatTest";                             //ウィンドウクラス名
SOCKET sock    = INVALID_SOCKET;                        //ソケット
SOCKET sv_sock = INVALID_SOCKET;                        //サーバ用ソケット
HOSTENT *phe;                                           //HOSTENT構造体

static SOCKADDR_IN cl_sin;
SOCKADDR client;
BOOL ConnectionFlag=FALSE;

LRESULT CALLBACK WindowProc(HWND,UINT,WPARAM,LPARAM);   //ウィンドウ関数
BOOL SockInit(HWND hWnd);                               //ソケット初期化
SOCKET SockAccept(HWND hWnd);                             //ソケット接続待ち
BOOL SockConnect(HWND hWnd, LPCSTR host);               //ソケット接続



int WINAPI WinMain(HINSTANCE hThisInst, HINSTANCE hPrevInst,
    LPSTR lpszArgs, int nWinMode)
{
    HWND hWnd;                                          //ウィンドウハンドル
    MSG  msg;                                           //メッセージ
    WNDCLASSEX wc;                                      //ウィンドウクラス

    hInstance = hThisInst;                              //グローバル化

    //ウィンドウクラス定義
    wc.hInstance     = hInstance;                       //インスタンス
    wc.lpszClassName = lpszClassName;                   //クラス名
    wc.lpfnWndProc   = WindowProc;                      //ウィンドウ関数名
    wc.style         = 0;                               //クラススタイル
    wc.cbSize        = sizeof(WNDCLASSEX);              //構造体サイズ
    wc.hIcon         = LoadIcon(NULL, IDI_APPLICATION); //アイコンハンドル
    wc.hIconSm       = LoadIcon(NULL, IDI_WINLOGO);     //スモールアイコン
    wc.hCursor       = LoadCursor(NULL, IDC_ARROW);     //マウスポインタ
    wc.lpszMenuName  = NULL;                            //メニュー(なし)
    wc.cbClsExtra    = 0;                               //クラス拡張情報
    wc.cbWndExtra    = 0;                               //ウィンドウ拡張情報
    wc.hbrBackground = (HBRUSH) COLOR_WINDOW;           //ウィンドウの背景色
    if(!RegisterClassEx(&wc)) return 0;                 //ウィンドウクラス登録

    //ウィンドウ生成
    hWnd = CreateWindow(
        lpszClassName,                                  //ウィンドウクラス名
        "Voice Chat ver0.1",                          //ウィンドウ名
        WS_DLGFRAME|WS_VISIBLE|WS_SYSMENU,              //ウィンドウ属性
        CW_USEDEFAULT,                                  //ウィンドウ表示位置(X)
        CW_USEDEFAULT,                                  //ウィンドウ表示位置(Y)
        382,                                            //ウィンドウサイズ(X)
        170,                                            //ウィンドウサイズ(Y)
        HWND_DESKTOP,                                   //親ウィンドウハンドル
        NULL,                                           //
        hInstance,                                      //インスタンスハンドル
        NULL                                            //
    );

    //ウィンドウ表示
    ShowWindow(hWnd, nWinMode);                         //ウィンドウ表示モード
    UpdateWindow(hWnd);                                 //ウインドウ更新

    //メッセージループ
    while(GetMessage(&msg, NULL, 0, 0)){                //メッセージを取得
        TranslateMessage(&msg);                         //
        DispatchMessage(&msg);                          //メッセージ送る
    }
    return msg.wParam;                                  //プログラム終了
}


LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wP, LPARAM lP)
{
    static HWND hWndHost, hWndConnect, hWndAccept;
    static HWND hWndReject, hWndSendMsg, hWndRecvMsg, hWndSend;

	static WAVEFORMATEX wfe;
	static HWAVEOUT hWaveOut;
	static HWAVEIN hWaveIn;
	static BYTE *bWave1 , *bWave2 , *bSave , *bTmp;
	static WAVEHDR whdr1 , whdr2,whdr3;
	static int dwLength = 0 , dwCount;
	static BOOL blReset = FALSE;
	static HWND hWndRec,hWndRecEnd,hWndPlay,hWndPlayEnd;

	WSADATA wsa;
	
	switch(uMsg){
    case WM_CREATE:
        //文字列表示
        CreateWindow("static", "接続先", WS_CHILD|WS_VISIBLE,
            10,10, 100, 18, hWnd, NULL, hInstance, NULL);
        //ホスト名入力用エディットボックス
        hWndHost    = CreateWindowEx(WS_EX_CLIENTEDGE, "edit","",
            WS_CHILD|WS_VISIBLE, 10,30, 200, 25,
            hWnd, (HMENU)IDE_HOSTNAME, hInstance, NULL);
        //[接続]ボタン
        hWndConnect = CreateWindow("button", "接続",
            WS_CHILD|WS_VISIBLE, 220,30,50,25,
            hWnd, (HMENU)IDB_CONNECT, hInstance, NULL);
        //[接続待ち]ボタン
        hWndAccept  = CreateWindow("button", "接続待ち",
            WS_CHILD|WS_VISIBLE, 275,30,90,25,
            hWnd, (HMENU)IDB_ACCEPT, hInstance, NULL);
        //[送信]ボタン
        hWndSend    = CreateWindow("button", "送信",
            WS_CHILD|WS_VISIBLE|WS_DISABLED, 10,80,50,25,
            hWnd, (HMENU)IDB_SEND, hInstance, NULL);
        //[切断]ボタン
        hWndReject  = CreateWindow("button", "切断",
            WS_CHILD|WS_VISIBLE|WS_DISABLED, 70,80,50,25,
            hWnd, (HMENU)IDB_REJECT, hInstance, NULL);
 
 		
		wfe.wFormatTag = WAVE_FORMAT_PCM;
		wfe.nChannels = 1;
		wfe.nSamplesPerSec = SRATE;
		wfe.nAvgBytesPerSec = SRATE;
		wfe.wBitsPerSample = 8;
		wfe.nBlockAlign = 1;

		wfe.cbSize = 0;

		
		
		SetFocus(hWndHost);                             //フォーカス指定
       SockInit(hWnd);
		   return 0L;
	case MM_WIM_OPEN:
		dwLength = 0;
		bSave =(BYTE *) realloc(bSave , 1);
		waveInAddBuffer(hWaveIn , &whdr1 , sizeof(WAVEHDR));
		waveInAddBuffer(hWaveIn , &whdr2 , sizeof(WAVEHDR));

		waveInStart(hWaveIn);
		return 0;

	case MM_WIM_DATA:
		{
			char *buf=(char *)malloc(sizeof(char)*3000000000000000000);

/*
			if(sendto(sock,(char*)((PWAVEHDR)lP)->lpData,((PWAVEHDR)lP)->dwBytesRecorded, 0,(SOCKADDR *)&cl_sin,(int)sizeof(cl_sin))==SOCKET_ERROR){
                  MessageBox(hWnd, "sending failed", "Error",
                       MB_OK|MB_ICONEXCLAMATION);
            }
*/
	

			whdr3.lpData =(LPSTR) ((PWAVEHDR)lP)->lpData;
			whdr3.dwBufferLength = ((PWAVEHDR)lP)->dwBytesRecorded;
			whdr3.dwBytesRecorded = 0;
			whdr3.dwFlags = WHDR_BEGINLOOP | WHDR_ENDLOOP;
			whdr3.dwLoops = 1;
			whdr3.lpNext = NULL;
			whdr3.dwUser = 0;
			whdr3.reserved = 0;


		
			waveOutOpen(&hWaveOut , WAVE_MAPPER , &wfe ,(DWORD)hWnd , 0 , CALLBACK_WINDOW);
			waveOutPrepareHeader(hWaveOut , &whdr3 , sizeof(WAVEHDR));
			waveOutWrite(hWaveOut , &whdr3 , sizeof(WAVEHDR));



					waveInAddBuffer(hWaveIn , (PWAVEHDR)lP , sizeof(WAVEHDR));

		}
		return 0;
		
		
	case MM_WIM_CLOSE:
		waveInUnprepareHeader(hWaveIn , &whdr1 , sizeof(WAVEHDR));
		waveInUnprepareHeader(hWaveIn , &whdr2 , sizeof(WAVEHDR));
		free(bWave1);
		free(bWave2);
		
		return 0;
	case MM_WOM_OPEN:
		return 0;
	case MM_WOM_DONE:
		waveOutClose(hWaveOut);
		return 0;
	case MM_WOM_CLOSE:
		waveOutUnprepareHeader(hWaveOut , &whdr3 , sizeof(WAVEHDR));
		return 0;

    case WM_COMMAND:
        switch(LOWORD(wP)){
        case IDB_ACCEPT://[接続待ち]ボタン押下
				sock=SockAccept(hWnd);
				if(sock==INVALID_SOCKET)  return 0L;//接続待ち失敗 

				WSAAsyncSelect(sock, hWnd, WM_SOCKET, FD_READ);
			   
				

            return 0L;
        case IDB_CONNECT:                               //[接続]ボタン押下
            {
				char hostname[1024];
				GetWindowText(hWndHost,hostname,1000);
				if(!SockConnect(hWnd,hostname)){
					EnableWindow(hWndHost, TRUE);              //[HostName]無効
			        EnableWindow(hWndConnect, TRUE);           //[接続]    無効
					EnableWindow(hWndSend, TRUE);
					EnableWindow(hWndSendMsg, TRUE);
					EnableWindow(hWndAccept, TRUE);            //[接続待ち]無効
					EnableWindow(hWndReject, TRUE);             //[切断]    有効
				}
			}
            return 0L;
        case IDB_REJECT:                                //[切断]ボタン押下
            {
                if(sock!=INVALID_SOCKET){               //ソケットを閉じる
                    closesocket(sock);
                    sock=INVALID_SOCKET;
                }
                if(sv_sock!=INVALID_SOCKET){            //サーバ用ソケット
                    closesocket(sv_sock);               //　　　　を閉じる
                    sv_sock=INVALID_SOCKET;
                }
                EnableWindow(hWndHost, TRUE);           //[HostName]有効
                EnableWindow(hWndConnect, TRUE);        //[接続]    有効
                EnableWindow(hWndAccept, TRUE);         //[接続待ち]有効
                EnableWindow(hWndSendMsg, FALSE);       //送信文入力不可
                EnableWindow(hWndSend, FALSE);          //[送信]    無効
                EnableWindow(hWndReject, FALSE);        //[切断]    無効
                SetFocus(hWndHost);                     //フォーカス指定
            }
            return 0L;
		case IDB_SEND:                                  //メッセージ送信
            {
			bWave1 =(BYTE*) malloc(SRATE);
			bWave2 =(BYTE*) malloc(SRATE);

			whdr1.lpData = (LPSTR)bWave1;
			whdr1.dwBufferLength = SRATE;
			whdr1.dwBytesRecorded = 0;
			whdr1.dwFlags = 0;
			whdr1.dwLoops = 1;
			whdr1.lpNext = NULL;
			whdr1.dwUser = 0;
			whdr1.reserved = 0;

			whdr2.lpData =(LPSTR) bWave2;
			whdr2.dwBufferLength = SRATE;
			whdr2.dwBytesRecorded = 0;
			whdr2.dwFlags = 0;
			whdr2.dwLoops = 1;
			whdr2.lpNext = NULL;
			whdr2.dwUser = 0;
			whdr2.reserved = 0;

			waveInOpen(&hWaveIn , WAVE_MAPPER , &wfe ,
				(DWORD)hWnd , 0 , CALLBACK_WINDOW);
			waveInPrepareHeader(hWaveIn , &whdr1 , sizeof(WAVEHDR));
			waveInPrepareHeader(hWaveIn , &whdr2 , sizeof(WAVEHDR));
			break;
            }
            return 0L;


			


        }
        return 0L;
    case WM_SOCKET:                                     //非同期処理メッセージ
        if(WSAGETSELECTERROR(lP)!=0) return 0L;
        switch(WSAGETSELECTEVENT(lP)){
        case FD_READ:                                   //メッセージ受信
            {
				if(!ConnectionFlag)ConnectionFlag=TRUE;	
				int len = (int)sizeof(cl_sin),ret;
	            char *buf=(char*)malloc(sizeof(char)*3000);                  //受信バッファ
				if(0==recvfrom(sock, buf, 3000,0,(SOCKADDR *)&cl_sin, &len))MessageBox(NULL,"","",NULL);

			
			whdr3.lpData =(LPSTR) buf;
			whdr3.dwBufferLength = len;
			whdr3.dwBytesRecorded = 0;
			whdr3.dwFlags = WHDR_BEGINLOOP | WHDR_ENDLOOP;
			whdr3.dwLoops = 1;
			whdr3.lpNext = NULL;
			whdr3.dwUser = 0;
			whdr3.reserved = 0;


		
			waveOutOpen(&hWaveOut , WAVE_MAPPER , &wfe ,(DWORD)hWnd , 0 , CALLBACK_WINDOW);
			waveOutPrepareHeader(hWaveOut , &whdr3 , sizeof(WAVEHDR));
			waveOutWrite(hWaveOut , &whdr3 , sizeof(WAVEHDR));

		
		
            }
            return 0L;
        case FD_CLOSE:                                  //切断された
            MessageBox(hWnd, "Disconnected", "Information",
                MB_OK|MB_ICONINFORMATION);
            SendMessage(hWnd,WM_COMMAND,IDB_REJECT,0);  //切断処理発行
            return 0L;
        }
        return 0L;
    case WM_SETFOCUS:
        SetFocus(IsWindowEnabled(hWndHost)?hWndHost:hWndSendMsg);
        return 0L;
    case WM_DESTROY:                                    //ウィンドウ終了通知
		if (bSave) free(bSave);
		closesocket(sock);                              //ソケットを閉じる
        PostQuitMessage(0);                             //プログラム終了
        return 0L;                                      //イベント処理終了
    default:
        return DefWindowProc(hWnd, uMsg, wP, lP);       //標準メッセージ処理
    }
    return 0L;                                          //イベント処理終了
}


BOOL SockInit(HWND hWnd)
{
    WSADATA wsa;
    int ret;
    if((ret=WSAStartup(MAKEWORD(1,1), &wsa))){
        char buf[80];
        wsprintf(buf, "%d is the err", ret);
        MessageBox(hWnd, buf, "Error", MB_OK|MB_ICONSTOP);
        exit(-1);
    }
    return FALSE;
}


BOOL SockConnect(HWND hWnd, LPCSTR host)
{
                                        //SOCKADDR_IN構造体

    //ソケットを開く
    sock = socket(AF_INET, SOCK_DGRAM,0);       //ソケット作成失敗
    if(sock==INVALID_SOCKET){
        MessageBox(hWnd, "Socket() failed", "Error",
            MB_OK|MB_ICONEXCLAMATION);
        return TRUE;
    }

    memset(&cl_sin, 0x00, sizeof(cl_sin));                  //構造体初期化
    cl_sin.sin_family = AF_INET;                            //インターネット
    cl_sin.sin_port   = htons(PORT);                        //ポート番号指定

    if(!(phe=gethostbyname(host))){                         //アドレス取得
        MessageBox(hWnd, "gethostbyname() failed.", "Error",
            MB_OK|MB_ICONEXCLAMATION);
        return TRUE;
    }
    memcpy(&cl_sin.sin_addr, phe->h_addr, phe->h_length);   //アドレス値格納

    //非同期モード (接続)
    if(WSAAsyncSelect(sock, hWnd, WM_SOCKET, FD_READ)==SOCKET_ERROR){
        closesocket(sock);
        sock=INVALID_SOCKET;
        MessageBox(hWnd, "WSAAsyncSelect() failed", "Error",
            MB_OK|MB_ICONEXCLAMATION);
        return TRUE;
    }

    return FALSE;
}


SOCKET SockAccept(HWND hWnd)
{
    
    //サーバ用ソケット
    sv_sock = socket(AF_INET, SOCK_DGRAM, 0);
    if(sv_sock == INVALID_SOCKET){                      //ソケット作成失敗
        MessageBox(hWnd, "Socket() failed", "Error",
            MB_OK|MB_ICONEXCLAMATION);
        return SOCKET_ERROR;
    }

    memset(&cl_sin, 0x00, sizeof(cl_sin));              //構造体初期化
    cl_sin.sin_family      = AF_INET;                   //インターネット
    cl_sin.sin_port        = htons(PORT);               //ポート番号指定
    cl_sin.sin_addr.s_addr = htonl(INADDR_ANY);         //アドレス指定

    if(bind(sv_sock, (LPSOCKADDR)&cl_sin, sizeof(cl_sin))==SOCKET_ERROR){
        closesocket(sv_sock);
        sv_sock = INVALID_SOCKET;
        MessageBox(hWnd, "bind() failed", "Error",
            MB_OK|MB_ICONEXCLAMATION);
        return SOCKET_ERROR;
    }
	
    //非同期処理モード (接続待ち)
    if(WSAAsyncSelect(sv_sock, hWnd, WM_SOCKET, FD_READ)==SOCKET_ERROR){
        closesocket(sv_sock);
        sv_sock = INVALID_SOCKET;
        MessageBox(hWnd, "WSAAsyncSelect() failed", "Error",
            MB_OK|MB_ICONEXCLAMATION);
        return SOCKET_ERROR;
    }
    return sv_sock;
}