// win32mineView.cpp : Cwin32mineView 類別的實作
//

#include "stdafx.h"
#include "win32mine.h"

#include "../ActionHandler.h"
#include "win32mineDoc.h"
#include "win32mineView.h"

#include "MainFrm.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif


// Cwin32mineView

IMPLEMENT_DYNCREATE(Cwin32mineView, CView)

BEGIN_MESSAGE_MAP(Cwin32mineView, CView)
	// 標準列印命令
	ON_COMMAND(ID_FILE_PRINT, &CView::OnFilePrint)
	ON_COMMAND(ID_FILE_PRINT_DIRECT, &CView::OnFilePrint)
	ON_COMMAND(ID_FILE_PRINT_PREVIEW, &CView::OnFilePrintPreview)
    ON_WM_ERASEBKGND()
    ON_WM_LBUTTONDOWN()
    ON_WM_RBUTTONDOWN()
    ON_WM_LBUTTONUP()
    ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTW, 0, 0xFFFF, OnToolTipNotify)
    ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTA, 0, 0xFFFF, OnToolTipNotify)
    ON_WM_TIMER()
END_MESSAGE_MAP()

// Cwin32mineView 建構/解構

Cwin32mineView::Cwin32mineView()
: m_cellOrg(CPoint(0,0))
, m_mineCountOrg(CPoint(0,0))
, m_clockOrg(CPoint(0,0))
, m_resetBtnOrg(CPoint(0,0))
, m_timer(0)
{
	// TODO: 在此加入建構程式碼

}

Cwin32mineView::~Cwin32mineView()
{
}

BOOL Cwin32mineView::PreCreateWindow(CREATESTRUCT& cs)
{
	// TODO: 在此經由修改 CREATESTRUCT cs 
	// 達到修改視窗類別或樣式的目的

	return CView::PreCreateWindow(cs);
}

// Cwin32mineView 描繪

void Cwin32mineView::OnDraw(CDC* pDC)
{
	Cwin32mineDoc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);
	if (!pDoc)
		return;

	// TODO: 在此加入原生資料的描繪程式碼
    int gameBoardWidth = pDoc->GetBoardWidth();
    int gameBoardHeight = pDoc->GetBoardHeight();

    DrawMineCount(pDC, pDoc->GetMineCount());
    DrawResetButton(pDC, pDoc->GetResetStatus());
    DrawClock(pDC, pDoc->GetClockTicks());
    DrawCells(pDC, pDoc->GetCells());
}

BOOL Cwin32mineView::OnEraseBkgnd(CDC* pDC)
{
    // TODO: 在此加入您的訊息處理常式程式碼和 (或) 呼叫預設值
    CRect rect;
    GetClientRect(&rect);
    CBrush *pOld=(CBrush *)pDC->SelectStockObject(LTGRAY_BRUSH);
    pDC->Rectangle(&rect);    
    pDC->SelectObject(pOld);

    int width = GetDocument()->GetBoardWidth();
    int height = GetDocument()->GetBoardHeight();

    // right boundary
    int ix1 = m_cellOrg.x + width*m_cellSize.cx;
    int iy1 = m_cellOrg.y;
    int ix2 = ix1;
    int iy2 = m_cellOrg.y + height*m_cellSize.cy;
    pDC->MoveTo(ix1, iy1);
    pDC->LineTo(ix2, iy2);

    // bottom boundary
    ix1 = m_cellOrg.x;
    iy1 = iy2;
    pDC->LineTo(ix1, iy1);

    return TRUE;

    return CView::OnEraseBkgnd(pDC);
}

// Cwin32mineView 列印

BOOL Cwin32mineView::OnPreparePrinting(CPrintInfo* pInfo)
{
	// 預設的準備列印程式碼
	return DoPreparePrinting(pInfo);
}

void Cwin32mineView::OnBeginPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/)
{
	// TODO: 加入列印前額外的初始設定
}

void Cwin32mineView::OnEndPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/)
{
	// TODO: 加入列印後的清除程式碼
}


// Cwin32mineView 診斷

#ifdef _DEBUG
void Cwin32mineView::AssertValid() const
{
	CView::AssertValid();
}

void Cwin32mineView::Dump(CDumpContext& dc) const
{
	CView::Dump(dc);
}

Cwin32mineDoc* Cwin32mineView::GetDocument() const // 內嵌非偵錯版本
{
	ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(Cwin32mineDoc)));
	return (Cwin32mineDoc*)m_pDocument;
}
#endif //_DEBUG


// Cwin32mineView 訊息處理常式

void Cwin32mineView::OnInitialUpdate()
{
    CView::OnInitialUpdate();

    // TODO: 在此加入特定的程式碼和 (或) 呼叫基底類別
//    windowLayout(); // will cause the initial window to be closed on any key
//    CEdit* pEdit = new CEdit;
//    pEdit->Create(ES_MULTILINE | WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_BORDER,
//                  CRect(10, 10, 100, 100), this, 111);

    EnableToolTips(TRUE);
}

void Cwin32mineView::windowLayout(void)
{
    int clientWidth, clientHeight;
    int windowWidth, windowHeight;

    calculateWindowSize(&clientWidth, &clientHeight);
//    TRACE("client width=%d height=%d\n", clientWidth, clientHeight);
    clientWidth += 6;
    clientHeight += 6;

    SetWindowPos(0, 0, 0, clientWidth, clientHeight, SWP_NOMOVE | SWP_NOZORDER);

    int titleBarHeight = GetSystemMetrics(SM_CYCAPTION);
    int menuBarHeight = GetSystemMetrics(SM_CYMENU);
//    int statusBarHeight = ((CMainFrame *)AfxGetMainWnd())->GetStatusBarHeight();
    int statusBarHeight = menuBarHeight;
//    TRACE("%d %d %d\n", titleBarHeight, menuBarHeight, statusBarHeight);

    int cxBorder = GetSystemMetrics(SM_CXEDGE) + GetSystemMetrics(SM_CXFRAME);
    int cyBorder = GetSystemMetrics(SM_CYEDGE) + GetSystemMetrics(SM_CYFRAME);

    windowWidth = clientWidth + 2*cxBorder-6;
    windowHeight = clientHeight + titleBarHeight + menuBarHeight + statusBarHeight + 2*cyBorder-6;
//    TRACE("window width=%d height=%d\n", windowWidth, windowHeight);

    CRect rect(0, 0, windowWidth, windowHeight);
    CWnd *pWnd = AfxGetMainWnd();
    if (pWnd)
    {
        pWnd->CalcWindowRect(&rect, CWnd::adjustBorder);
//        pWnd->MoveWindow(0, 0, windowWidth, windowHeight);
        pWnd->SetWindowPos(0, 0, 0, windowWidth, windowHeight, SWP_NOMOVE | SWP_NOZORDER);
    }
}

void Cwin32mineView::OnUpdate(CView* /*pSender*/, LPARAM lHint, CObject* /*pHint*/)
{
    // TODO: 在此加入特定的程式碼和 (或) 呼叫基底類別
    Invalidate(FALSE);

    if (lHint == 1) // recalculate the window width and height
        windowLayout();
}

//Notification handler
BOOL Cwin32mineView::OnToolTipNotify(UINT id, NMHDR *pNMHDR, LRESULT *pResult)
{
   // need to handle both ANSI and UNICODE versions of the message
   TOOLTIPTEXTA* pTTTA = (TOOLTIPTEXTA*)pNMHDR;
   TOOLTIPTEXTW* pTTTW = (TOOLTIPTEXTW*)pNMHDR;
   CString strTipText;
   UINT nID = (UINT) pNMHDR->idFrom;
   if (pNMHDR->code == TTN_NEEDTEXTA && (pTTTA->uFlags & TTF_IDISHWND) ||
       pNMHDR->code == TTN_NEEDTEXTW && (pTTTW->uFlags & TTF_IDISHWND))
   {
      // idFrom is actually the HWND of the tool
      nID = ::GetDlgCtrlID((HWND)(UINT_PTR)nID);
   }

   if (nID == 2) // will be zero on a separator
   {
      strTipText.Format("重設遊戲");
      //((CMainFrame *)AfxGetMainWnd())->SetMessageText(strTipText);
   }
   else if (nID == 3)
      strTipText.Format("未定位地雷個數");
   else if (nID == 4)
      strTipText.Format("時鐘(秒)");

   if (pNMHDR->code == TTN_NEEDTEXTA)
      lstrcpyn(pTTTA->szText, strTipText, sizeof(pTTTA->szText));
   else
      ::MultiByteToWideChar( CP_ACP , 0, strTipText, -1, pTTTW->szText, sizeof(pTTTW->szText)/(sizeof pTTTW->szText[0]) );
   *pResult = 0;

   return TRUE;    // message was handled
}

INT_PTR Cwin32mineView::OnToolHitTest(CPoint point, TOOLINFO* pTI) const
{
    // TODO: 在此加入特定的程式碼和 (或) 呼叫基底類別
    int irow, icol;
    int iret = hitTest(point, &irow, &icol);

    if (iret <= 1) // cell 1 or other 0
        return -1;
 
    // if (iret == 2) // resetBtn
    // iret == 3 // mineCount
    // iret == 4 // clock
	pTI->hwnd = m_hWnd;
	pTI->uId = (UINT) iret;
	pTI->lpszText = LPSTR_TEXTCALLBACK;

    if (iret == 2) // resetBtn;
        pTI->rect = CRect(m_resetBtnOrg.x, 
                          m_resetBtnOrg.y,
                          m_resetBtnOrg.x+m_resetBtnSize.cx,
                          m_resetBtnOrg.y+m_resetBtnSize.cy);
    else if (iret == 3) // mineCount
        pTI->rect = CRect(m_mineCountOrg.x, 
                          m_mineCountOrg.y,
                          m_mineCountOrg.x+3*m_digitSize.cx,
                          m_mineCountOrg.y+m_digitSize.cy);
    else if (iret == 4) // clock
        pTI->rect = CRect(m_clockOrg.x, 
                          m_clockOrg.y,
                          m_clockOrg.x+3*m_digitSize.cx,
                          m_clockOrg.y+m_digitSize.cy);

    return pTI->uId;
//	return point.x*2000+point.y; // if you would like the tooltip to show constantly in that button
    
    return 0;

//    return CView::OnToolHitTest(point, pTI);
}

// return 0 if no cell or resetBtn is hit
// return 1 if a cell is hit
// return 2 if resetBtn is hit
// return 3 if mineCount is hit
// return 4 if clock is hit
int Cwin32mineView::hitTest(CPoint pt, int * cell_irow, int * cell_icol) const
{
    CRect rstRect(m_resetBtnOrg.x, 
                  m_resetBtnOrg.y, 
                  m_resetBtnOrg.x+m_resetBtnSize.cx, 
                  m_resetBtnOrg.y+m_resetBtnSize.cy);
    if (rstRect.PtInRect(pt))
        return 2;

    CRect mineCountRect(m_mineCountOrg.x, 
                        m_mineCountOrg.y,
                        m_mineCountOrg.x+3*m_digitSize.cx,
                        m_mineCountOrg.y+m_digitSize.cy);
    if (mineCountRect.PtInRect(pt))
        return 3;

    CRect clockRect(m_clockOrg.x, 
                    m_clockOrg.y,
                    m_clockOrg.x+3*m_digitSize.cx,
                    m_clockOrg.y+m_digitSize.cy);
    if (clockRect.PtInRect(pt))
        return 4;

    int i, j;
    int width = GetDocument()->GetBoardWidth();
    int height = GetDocument()->GetBoardHeight();
    for (i=0; i<height; i++)
        for (j=0; j<width; j++)
        {
            if (CRect(m_cellOrg.x+j*m_cellSize.cx,
                      m_cellOrg.y+i*m_cellSize.cy,
                      m_cellOrg.x+(j+1)*m_cellSize.cx,
                      m_cellOrg.y+(i+1)*m_cellSize.cy).PtInRect(pt))
            {
                *cell_irow = i;
                *cell_icol = j;
                return 1;
            }
        }
    return 0;
}

// calcualte the width and height in pixel of the window based on the width and the height of the game board
void Cwin32mineView::calculateWindowSize(int * windowWidth, int * windowHeight)
{
    CBitmap cell;
    BITMAP bm1;
    cell.LoadBitmapA(IDB_CELL);
    cell.GetBitmap(&bm1);
    m_cellSize = CSize(bm1.bmWidth, bm1.bmHeight);
    *windowWidth = bm1.bmWidth * GetDocument()->GetBoardWidth() + 20; // 10 pixels each side
    CBitmap digit;
    BITMAP bm2;
    digit.LoadBitmapA(IDB_0);
    digit.GetBitmap(&bm2);
    m_digitSize = CSize(bm2.bmWidth, bm2.bmHeight);
    *windowHeight = bm1.bmHeight * GetDocument()->GetBoardHeight() + 
                    bm2.bmHeight + 30; // 10 pixels each side and interval

    m_mineCountOrg = CPoint(10+10, 10);
    m_clockOrg = CPoint(*windowWidth-10-10-m_digitSize.cx*3, 10);
    m_cellOrg = CPoint(10, 10+m_digitSize.cy+10);

    CBitmap resetBtn;
    BITMAP bm3;
    resetBtn.LoadBitmapA(IDB_SMILE1);
    resetBtn.GetBitmap(&bm3);
    m_resetBtnSize = CSize(bm3.bmWidth, bm3.bmHeight);

    m_resetBtnOrg = CPoint(((m_mineCountOrg.x + 3*m_digitSize.cx)+m_clockOrg.x-bm3.bmWidth)/2, 8);
}

int Cwin32mineView::DrawBitmap(CDC *pdc, int x, int y, int id)
{
    CBitmap bmp, *pOldBmp;
    bmp.LoadBitmapA(id);
    BITMAP bm;
    bmp.GetBitmap(&bm);
    CDC dcMem;
    dcMem.CreateCompatibleDC(pdc);
    pOldBmp = (CBitmap *)dcMem.SelectObject(&bmp);
    pdc->BitBlt(x, y, bm.bmWidth, bm.bmHeight, &dcMem, 0, 0, SRCCOPY);
    dcMem.SelectObject(pOldBmp);
    return 0;
}

void Cwin32mineView::DrawCells(CDC *pDC, int cells[][MAXBOARDSIZE])
{
    int i, j;
    int gameBoardWidth = GetDocument()->GetBoardWidth();
    int gameBoardHeight = GetDocument()->GetBoardHeight();

    for (i=0; i<gameBoardHeight; i++)
        for (j=0; j<gameBoardWidth; j++)
            DrawBitmap(pDC, m_cellOrg.x+j*m_cellSize.cx, 
                            m_cellOrg.y+i*m_cellSize.cy,
                            cells[i][j]);
}

void Cwin32mineView::DrawClock(CDC* pDC, int ticks)
{
    int i, tmp;
    if (ticks < 0) 
        ticks = 0;
    else if (ticks > 999) 
        ticks = 999;
    for (i=0; i<3; i++)
    {
        tmp = ticks % 10;
        ticks = ticks / 10;
        DrawBitmap(pDC, m_clockOrg.x+(2-i)*m_digitSize.cx, 
                        m_clockOrg.y, IDB_0+tmp);
    }
}

void Cwin32mineView::DrawMineCount(CDC* pDC, int count)
{
    int i, tmp;
    if (count < 0) 
        count = 0;
    else if (count > 999) 
        count = 999;
    for (i=0; i<3; i++)
    {
        tmp = count % 10;
        count = count / 10;
        DrawBitmap(pDC, m_mineCountOrg.x+(2-i)*m_digitSize.cx, 
                        m_mineCountOrg.y, IDB_0+tmp);
    }
}

void Cwin32mineView::DrawResetButton(CDC* pDC, int status)
{
    if ((status != IDB_FRAWN1) && (status != IDB_FRAWN0) && 
        (status != IDB_SMILE1) && (status != IDB_SMILE0))
        status = IDB_SMILE1;
    DrawBitmap(pDC, m_resetBtnOrg.x, m_resetBtnOrg.y, status);
}

void Cwin32mineView::OnLButtonDown(UINT nFlags, CPoint point)
{
    // TODO: 在此加入您的訊息處理常式程式碼和 (或) 呼叫預設值
    int irow, icol;
    int iret = hitTest(point, &irow, &icol); // 0, 1, 2, 3, 4
    if (iret == 2)
    {
        GetDocument()->resetBtnPressed();
        SetCapture();
    }
    else if (iret == 1)
    {
        GetDocument()->GetHandler()->cellOpen(irow, icol);
    }

    CView::OnLButtonDown(nFlags, point);
}

void Cwin32mineView::OnLButtonUp(UINT nFlags, CPoint point)
{
    // TODO: 在此加入您的訊息處理常式程式碼和 (或) 呼叫預設值
    Cwin32mineDoc *pDoc = GetDocument();
    int status = pDoc->GetResetStatus();
    if ((status == IDB_FRAWN0) || (status == IDB_SMILE0))
    {
        GetDocument()->resetBtnReleased();
        int irow, icol;
        if (hitTest(point, &irow, &icol) == 2)
            pDoc->OnNewgame();
    }
    ReleaseCapture();

    CView::OnLButtonUp(nFlags, point);
}

void Cwin32mineView::OnRButtonDown(UINT nFlags, CPoint point)
{
    // TODO: 在此加入您的訊息處理常式程式碼和 (或) 呼叫預設值
    int irow, icol;

    if (hitTest(point, &irow, &icol) == 1)
        GetDocument()->GetHandler()->cellMark(irow, icol);

    CView::OnRButtonDown(nFlags, point);
}

void Cwin32mineView::startTimer(void)
{
    m_timer = SetTimer(IDT_TIMER, 1000, 0);
//    TRACE("startTimer() %d\n", m_timer);
}

void Cwin32mineView::stopTimer(void)
{
//    TRACE("stopTimer() %d\n", m_timer);
    if (m_timer)
    {
        KillTimer(m_timer);
        m_timer = 0;
    }

}

void Cwin32mineView::OnTimer(UINT_PTR nIDEvent)
{
    // TODO: 在此加入您的訊息處理常式程式碼和 (或) 呼叫預設值
//    TRACE("OnTimer() ");
    if (nIDEvent == IDT_TIMER)
    {
        int i=GetDocument()->GetClockTicks();
        GetDocument()->SetClock(++i);
//        TRACE("tick=%d\n", i);
    }

    CView::OnTimer(nIDEvent);
}