/* -*- Mode: C; c-basic-offset:4 ; indent-tabs-mode:nil ; -*- */
/*
 *  (C) 2001 by Argonne National Laboratory.
 *      See COPYRIGHT in top-level directory.
 */

// rimshotView.cpp : implementation of the CRimshotView class
//

#include "stdafx.h"
#include "rimshot.h"

#include "rimshotDoc.h"
#include "rimshotView.h"
#include "rimshot_draw.h"
#include "zoomdlg.h"
#include "OffsetDlg.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CRimshotView

IMPLEMENT_DYNCREATE(CRimshotView, CView)

    BEGIN_MESSAGE_MAP(CRimshotView, CView)
    //{{AFX_MSG_MAP(CRimshotView)
    ON_COMMAND(ID_NEXT, OnNext)
    ON_COMMAND(ID_PREVIOUS, OnPrevious)
    ON_COMMAND(ID_RESET_ZOOM, OnResetZoom)
    ON_COMMAND(ID_ZOOM_IN, OnZoomIn)
    ON_COMMAND(ID_ZOOM_OUT, OnZoomOut)
    ON_WM_KEYDOWN()
    ON_COMMAND(ID_ZOOM_TO, OnZoomTo)
    ON_WM_SIZE()
    ON_WM_ERASEBKGND()
    ON_COMMAND(ID_TOGGLE_ARROWS, OnToggleArrows)
    ON_WM_LBUTTONDOWN()
    ON_COMMAND(ID_VIEW_UNIFORM, OnViewUniform)
    ON_COMMAND(ID_VIEW_ZOOM, OnViewZoom)
    ON_COMMAND(ID_VIEW_SLIDERANKOFFSET, OnSlideRankOffset)
    //}}AFX_MSG_MAP
    // Standard printing commands
    ON_COMMAND(ID_FILE_PRINT, CView::OnFilePrint)
    ON_COMMAND(ID_FILE_PRINT_DIRECT, CView::OnFilePrint)
    ON_COMMAND(ID_FILE_PRINT_PREVIEW, CView::OnFilePrintPreview)
    END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CRimshotView construction/destruction
    CRimshotView::CRimshotView()
{
    m_Draw.pCanvas = NULL;
    m_Draw.pBitmap = NULL;
    m_Draw.pCopyCanvas = NULL;
    m_Draw.pCopyBitmap = NULL;
    m_hDrawThread = NULL;
    m_Draw.max_rect_size.cx = 0;
    m_Draw.max_rect_size.cy = 0;
    m_Draw.max_copy_size.cx = 0;
    m_Draw.max_copy_size.cy = 0;
    m_Draw.bDrawUniform = false;
    m_Draw.nUniformWidth = 15;
    m_Draw.pCursorRanks = NULL;
    m_Draw.ppUniRecursionColor = NULL;
    m_Draw.bSliding = false;
    m_Draw.pSlidingBitmap = NULL;
}

CRimshotView::~CRimshotView()
{
    int i;

    if (m_Draw.pCursorRanks)
        delete[]m_Draw.pCursorRanks;
    if (m_Draw.ppUniRecursionColor) {
        for (i = 0; i < m_Draw.nUniNumRanks; i++) {
            delete m_Draw.ppUniRecursionColor[i];
        }
        delete m_Draw.ppUniRecursionColor;
        m_Draw.ppUniRecursionColor = NULL;
    }
}

BOOL CRimshotView::PreCreateWindow(CREATESTRUCT & cs)
{
    return CView::PreCreateWindow(cs);
}

/////////////////////////////////////////////////////////////////////////////
// CRimshotView drawing

void CRimshotView::OnDraw(CDC * pDC)
{
    CRect rect;

    GetClientRect(&rect);
    if (rect.Width() == m_Draw.copy_size.cx && rect.Height() == m_Draw.copy_size.cy) {
        pDC->BitBlt(0, 0, rect.Width(), rect.Height(), m_Draw.pCopyCanvas, 0, 0, SRCCOPY);
    } else {
        pDC->StretchBlt(0, 0, rect.Width(), rect.Height(),
                        m_Draw.pCopyCanvas, 0, 0, m_Draw.copy_size.cx, m_Draw.copy_size.cy,
                        SRCCOPY);
    }
}

void CRimshotView::DrawToCanvas(CDC * pDC)
{
    CRect rect, big_rect, client_rect;
    RLOG_EVENT event;
    RLOG_ARROW arrow;
    double dx;
    int height, lip;
    CBrush *pBrush;
    int i, j, k, x, y;
    HCURSOR hOldCursor;
    double dwPixel, *pdNextPixel;
    int nMaxRecursions = 0;
    CBrush white_brush;
    CPen line_pen, cursor_pen;
    CString str;
    CSize size;
    double duration;

    CRimshotDoc *pDoc = GetDocument();
    ASSERT_VALID(pDoc);

    GetClientRect(&client_rect);
    GetClientRect(&big_rect);
    big_rect.DeflateRect(25, 25);

    line_pen.CreatePen(PS_SOLID, 1, RGB(100, 100, 100));
    cursor_pen.CreatePen(PS_SOLID, 1, RGB(255, 255, 255));

    pDC->FillSolidRect(client_rect, RGB(255, 255, 255));
    pDC->FillSolidRect(big_rect, RGB(0, 0, 0));

    if (pDoc->m_pInput) {
        hOldCursor = SetCursor(LoadCursor(NULL, IDC_WAIT));

        dx = ((double) big_rect.right - (double) big_rect.left) / (pDoc->m_dRight - pDoc->m_dLeft);
        height = (big_rect.bottom - big_rect.top) / pDoc->m_pInput->nNumRanks;

        for (i = 0; i < pDoc->m_pInput->nNumRanks; i++)
            nMaxRecursions = max(pDoc->m_pInput->pNumEventRecursions[i], nMaxRecursions);
        lip = (height / 2) / (nMaxRecursions + 1);

        dwPixel = 1.0 / dx;
        pdNextPixel = new double[pDoc->m_pInput->nNumRanks];

        // draw the horizontal rank lines
        pDC->SelectObject(&line_pen);
        for (i = 0; i < pDoc->m_pInput->nNumRanks; i++) {
            y = big_rect.top + (height * i) + (height / 2);
            pDC->MoveTo(big_rect.left, y);
            pDC->LineTo(big_rect.right, y);
        }

        // draw the event boxes
        for (j = 0; j < pDoc->m_pInput->nNumRanks; j++) {
            for (i = 0; i < pDoc->m_pInput->pNumEventRecursions[j]; i++) {
                if (RLOG_FindEventBeforeTimestamp(pDoc->m_pInput,
                                                  pDoc->m_pInput->header.nMinRank + j, i,
                                                  pDoc->m_dLeft, &event, NULL) == 0) {
                    if (event.end_time < pDoc->m_dLeft)
                        RLOG_GetNextEvent(pDoc->m_pInput, pDoc->m_pInput->header.nMinRank + j, i,
                                          &event);
                    else {
                        if (event.start_time < pDoc->m_dLeft)
                            event.start_time = pDoc->m_dLeft;
                    }
                    for (k = 0; k < pDoc->m_pInput->nNumRanks; k++)
                        pdNextPixel[k] = event.start_time;
                    while (event.start_time < pDoc->m_dRight) {
                        if (event.end_time > pdNextPixel[event.rank]) {
                            pdNextPixel[event.rank] = event.end_time + dwPixel;
                            rect.top = big_rect.top + (event.rank * height) + (lip * (i + 1));
                            rect.bottom = rect.top + height - (lip * (i + 1) * 2);
                            rect.left =
                                big_rect.left + (long) (dx * (event.start_time - pDoc->m_dLeft));
                            rect.right =
                                rect.left + (long) (dx * (event.end_time - event.start_time));
                            if (rect.left == rect.right)
                                rect.right++;
                            if (rect.right > big_rect.right)
                                rect.right = big_rect.right;

                            pDC->FillSolidRect(rect, pDoc->GetEventColor(event.event));
                        }

                        if (RLOG_GetNextEvent(pDoc->m_pInput,
                                              pDoc->m_pInput->header.nMinRank + j, i, &event) != 0)
                            event.start_time = pDoc->m_dRight + 1;
                    }
                }
            }
        }

        // draw the arrows
        // This isn't exactly right because I don't think the arrows are stored in end time order.
        if (RLOG_FindArrowBeforeTimestamp(pDoc->m_pInput, pDoc->m_dLeft, &arrow, NULL) == 0) {
            if (arrow.end_time < pDoc->m_dLeft)
                RLOG_GetNextArrow(pDoc->m_pInput, &arrow);
            //pDC->SelectObject(&line_pen);
            pDC->SelectObject(GetStockObject(WHITE_PEN));
            while (arrow.end_time < pDoc->m_dRight) {
                x = big_rect.left + (long) (dx * (arrow.start_time - pDoc->m_dLeft));
                y = big_rect.top + (height * arrow.src) + (height / 2);
                pDC->MoveTo(x, y);
                pDC->Ellipse(x - 5, y - 5, x + 5, y + 5);
                x = x + (long) (dx * (arrow.end_time - arrow.start_time));
                y = big_rect.top + (height * arrow.dest) + (height / 2);
                pDC->LineTo(x, y);
                if (RLOG_GetNextArrow(pDoc->m_pInput, &arrow) != 0)
                    arrow.end_time = pDoc->m_dRight + 1;
            }
        }
        // draw the vertical cursor line
        pDC->SelectObject(&cursor_pen);
        pDC->MoveTo((big_rect.right + big_rect.left) / 2, big_rect.top);
        pDC->LineTo((big_rect.right + big_rect.left) / 2, big_rect.bottom);

        pDC->SetBkMode(OPAQUE);
        pDC->SetBkColor(RGB(255, 255, 255));
        pDC->SetTextColor(RGB(0, 0, 0));

        // draw the ranks
        for (i = 0; i < pDoc->m_pInput->nNumRanks; i++) {
            y = big_rect.top + (height * i) + (height / 2);
            str.Format("%d", i);
            size = pDC->GetTextExtent(str);
            pDC->TextOut((big_rect.left - size.cx - 7) >= 0 ? (big_rect.left - size.cx - 7) : 0,
                         y - (size.cy / 2), str);
        }

        // draw the box, event description and duration
        if (RLOG_GetCurrentGlobalEvent(pDoc->m_pInput, &event) == 0) {
            pBrush = pDoc->GetEventBrush(event.event);
            rect.left = (big_rect.left + big_rect.right) / 2;
            rect.right = rect.left + 13;
            rect.top = 7;
            rect.bottom = 20;
            pDC->FillRect(&rect, pBrush);

            //str.Format("%3d: %s    duration: %.6f", event.rank, pDoc->GetEventDescription(event.event), event.end_time - event.start_time);
            //pDC->TextOut(rect.left + 20, 7, str);
            str.Format("%3d: %s ", event.rank, pDoc->GetEventDescription(event.event));
            size = pDC->GetTextExtent(str);
            pDC->TextOut(rect.left + 13, 7, str);
            //pDC->TextOut(rect.left - size.cx, 7, str);

            duration = event.end_time - event.start_time;
            str.Format("%.9f", duration);
            size = pDC->GetTextExtent(str);
            x = rect.left - 7 - size.cx;
            // write the milliseconds
            str.Format("%.3f", duration);
            size = pDC->GetTextExtent(str);
            CRgn rgn;
            rgn.CreateRectRgn(x, 7, x + size.cx, 7 + size.cy);
            pDC->SelectClipRgn(&rgn, RGN_COPY);
            str.Format("%.6f", duration);
            pDC->SetTextColor(RGB(0, 0, 0));
            pDC->TextOut(x, 7, str);
            // write the microseconds
            str.Format("%.6f", duration);
            size = pDC->GetTextExtent(str);
            rgn.CreateRectRgnIndirect(&client_rect);
            pDC->SelectClipRgn(&rgn, RGN_XOR);
            rgn.CreateRectRgn(x, 7, x + size.cx, 7 + size.cy);
            str.Format("%.9f", duration);
            pDC->SetTextColor(RGB(255, 0, 0));
            pDC->TextOut(x, 7, str);
            // write the nanoseconds
            pDC->SelectClipRgn(&rgn, RGN_COPY);
            rgn.CreateRectRgnIndirect(&client_rect);
            pDC->SelectClipRgn(&rgn, RGN_XOR);
            str.Format("%.9f", duration);
            pDC->SetTextColor(RGB(0, 0, 255));
            pDC->TextOut(x, 7, str);
            pDC->SetTextColor(RGB(0, 0, 0));

            rect.top = big_rect.top + (event.rank * height) + (lip * (event.recursion + 1));
            rect.bottom = rect.top + height - (lip * (event.recursion + 1) * 2);
            rect.left = big_rect.left + (long) (dx * (event.start_time - pDoc->m_dLeft));
            rect.right = rect.left + (long) (dx * (event.end_time - event.start_time));
            if (rect.left == rect.right)
                rect.right++;
            if (rect.right > big_rect.right)
                rect.right = big_rect.right;
            white_brush.CreateStockObject(WHITE_BRUSH);
            pDC->FrameRect(&rect, &white_brush);

            // draw the current start time
            str.Format("%.0f", event.start_time);
            size = pDC->GetTextExtent(str);
            x = ((big_rect.left + big_rect.right) / 2) - size.cx;
            y = big_rect.bottom + 7;
            str.Format("%.3f", event.start_time);
            size = pDC->GetTextExtent(str);
            rgn.CreateRectRgn(x, y, x + size.cx, y + size.cy);
            pDC->SelectClipRgn(&rgn, RGN_COPY);
            str.Format("%.6f", event.start_time);
            pDC->SetTextColor(RGB(0, 0, 0));
            pDC->TextOut(x, y, str);
            rgn.CreateRectRgnIndirect(&client_rect);
            pDC->SelectClipRgn(&rgn, RGN_XOR);
            pDC->SetTextColor(RGB(255, 0, 0));
            pDC->TextOut(x, y, str);
            pDC->SetTextColor(RGB(0, 0, 0));
        } else {
            str.Format("%.6f", (pDoc->m_dLeft + pDoc->m_dRight) / 2.0);
            size = pDC->GetTextExtent(str);
            pDC->TextOut(((big_rect.left + big_rect.right) / 2) - (size.cx / 2),
                         big_rect.bottom + 7, str);
        }

        // draw the left and right timestamps
        str.Format("%.6f", pDoc->m_dLeft);
        pDC->TextOut(big_rect.left, big_rect.bottom + 7, str);
        str.Format("%.6f", pDoc->m_dRight);
        size = pDC->GetTextExtent(str);
        pDC->TextOut(big_rect.right - size.cx, big_rect.bottom + 7, str);

        delete pdNextPixel;
        SetCursor(hOldCursor);
    }
}

/////////////////////////////////////////////////////////////////////////////
// CRimshotView printing

BOOL CRimshotView::OnPreparePrinting(CPrintInfo * pInfo)
{
    return DoPreparePrinting(pInfo);
}

void CRimshotView::OnBeginPrinting(CDC * /*pDC */ , CPrintInfo * /*pInfo */)
{
}

void CRimshotView::OnEndPrinting(CDC * /*pDC */ , CPrintInfo * /*pInfo */)
{
}

void CRimshotView::OnPrepareDC(CDC * pDC, CPrintInfo * pInfo)
{
    CView::OnPrepareDC(pDC, pInfo);

    if (pDC->IsPrinting()) {
        /*
         * CRect rect;
         * GetClientRect (&rect);
         *
         * int oldMapMode = pDC->SetMapMode(MM_ISOTROPIC);
         * CSize ptOldWinExt = pDC->SetWindowExt(1000, 1000);
         * CSize ptOldViewportExt = pDC->SetViewportExt(rect.Width(), -rect.Height());
         * CPoint ptOldOrigin = pDC->SetViewportOrg(rect.Width()/2, rect.Height()/2);
         */
    }
}

/////////////////////////////////////////////////////////////////////////////
// CRimshotView diagnostics

/* *INDENT-OFF* */
#ifdef _DEBUG
void CRimshotView::AssertValid() const
{
	CView::AssertValid();
}

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

CRimshotDoc* CRimshotView::GetDocument() // non-debug version is inline
{
	ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CRimshotDoc)));
	return (CRimshotDoc*)m_pDocument;
}
#endif //_DEBUG
/* *INDENT-ON* */

/////////////////////////////////////////////////////////////////////////////
// CRimshotView message handlers

void CRimshotView::OnNext()
{
    double d;
    RLOG_EVENT event1, event2;
    CRimshotDoc *pDoc = GetDocument();

    StopDrawing();
    d = (pDoc->m_dRight - pDoc->m_dLeft) / 6.0;
    RLOG_GetCurrentGlobalEvent(pDoc->m_pInput, &event1);
    RLOG_FindGlobalEventBeforeTimestamp(pDoc->m_pInput, (pDoc->m_dLeft + pDoc->m_dRight) / 2.0 + d,
                                        &event2);
    if (event1.start_time == event2.start_time)
        RLOG_GetNextGlobalEvent(pDoc->m_pInput, &event2);
    pDoc->m_dLeft = event2.start_time - (3.0 * d);
    pDoc->m_dRight = event2.start_time + (3.0 * d);
    StartDrawing();
}

void CRimshotView::OnPrevious()
{
    double d;
    RLOG_EVENT event;
    CRimshotDoc *pDoc = GetDocument();

    StopDrawing();
    d = (pDoc->m_dRight - pDoc->m_dLeft) / 6.0;
    RLOG_FindGlobalEventBeforeTimestamp(pDoc->m_pInput, (pDoc->m_dLeft + pDoc->m_dRight) / 2.0 - d,
                                        &event);
    pDoc->m_dLeft = event.start_time - (3.0 * d);
    pDoc->m_dRight = event.start_time + (3.0 * d);
    StartDrawing();
}

void CRimshotView::OnResetZoom()
{
    CRimshotDoc *pDoc = GetDocument();
    StopDrawing();
    pDoc->m_dLeft = pDoc->m_dFirst;
    pDoc->m_dRight = pDoc->m_dLast;
    StartDrawing();
}

void CRimshotView::OnZoomIn()
{
    double d;
    CRimshotDoc *pDoc = GetDocument();

    StopDrawing();
    d = (pDoc->m_dRight - pDoc->m_dLeft) / 4.0;
    pDoc->m_dLeft += d;
    pDoc->m_dRight -= d;
    StartDrawing();
}

void CRimshotView::OnZoomOut()
{
    double d;
    CRimshotDoc *pDoc = GetDocument();

    StopDrawing();
    d = (pDoc->m_dRight - pDoc->m_dLeft) / 2.0;
    pDoc->m_dLeft -= d;
    pDoc->m_dRight += d;
    StartDrawing();
}

void CRimshotView::OnZoomTo()
{
    CRect rect;
    RLOG_EVENT event;
    CRimshotDoc *pDoc = GetDocument();
    double width;

    if (pDoc->m_pInput) {
        StopDrawing();
        RLOG_GetCurrentGlobalEvent(pDoc->m_pInput, &event);
        GetClientRect(&rect);
        width = event.end_time - event.start_time;
        pDoc->m_dLeft = event.start_time - (10.0 * width);
        pDoc->m_dRight = event.start_time + (10.0 * width);
        StartDrawing();
    }
}

void CRimshotView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
    RLOG_EVENT event;
    double d;
    CRimshotDoc *pDoc = GetDocument();

    if (pDoc->m_pInput) {
        switch (nChar) {
            case VK_RIGHT:
                if (RLOG_GetNextGlobalEvent(pDoc->m_pInput, &event) == 0) {
                    while (!m_Draw.pCursorRanks[event.rank].active) {
                        if (RLOG_GetNextGlobalEvent(pDoc->m_pInput, &event) != 0)
                            break;
                    }
                    StopDrawing();
                    d = (pDoc->m_dRight - pDoc->m_dLeft) / 2;
                    pDoc->m_dLeft = event.start_time - d;
                    pDoc->m_dRight = event.start_time + d;
                    StartDrawing();
                }
                break;
            case VK_LEFT:
                if (RLOG_GetPreviousGlobalEvent(pDoc->m_pInput, &event) == 0) {
                    while (!m_Draw.pCursorRanks[event.rank].active) {
                        if (RLOG_GetPreviousGlobalEvent(pDoc->m_pInput, &event) != 0)
                            break;
                    }
                    StopDrawing();
                    d = (pDoc->m_dRight - pDoc->m_dLeft) / 2;
                    pDoc->m_dLeft = event.start_time - d;
                    pDoc->m_dRight = event.start_time + d;
                    StartDrawing();
                }
                break;
            default:
                break;
        }
    }

    CView::OnKeyDown(nChar, nRepCnt, nFlags);
}

void CRimshotView::OnSize(UINT nType, int cx, int cy)
{
    CView::OnSize(nType, cx, cy);

    if (m_Draw.pCanvas != NULL) {
        // stop any current drawing
        StopDrawing();

        if (m_Draw.max_rect_size.cx < cx || m_Draw.max_rect_size.cy < cy) {
            m_Draw.max_rect_size.cx = max(m_Draw.max_rect_size.cx, cx);
            m_Draw.max_rect_size.cy = max(m_Draw.max_rect_size.cy, cy);
            // create a new resized canvas bitmap
            CBitmap *pBmp = new CBitmap();
            pBmp->CreateDiscardableBitmap(GetDC(), m_Draw.max_rect_size.cx,
                                          m_Draw.max_rect_size.cy);

            if (m_Draw.pBitmap) {
                m_Draw.pCanvas->SelectObject(pBmp);
                delete m_Draw.pBitmap;
                m_Draw.pBitmap = pBmp;
            } else {
                m_Draw.pOriginalBmp = m_Draw.pCanvas->SelectObject(pBmp);
                m_Draw.pBitmap = pBmp;
            }
        }
        m_Draw.rect_size.cx = cx;
        m_Draw.rect_size.cy = cy;

        // create the copy bitmap if this is the first size event
        if (m_Draw.pCopyBitmap == NULL) {
            CBitmap *pBmpCopy = new CBitmap();
            pBmpCopy->CreateDiscardableBitmap(GetDC(), cx, cy);

            m_Draw.pCopyOriginalBmp = m_Draw.pCopyCanvas->SelectObject(pBmpCopy);
            m_Draw.pCopyBitmap = pBmpCopy;
            m_Draw.copy_size.cx = cx;
            m_Draw.copy_size.cy = cy;
            m_Draw.max_copy_size.cx = cx;
            m_Draw.max_copy_size.cy = cy;
        }
        // start redrawing
        StartDrawing();
    }
}

void CRimshotView::OnInitialUpdate()
{
    DWORD dwThreadID;
    CRimshotDoc *pDoc = GetDocument();
    CView::OnInitialUpdate();

    m_Draw.pCanvas = new CDC();
    m_Draw.pCanvas->CreateCompatibleDC(GetDC());
    m_Draw.pCanvas->SetStretchBltMode(COLORONCOLOR);
    m_Draw.pCopyCanvas = new CDC();
    m_Draw.pCopyCanvas->CreateCompatibleDC(GetDC());
    m_Draw.pCopyCanvas->SetStretchBltMode(COLORONCOLOR);
    m_Draw.hWnd = m_hWnd;
    m_Draw.pDoc = pDoc;
    m_Draw.hDrawEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
    m_Draw.hStoppedEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
    m_Draw.hMutex = CreateMutex(NULL, FALSE, NULL);
    m_Draw.bStop = false;
    m_Draw.nCmd = -1;
    m_Draw.bDrawArrows = true;
    m_hDrawThread =
        CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) RimshotDrawThread, &m_Draw, 0, &dwThreadID);
}

BOOL CRimshotView::DestroyWindow()
{
    StopDrawing();
    m_Draw.nCmd = EXIT_CMD;
    SetEvent(m_Draw.hDrawEvent);
    WaitForSingleObject(m_hDrawThread, 10000);
    CloseHandle(m_Draw.hDrawEvent);
    CloseHandle(m_Draw.hStoppedEvent);
    CloseHandle(m_hDrawThread);

    if (m_Draw.pCanvas) {
        if (m_Draw.pBitmap) {
            // select out the bitmap
            m_Draw.pCanvas->SelectObject(m_Draw.pOriginalBmp);
        }
        delete m_Draw.pCanvas;
        m_Draw.pCanvas = NULL;
    }
    if (m_Draw.pBitmap) {
        delete m_Draw.pBitmap;
        m_Draw.pBitmap = NULL;
    }

    if (m_Draw.pCopyCanvas) {
        if (m_Draw.pCopyBitmap) {
            m_Draw.pCopyCanvas->SelectObject(m_Draw.pCopyOriginalBmp);
        }
        delete m_Draw.pCopyCanvas;
        m_Draw.pCopyCanvas = NULL;
    }
    if (m_Draw.pCopyBitmap) {
        delete m_Draw.pCopyBitmap;
        m_Draw.pCopyBitmap = NULL;
    }

    return CView::DestroyWindow();
}

LRESULT CRimshotView::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
    CRect rect;
    CBitmap *pBmp;

    switch (message) {
        case DRAW_COMPLETE_MSG:
            GetClientRect(&rect);
            if (m_Draw.max_copy_size.cx < rect.Width() || m_Draw.max_copy_size.cy < rect.Height()) {
                m_Draw.max_copy_size.cx = max(m_Draw.max_copy_size.cx, rect.Width());
                m_Draw.max_copy_size.cy = max(m_Draw.max_copy_size.cy, rect.Height());
                pBmp = new CBitmap();
                pBmp->CreateDiscardableBitmap(GetDC(), m_Draw.max_copy_size.cx,
                                              m_Draw.max_copy_size.cy);
                m_Draw.pCopyCanvas->SelectObject(pBmp);
                delete m_Draw.pCopyBitmap;
                m_Draw.pCopyBitmap = pBmp;
            }
            m_Draw.copy_size.cx = rect.Width();
            m_Draw.copy_size.cy = rect.Height();
            if (WaitForSingleObject(m_Draw.hMutex, 2000) == WAIT_OBJECT_0) {
                m_Draw.pCopyCanvas->BitBlt(0, 0, rect.Width(), rect.Height(), m_Draw.pCanvas, 0, 0,
                                           SRCCOPY);
                ReleaseMutex(m_Draw.hMutex);
            }
            Invalidate(FALSE);
            break;
    }
    return CView::WindowProc(message, wParam, lParam);
}

void CRimshotView::StopDrawing()
{
    m_Draw.bStop = true;
    WaitForSingleObject(m_Draw.hStoppedEvent, 10000);
    m_Draw.bStop = false;
    ResetEvent(m_Draw.hStoppedEvent);
}

void CRimshotView::StartDrawing()
{
    m_Draw.nCmd = REDRAW_CMD;
    SetEvent(m_Draw.hDrawEvent);
    Invalidate(FALSE);
}

BOOL CRimshotView::OnEraseBkgnd(CDC * pDC)
{
    return TRUE;
}

void CRimshotView::OnToggleArrows()
{
    StopDrawing();
    m_Draw.bDrawArrows = !m_Draw.bDrawArrows;
    StartDrawing();
}

#define PointInside(point, rect) (point.x >= rect.left && point.x <= rect.right && point.y >= rect.top && point.y <= rect.bottom)

void CRimshotView::OnLButtonDown(UINT nFlags, CPoint point)
{
    bool bHit = false;
    CRimshotDoc *pDoc = GetDocument();
    if (pDoc->m_pInput) {
        for (int i = 0; i < pDoc->m_pInput->nNumRanks; i++) {
            if (PointInside(point, m_Draw.pCursorRanks[i].rect)) {
                m_Draw.pCursorRanks[i].active = !m_Draw.pCursorRanks[i].active;
                //InvalidateRect(&m_Draw.pCursorRanks[i].rect);
                bHit = true;
            }
        }
    }
    if (bHit) {
        StopDrawing();
        StartDrawing();
    }
    CView::OnLButtonDown(nFlags, point);
}

void CRimshotView::OnViewUniform()
{
    CMenu *pMenu = GetParentFrame()->GetMenu();

    m_Draw.bDrawUniform = !m_Draw.bDrawUniform;

    if (pMenu)
        pMenu->CheckMenuItem(ID_VIEW_UNIFORM,
                             MF_BYCOMMAND | m_Draw.bDrawUniform ? MF_CHECKED : MF_UNCHECKED);
    StopDrawing();
    StartDrawing();
}

void CRimshotView::OnViewZoom()
{
    CRimshotDoc *pDoc = GetDocument();
    if (pDoc->m_pInput) {
        CZoomDlg dlg;
        CRect rect;

        GetClientRect(&rect);
        dlg.m_dLeft = pDoc->m_dLeft;
        dlg.m_dRight = pDoc->m_dRight;
        dlg.m_dWidth = pDoc->m_dRight - pDoc->m_dLeft;
        dlg.m_dPerPixel = dlg.m_dWidth / (double) rect.Width();

        if (dlg.DoModal() == IDOK) {
            if (dlg.m_bLR) {
                if (dlg.m_dLeft < dlg.m_dRight) {
                    RLOG_EVENT event;
                    pDoc->m_dLeft = dlg.m_dLeft;
                    pDoc->m_dRight = dlg.m_dRight;
                    RLOG_FindGlobalEventBeforeTimestamp(pDoc->m_pInput,
                                                        (dlg.m_dLeft + dlg.m_dRight) / 2.0, &event);
                    StopDrawing();
                    StartDrawing();
                }
            }
            if (dlg.m_bWidth) {
                if (dlg.m_dWidth > 0.0) {
                    double middle = (pDoc->m_dLeft + pDoc->m_dRight) / 2.0;
                    pDoc->m_dLeft = middle - (dlg.m_dWidth / 2.0);
                    pDoc->m_dRight = middle + (dlg.m_dWidth / 2.0);
                    StopDrawing();
                    StartDrawing();
                }
            }
            if (dlg.m_bPerPixel) {
                dlg.m_dWidth = dlg.m_dPerPixel * (double) rect.Width();
                if (dlg.m_dWidth > 0.0) {
                    double middle = (pDoc->m_dLeft + pDoc->m_dRight) / 2.0;
                    pDoc->m_dLeft = middle - (dlg.m_dWidth / 2.0);
                    pDoc->m_dRight = middle + (dlg.m_dWidth / 2.0);
                    StopDrawing();
                    StartDrawing();
                }
            }
        }
    }
}

void CRimshotView::OnSlideRankOffset()
{
    CRimshotDoc *pDoc = GetDocument();
    if (pDoc->m_pInput) {
        COffsetDlg dlg;

        if (dlg.DoModal() == IDOK) {
            if (dlg.m_row < pDoc->m_pInput->nNumRanks) {
                if (dlg.m_bUseGUI) {
                    m_Draw.bSliding = true;
                    m_Draw.nSlidingRank = dlg.m_row;
                    // setup bitmap and stuff
                } else {
                    // do file offset stuff
                }
            }
        }
    }
}
