/* **************************************************************************************
   File: CardGameDlg.cpp
   By Mauro Mindoli January 2003
***************************************************************************************** */

// CardGameDlg.cpp : implementation file
//

#include "stdafx.h"
#include "CardGame.h"
#include "CardGameDlg.h"
#include "DlgHelp.h"
#include <sstream>
#include <mmsystem.h>

// Global Variables:

const int NumCards = 40;
const int HIDDENCARD = 1;
const int TIMEPCCARD = 1;
LPPICTURE gpPic[NumCards+1];
LPPICTURE gpPicHappy = NULL;
LPPICTURE gpPicAngry = NULL;
LPPICTURE gpPicCard = NULL;
LPPICTURE gpPicJolly = NULL;
LPPICTURE gpPicSound = NULL;
LPPICTURE gpPicNoSound = NULL;


template <class Num>
  std::string to_string( Num n )
  {
    std::ostringstream oss;
    oss << n;
    return oss.str();
  }


// This function loads a file into an IStream.
void myUtilities::LoadPictureFile(LPCTSTR szFile, LPPICTURE &gpPic)
{
   // open file
   HANDLE hFile = CreateFile(szFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
	_ASSERTE(INVALID_HANDLE_VALUE != hFile);

	// get file size
	DWORD dwFileSize = GetFileSize(hFile, NULL);
	_ASSERTE(-1 != dwFileSize);

	LPVOID pvData = NULL;
	// alloc memory based on file size
	HGLOBAL hGlobal = GlobalAlloc(GMEM_MOVEABLE, dwFileSize);
	_ASSERTE(NULL != hGlobal);

	pvData = GlobalLock(hGlobal);
	_ASSERTE(NULL != pvData);

	DWORD dwBytesRead = 0;
	// read file and store in global memory
	BOOL bRead = ReadFile(hFile, pvData, dwFileSize, &dwBytesRead, NULL);
   if (!bRead)
   {
      CString msg("It is not possible to load the file");
      msg += szFile;
      AfxMessageBox(LPCTSTR(msg));
      exit(1);
   }

	_ASSERTE(FALSE != bRead);
	GlobalUnlock(hGlobal);
	CloseHandle(hFile);

	LPSTREAM pstm = NULL;
	// create IStream* from global memory
	HRESULT hr = CreateStreamOnHGlobal(hGlobal, TRUE, &pstm);
	_ASSERTE(SUCCEEDED(hr) && pstm);

	// Create IPicture from image file
	if (gpPic)
		gpPic->Release();
	hr = ::OleLoadPicture(pstm, dwFileSize, FALSE, IID_IPicture, (LPVOID *)&gpPic);
	_ASSERTE(SUCCEEDED(hr) && gpPic);	
	pstm->Release();
}


void myUtilities::RenderPic(CDialog* pDlg, HDC hDC, LPPICTURE pPic, long lx, long ly)
{
	long hmWidth;
	long hmHeight;
	CRect rect;
	pDlg->GetClientRect(&rect);
   pPic->get_Width(&hmWidth);
	pPic->get_Height(&hmHeight);
	// convert himetric to pixels
	int nWidth	= MulDiv(hmWidth, GetDeviceCaps(hDC, LOGPIXELSX), HIMETRIC_INCH);
	int nHeight	= MulDiv(hmHeight, GetDeviceCaps(hDC, LOGPIXELSY), HIMETRIC_INCH);
   pPic->Render(hDC, lx, ly, 
         nWidth, nHeight, 0, hmHeight, hmWidth, -hmHeight, &rect);
}


/////////////////////////////////////////////////////////////////////////////
// CCardGameDlg dialog

CCardGameDlg::CCardGameDlg(CWnd* pParent /*=NULL*/)
	: CDialog(CCardGameDlg::IDD, pParent)
{
	//{{AFX_DATA_INIT(CCardGameDlg)
	m_MyPoints = 0.0;
	m_MyDollars = 10;
	m_PcDollars = 10;
	m_PcPoints = 0.0;
	m_Message = _T("Press <Card> button to play");
	m_bet = 0;
	//}}AFX_DATA_INIT
	// Note that LoadIcon does not require a subsequent DestroyIcon in Win32
	m_hIcon = AfxGetApp()->LoadIcon(IDR_SEVEN);
   
   // card selected;
   m_itMyCard = m_CardMap.end();
   m_itPcCard = m_CardMap.end();
   m_end_hand = false;
   m_iCountCards = 0;
   m_bFirstCard = true;
   m_bStopCard = false;
   m_bSound = true;
   m_gameResult = game::GAME_OK;
   m_resultPic = gpPicCard;
}

void CCardGameDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CCardGameDlg)
	DDX_Text(pDX, IDC_MYPOINTS, m_MyPoints);
	DDX_Text(pDX, IDC_MYDOLLARS, m_MyDollars);
	DDX_Text(pDX, IDC_PCDOLLARS, m_PcDollars);
	DDX_Text(pDX, IDC_PCPOINTS, m_PcPoints);
	DDX_Text(pDX, IDC_MESSAGE, m_Message);
	DDX_CBIndex(pDX, IDC_COMBOBET, m_bet);
	//}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CCardGameDlg, CDialog)
	//{{AFX_MSG_MAP(CCardGameDlg)
   ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	ON_BN_CLICKED(IDC_CARD, OnCard)
	ON_BN_CLICKED(IDC_STOP, OnStop)
	ON_BN_CLICKED(ID_EXIT, OnExit)
	ON_WM_SETCURSOR()
	ON_BN_CLICKED(IDC_HELPBUT, OnHelpbut)
	ON_CBN_SELENDOK(IDC_COMBOBET, OnSelendokCombobet)
	ON_BN_CLICKED(IDC_SOUND, OnSound)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CCardGameDlg message handlers


BOOL CCardGameDlg::OnInitDialog()
{
	CDialog::OnInitDialog();

	// Set the icon for this dialog.  The framework does this automatically
	//  when the application's main window is not a dialog
	SetIcon(m_hIcon, TRUE);			// Set big icon
	SetIcon(m_hIcon, FALSE);		// Set small icon


   for (int i = 0; i <= NumCards; i++)
   {
      MyPlaySound(".\\wav\\start.wav", NULL, SND_ASYNC | SND_FILENAME);
      
      std::string strPath(".\\napoletane\\");
      strPath += to_string(i) + ".bmp";
	   myUtilities::LoadPictureFile(strPath.c_str(), gpPic[i]);

      // *************************************************************
      // Building of a CardMap map<idCard, LPPICTURE>                *
      // idCard = [(fam << 8) | val]                                 *
      // (0, 9) [cups]coppe - (10, 19) [coins]denari -               * 
      // (20, 29) [swards]spade - (30, 39) [woods]bastoni - 40 cover *
      // *************************************************************
      if (i != NumCards)
         m_CardMap[(i/10 << 8) | ((i%10)+1)] = gpPic[i];
      else // the cover
         m_CardMap[0] = gpPic[NumCards];
   }
   myUtilities::LoadPictureFile(".\\napoletane\\happy.bmp", gpPicHappy);
   myUtilities::LoadPictureFile(".\\napoletane\\angry.bmp", gpPicAngry);
   myUtilities::LoadPictureFile(".\\napoletane\\card.bmp", gpPicCard);
   myUtilities::LoadPictureFile(".\\napoletane\\jolly.bmp", gpPicJolly);
   myUtilities::LoadPictureFile(".\\napoletane\\sound.bmp", gpPicSound);
   myUtilities::LoadPictureFile(".\\napoletane\\nosound.bmp", gpPicNoSound);

   m_itMyCard = m_CardMap.find(0);
   m_itPcCard = m_CardMap.find(0);
	return TRUE;  // return TRUE  unless you set the focus to a control
}

void CCardGameDlg::MyPlaySound(LPCSTR pszSound, HMODULE hmod, DWORD fdwSound)
{
         if (m_bSound)
            PlaySound(pszSound, hmod, fdwSound);

}

// If you add a minimize button to your dialog, you will need the code below
//  to draw the icon.  For MFC applications using the document/view model,
//  this is automatically done for you by the framework.

void CCardGameDlg::OnPaint() 
{
   // Update dollars and points
   SetDlgItemInt(IDC_PCDOLLARS, m_PcDollars, true);
   SetDlgItemInt(IDC_MYDOLLARS, m_MyDollars, true);
   if (m_gameResult != game::GAME_LOST)
   {
      SetDlgItemText(IDC_PCPOINTS, to_string(m_PcPoints).c_str());
      SetDlgItemText(IDC_MYPOINTS, to_string(m_MyPoints).c_str());
   }
   SetDlgItemText(IDC_MESSAGE, m_Message);
	CPaintDC dc(this); // device context for painting
	// get width and height of picture
	long hmWidth;
	long hmHeight;
   LPPICTURE pPic = 0;
   if (m_itMyCard != m_CardMap.end())
      pPic =(*m_itMyCard).second; // this is the current card picture pointer
	pPic->get_Width(&hmWidth);
	pPic->get_Height(&hmHeight);
	// convert himetric to pixels
	int nWidth	= MulDiv(hmWidth, GetDeviceCaps(dc.m_hDC, LOGPIXELSX), HIMETRIC_INCH);
	int nHeight	= MulDiv(hmHeight, GetDeviceCaps(dc.m_hDC, LOGPIXELSY), HIMETRIC_INCH);
	CRect rect;
	//RECT rc;
	GetClientRect(&rect);

   // display picture using IPicture::Render
   // my cards
   std::vector<LPPICTURE>::iterator itPic;
   std::vector<LPPICTURE>::iterator itPicEnd;
   
   itPic = m_picMyVect.begin();
   itPicEnd = m_picMyVect.end();
   int vy = 220, vx = 15;
   for ( ; itPic != itPicEnd; itPic++)
   {
      (*itPic)->Render(dc.m_hDC, vx, vy, 
         nWidth, nHeight, 0, hmHeight, hmWidth, -hmHeight, &rect);
      vx += (5*nWidth/4.0);
   }

   //pc cards
   itPic = m_picPcVect.begin();
   itPicEnd = m_picPcVect.end();
   vy = 30;
   vx = 15;
   for ( ; itPic != itPicEnd; itPic++)
   {
      (*itPic)->Render(dc.m_hDC, vx, vy, 
         nWidth, nHeight, 0, hmHeight, hmWidth, -hmHeight, &rect);
      vx += (5*nWidth/4.0);
   }

   // result picture
   if (m_resultPic)
      pPic = m_resultPic; // happy (Pc) or cup (Me)
   else
      pPic = gpPicCard; // card cover pic
   myUtilities::RenderPic(this, dc.m_hDC, pPic, 80, 154);   
   // sound picture
   if (m_bSound)
      pPic = gpPicSound; // happy (Pc) or cup (Me)
   else
      pPic = gpPicNoSound; // card cover pic
   myUtilities::RenderPic(this, dc.m_hDC, pPic, 610, 158);   
	CDialog::OnPaint();
}

// The system calls this to obtain the cursor to display while the user drags
//  the minimized window.
HCURSOR CCardGameDlg::OnQueryDragIcon()
{
	return (HCURSOR) m_hIcon;
}

#define KEY_DOWN(vk_code)((GetAsyncKeyState(vk_code)&0x8000)?1 :0)

void CCardGameDlg::OnCard() 
{
   // initialisation at first card of every hand   

   if (m_bFirstCard)
   {
      if (m_MyDollars <= 0)
      {
         MyPlaySound(".\\wav\\match.wav", NULL, SND_ASYNC | SND_FILENAME);
         AfxMessageBox("No Money? No Party! Pc Won the game :-(");
         OnExit();
      }
      m_gn.ResetPoints(); // both pc and my points
      m_bFirstCard = false; // this is not anymore the first card
      m_bStopCard = false; // I still have not press stop
      m_iCountCards = 0; // this counts the cards in my hand
      m_Message = _T("Card to play again. Stop to let Pc play. Bet to change bet");
      // at the start of every hand we erase the 2 vectors of cards
      m_picMyVect.erase(m_picMyVect.begin(), m_picMyVect.end());
      m_picPcVect.erase(m_picPcVect.begin(), m_picPcVect.end());
      m_resultPic = gpPicCard;
   }
   else
      m_Message = _T("Card to play again. Stop to let Pc play.");

   if (m_bStopCard)
   {
      MyPlaySound(".\\wav\\nostop.wav", NULL, SND_ASYNC | SND_FILENAME);
      return;
   }
   m_gameResult = m_gn.Play(game::ME);
   if (m_iCountCards == HIDDENCARD)
   {
      m_bet = GetDlgItemInt(IDC_COMBOBET);
      if (m_MyDollars - m_bet < 0)
      {
         MyPlaySound(".\\wav\\change.wav", NULL, SND_ASYNC | SND_FILENAME);
         AfxMessageBox("Change bet, you do not have enough money");
         return;
      }
      m_MyDollars -=m_bet;
   }
   m_iCountCards++;
   
   CGameCardNapoletano card;
   MyPlaySound(".\\wav\\card.wav", NULL, SND_ASYNC | SND_FILENAME);

   m_gn.GiveTheCard(card);
   if (card.isJolly())
   {
      m_resultPic = gpPicJolly;
      MyPlaySound(".\\wav\\matta.wav", NULL, SND_ASYNC | SND_FILENAME);
   }
   if (m_gameResult == game::GAME_LOST)
   {
      m_bFirstCard = true;
      m_PcDollars += m_bet;
      m_Message = _T("Pc won the hand!");
      m_resultPic = gpPicHappy;
      MyPlaySound(".\\wav\\loose.wav", NULL, SND_ASYNC | SND_FILENAME);
   }
   else
      m_gn.GetPoints(m_MyPoints, m_PcPoints);
   // take the idCard from the current card
   int idCard = card.GetIdCard();
   // with the idCard we can find the iterator to the picture by the map
   m_itMyCard = m_CardMap.find(idCard);
   LPPICTURE pPic = 0;
   if (m_itMyCard != m_CardMap.end())
   {
      pPic =(*m_itMyCard).second; // this is the current card picture pointer
      m_picMyVect.push_back(pPic); // we put it in the My vector pictures
   }
	CRect rect;
	GetClientRect(&rect);
   InvalidateRect(&rect, TRUE);   
}


void CCardGameDlg::OnPcCard() 
{
   if (m_bFirstCard)
   {
      m_gameResult = game::GAME_OK;
      m_bet = GetDlgItemInt(IDC_COMBOBET);
      m_PcDollars -=m_bet;
      m_bFirstCard = false;
      m_Message = _T("Pc is playing now");
   }

   m_gameResult = m_gn.Play(game::PC);
   CGameCardNapoletano card;
   MyPlaySound(".\\wav\\pccard.wav", NULL, SND_ASYNC | SND_FILENAME);
   m_gn.GiveTheCard(card);
   if (card.isJolly())
   {
      m_resultPic = gpPicJolly;
      MyPlaySound(".\\wav\\matta.wav", NULL, SND_ASYNC | SND_FILENAME);
   }
   if (m_gameResult == game::GAME_LOST)
   {
      m_bFirstCard = true;
      m_end_hand = true;
      m_MyDollars += 2*m_bet;
   }
   else
   if (m_gameResult == game::GAME_WON)
   {
      m_gn.GetPoints(m_MyPoints, m_PcPoints);
      m_end_hand = true;
   }
   else
      m_gn.GetPoints(m_MyPoints, m_PcPoints);
   // take the idCard from the current card
   int idCard = card.GetIdCard();
   // with the idCard we can find the iterator to the picture by the map
   m_itPcCard = m_CardMap.find(idCard);
   LPPICTURE pPic = 0;
   if (m_itPcCard != m_CardMap.end())
   {
      pPic =(*m_itPcCard).second; // this is the current card picture pointer
      m_picPcVect.push_back(pPic);
   }
   CRect rect;
   GetClientRect(&rect);
   InvalidateRect(&rect, TRUE);   
}


void CCardGameDlg::CheckWin()
{
   if (m_gameResult == game::GAME_WON)
   {
      m_PcDollars += 2*m_bet;
      m_Message = _T("Pc won the hand!");
      m_resultPic = gpPicHappy;
      MyPlaySound(".\\wav\\loose.wav", NULL, SND_ASYNC | SND_FILENAME);
      if (m_MyDollars <= 0)
      {
         MyPlaySound(".\\wav\\match.wav", NULL, SND_ASYNC | SND_FILENAME);
         AfxMessageBox("No Money? No Party! Pc Won the game :-(");
         OnExit();
      }
   }
   else
   {
      m_Message = _T("You won the hand!");
      m_resultPic = gpPicAngry;
      MyPlaySound(".\\wav\\win.wav", NULL, SND_ASYNC | SND_FILENAME);
      if (m_PcDollars < 0)
      {
         MyPlaySound(".\\wav\\match.wav", NULL, SND_ASYNC | SND_FILENAME);
         AfxMessageBox("No Money? No Party! You won the match! :-)");
         OnExit();
      }
   }
   CRect rect;
   GetClientRect(&rect);
   InvalidateRect(&rect, TRUE);   
}

// When we stop, this timer let the Pc play every second
void CALLBACK EXPORT TimerPcCard(
   HWND hWnd,      // handle of CWnd that called SetTimer
   UINT nMsg,      // WM_TIMER
   UINT nIDEvent,   // timer identification
   DWORD dwTime    // system time
)
{
   CCardGameDlg *pCardGameDlgInst = (CCardGameDlg *) CWnd::FromHandle(hWnd);
   // m_end_hand is a flag to check the winner and stop the timer
   if (pCardGameDlgInst->m_end_hand)
   {
     
      KillTimer(pCardGameDlgInst->m_hWnd, TIMEPCCARD);
      pCardGameDlgInst->CheckWin();
      pCardGameDlgInst->m_end_hand = false;
      pCardGameDlgInst->m_bFirstCard = true;
      // this message is to OnSetCursor to change the cursor shape
      pCardGameDlgInst->SendMessage(WM_SETCURSOR, (WPARAM)hWnd, (LPARAM) 0);
      if (pCardGameDlgInst->m_PcDollars < 0)
      {
         pCardGameDlgInst->MyPlaySound(".\\wav\\match.wav", NULL, SND_ASYNC | SND_FILENAME);
         AfxMessageBox("Pc No Money? You won the game!");
         pCardGameDlgInst->OnExit();
      }
   }
   else // every second the Pc plays
      pCardGameDlgInst->OnPcCard();
}

// To pass the hand to Pc
void CCardGameDlg::OnStop() 
{
   // Forbidden to stop at the before the firs card or when
   // already stopped
   if (m_bFirstCard || m_bStopCard)
   {
      MyPlaySound(".\\wav\\nostop.wav", NULL, SND_ASYNC | SND_FILENAME);
      return;
   }
   if (m_iCountCards == HIDDENCARD)
   {
      m_bet = GetDlgItemInt(IDC_COMBOBET);
      if (m_MyDollars - m_bet < 0)
      {
         MyPlaySound(".\\wav\\change.wav", NULL, SND_ASYNC | SND_FILENAME);
         AfxMessageBox("Change bet, you do not have enough money");
         return;
      }
      m_MyDollars -=m_bet;
   }
   MyPlaySound(".\\wav\\stop.wav", NULL, SND_ASYNC | SND_FILENAME);

   m_bFirstCard = true;
   m_bStopCard = true;
   void (CALLBACK EXPORT* pTimerPcCard)(HWND, UINT, UINT, DWORD) = TimerPcCard;
   SetTimer(TIMEPCCARD, 1000, pTimerPcCard);
}

void CCardGameDlg::OnExit() 
{
  CDialog::OnOK();
}

// To make a change to the cursor shape
BOOL CCardGameDlg::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message) 
{
	// shape hourglass and shape 7&half
   if (m_bStopCard == true && (m_resultPic == gpPicCard))
      SetCursor(AfxGetApp()->LoadStandardCursor(IDC_WAIT));
   else
      SetCursor(AfxGetApp()->LoadCursor(IDC_CARDCURSOR));
   return 0;
	
}

void CCardGameDlg::OnHelpbut() 
{

   MyPlaySound(".\\wav\\help.wav", NULL, SND_ASYNC | SND_FILENAME);
   DlgHelp DHelp(this);
   DHelp.DoModal();
}


void CCardGameDlg::OnSelendokCombobet() 
{
	// TODO: Add your control notification handler code here
   if ((m_iCountCards != 1) || m_bStopCard)
   {
      MyPlaySound(".\\wav\\bet.wav", NULL, SND_ASYNC | SND_FILENAME);
      AfxMessageBox("You can only bet right after you have seen your hidden card!");
      CComboBox *cbp = (CComboBox *)GetDlgItem(IDC_COMBOBET);
      cbp->SetCurSel(0);
   }
   else
      MyPlaySound(".\\wav\\money.wav", NULL, SND_ASYNC | SND_FILENAME);

}

void CCardGameDlg::OnSound() 
{
   if (m_bSound)
      m_bSound = false;
   else
   {
      m_bSound = true;
      MyPlaySound(".\\wav\\stop.wav", NULL, SND_ASYNC | SND_FILENAME);
   }
	CRect rect;
	GetClientRect(&rect);
   InvalidateRect(&rect, TRUE);   
  
}

