/////////////////////////////////////////////////////////////////////////////
// Name:        samples/docview/view.cpp
// Purpose:     View classes implementation
// Author:      Julian Smart
// Modified by: Vadim Zeitlin: merge with the MDI version and general cleanup
// Created:     04/01/98
// Copyright:   (c) 1998 Julian Smart
//              (c) 2008 Vadim Zeitlin
// Licence:     wxWindows licence
/////////////////////////////////////////////////////////////////////////////

// For compilers that support precompilation, includes "wx/wx.h".
#include "wx/wxprec.h"


#ifndef WX_PRECOMP
    #include "wx/wx.h"
#endif

#if !wxUSE_DOC_VIEW_ARCHITECTURE
    #error You must set wxUSE_DOC_VIEW_ARCHITECTURE to 1 in setup.h!
#endif

#include "docview.h"
#include "doc.h"
#include "view.h"

// ----------------------------------------------------------------------------
// DrawingView implementation
// ----------------------------------------------------------------------------

wxIMPLEMENT_DYNAMIC_CLASS(DrawingView, wxView);

wxBEGIN_EVENT_TABLE(DrawingView, wxView)
    EVT_MENU(wxID_CUT, DrawingView::OnCut)
wxEND_EVENT_TABLE()

// What to do when a view is created. Creates actual
// windows for displaying the view.
bool DrawingView::OnCreate(wxDocument *doc, long flags)
{
    if ( !wxView::OnCreate(doc, flags) )
        return false;

    MyApp& app = wxGetApp();
    if ( app.GetMode() != MyApp::Mode_Single )
    {
        // create a new window and canvas inside it
        wxFrame* frame = app.CreateChildFrame(this, true);
        wxASSERT(frame == GetFrame());
        m_canvas = new MyCanvas(this);
        frame->Show();
    }
    else // single document mode
    {
        // reuse the existing window and canvas
        m_canvas = app.GetMainWindowCanvas();
        m_canvas->SetView(this);

        // Initialize the edit menu Undo and Redo items
        doc->GetCommandProcessor()->SetEditMenu(app.GetMainWindowEditMenu());
        doc->GetCommandProcessor()->Initialize();
    }

    return true;
}

// Sneakily gets used for default print/preview as well as drawing on the
// screen.
void DrawingView::OnDraw(wxDC *dc)
{
    dc->SetPen(*wxBLACK_PEN);

    // simply draw all lines of all segments
    const DoodleSegments& segments = GetDocument()->GetSegments();
    for ( DoodleSegments::const_iterator i = segments.begin();
          i != segments.end();
          ++i )
    {
        const DoodleLines& lines = i->GetLines();
        for ( DoodleLines::const_iterator j = lines.begin();
              j != lines.end();
              ++j )
        {
            const DoodleLine& line = *j;

            dc->DrawLine(line.x1, line.y1, line.x2, line.y2);
        }
    }
}

DrawingDocument* DrawingView::GetDocument()
{
    return wxStaticCast(wxView::GetDocument(), DrawingDocument);
}

void DrawingView::OnUpdate(wxView* sender, wxObject* hint)
{
    wxView::OnUpdate(sender, hint);
    if ( m_canvas )
        m_canvas->Refresh();
}

// Clean up windows used for displaying the view.
bool DrawingView::OnClose(bool deleteWindow)
{
    if ( !wxView::OnClose(deleteWindow) )
        return false;

    Activate(false);

    // Clear the canvas in single-window mode in which it stays alive
    if ( wxGetApp().GetMode() == MyApp::Mode_Single )
    {
        m_canvas->ClearBackground();
        m_canvas->ResetView();
        m_canvas = nullptr;

        if (GetFrame())
            wxStaticCast(GetFrame(), wxFrame)->SetTitle(wxTheApp->GetAppDisplayName());
    }
    else // not single window mode
    {
        if ( deleteWindow )
        {
            GetFrame()->Destroy();
            SetFrame(nullptr);
        }
    }
    return true;
}

void DrawingView::OnCut(wxCommandEvent& WXUNUSED(event) )
{
    DrawingDocument * const doc = GetDocument();

    doc->GetCommandProcessor()->Submit(new DrawingRemoveSegmentCommand(doc));
}

// ----------------------------------------------------------------------------
// TextEditView implementation
// ----------------------------------------------------------------------------

wxIMPLEMENT_DYNAMIC_CLASS(TextEditView, wxView);

wxBEGIN_EVENT_TABLE(TextEditView, wxView)
    EVT_MENU(wxID_COPY, TextEditView::OnCopy)
    EVT_MENU(wxID_PASTE, TextEditView::OnPaste)
    EVT_MENU(wxID_SELECTALL, TextEditView::OnSelectAll)
wxEND_EVENT_TABLE()

bool TextEditView::OnCreate(wxDocument *doc, long flags)
{
    if ( !wxView::OnCreate(doc, flags) )
        return false;

    wxFrame* frame = wxGetApp().CreateChildFrame(this, false);
    wxASSERT(frame == GetFrame());
    m_text = new wxTextCtrl(frame, wxID_ANY, "",
                            wxDefaultPosition, wxDefaultSize,
                            wxTE_MULTILINE);
    frame->Show();

    return true;
}

void TextEditView::OnDraw(wxDC *WXUNUSED(dc))
{
    // nothing to do here, wxTextCtrl draws itself
}

bool TextEditView::OnClose(bool deleteWindow)
{
    if ( !wxView::OnClose(deleteWindow) )
        return false;

    Activate(false);

    if ( wxGetApp().GetMode() == MyApp::Mode_Single )
    {
        m_text->Clear();
    }
    else // not single window mode
    {
        if ( deleteWindow )
        {
            GetFrame()->Destroy();
            SetFrame(nullptr);
        }
    }
    return true;
}

// ----------------------------------------------------------------------------
// MyCanvas implementation
// ----------------------------------------------------------------------------

wxBEGIN_EVENT_TABLE(MyCanvas, wxScrolledWindow)
    EVT_MOUSE_EVENTS(MyCanvas::OnMouseEvent)
wxEND_EVENT_TABLE()

// Define a constructor for my canvas
MyCanvas::MyCanvas(wxView *view, wxWindow *parent)
    : wxScrolledWindow(parent ? parent : view->GetFrame())
{
    m_view = view;
    m_currentSegment = nullptr;
    m_lastMousePos = wxDefaultPosition;

    SetCursor(wxCursor(wxCURSOR_PENCIL));

    // this is completely arbitrary and is done just for illustration purposes
    SetVirtualSize(1000, 1000);
    SetScrollRate(20, 20);

    SetBackgroundColour(*wxWHITE);
}

MyCanvas::~MyCanvas()
{
    delete m_currentSegment;
}

// Define the repainting behaviour
void MyCanvas::OnDraw(wxDC& dc)
{
    if ( m_view )
        m_view->OnDraw(& dc);
}

// This implements a tiny doodling program. Drag the mouse using the left
// button.
void MyCanvas::OnMouseEvent(wxMouseEvent& event)
{
    if ( !m_view )
        return;

    wxClientDC dc(this);
    PrepareDC(dc);

    dc.SetPen(*wxBLACK_PEN);

    const wxPoint pt(event.GetLogicalPosition(dc));

    // is this the end of the current segment?
    if ( m_currentSegment && event.LeftUp() )
    {
        if ( !m_currentSegment->IsEmpty() )
        {
            // We've got a valid segment on mouse left up, so store it.
            DrawingDocument * const
                doc = wxStaticCast(m_view->GetDocument(), DrawingDocument);

            doc->GetCommandProcessor()->Submit(
                new DrawingAddSegmentCommand(doc, *m_currentSegment));

            doc->Modify(true);
        }

        wxDELETE(m_currentSegment);
    }

    // is this the start of a new segment?
    if ( m_lastMousePos != wxDefaultPosition && event.Dragging() )
    {
        if ( !m_currentSegment )
            m_currentSegment = new DoodleSegment;

        m_currentSegment->AddLine(m_lastMousePos, pt);

        dc.DrawLine(m_lastMousePos, pt);
    }

    m_lastMousePos = pt;
}

// ----------------------------------------------------------------------------
// ImageCanvas implementation
// ----------------------------------------------------------------------------

// Define a constructor for my canvas
ImageCanvas::ImageCanvas(wxView* view)
    : wxScrolledWindow(view->GetFrame())
{
    m_view = view;
    SetScrollRate( 10, 10 );
}

// Define the repainting behaviour
void ImageCanvas::OnDraw(wxDC& dc)
{
    if ( m_view )
        m_view->OnDraw(& dc);
}

// ----------------------------------------------------------------------------
// ImageView implementation
// ----------------------------------------------------------------------------

wxIMPLEMENT_DYNAMIC_CLASS(ImageView, wxView);

ImageDocument* ImageView::GetDocument()
{
    return wxStaticCast(wxView::GetDocument(), ImageDocument);
}

bool ImageView::OnCreate(wxDocument* doc, long flags)
{
    if ( !wxView::OnCreate(doc, flags) )
        return false;

    wxFrame* frame = wxGetApp().CreateChildFrame(this, false);
    wxASSERT(frame == GetFrame());
    m_canvas = new ImageCanvas(this);
    frame->Show();

    return true;
}

void ImageView::OnUpdate(wxView* sender, wxObject* hint)
{
    wxView::OnUpdate(sender, hint);
    wxImage image = GetDocument()->GetImage();
    if ( image.IsOk() )
    {
        m_canvas->SetVirtualSize(image.GetWidth(), image.GetHeight());
    }
}

void ImageView::OnDraw(wxDC* dc)
{
    wxImage image = GetDocument()->GetImage();
    if ( image.IsOk() )
    {
        dc->DrawBitmap(wxBitmap(image), 0, 0, true /* use mask */);
    }
}

bool ImageView::OnClose(bool deleteWindow)
{
    if ( !wxView::OnClose(deleteWindow) )
        return false;

    Activate(false);

    if ( wxGetApp().GetMode() == MyApp::Mode_Single )
    {
        GetDocument()->DeleteContents();
    }
    else // not single window mode
    {
        if ( deleteWindow )
        {
            GetFrame()->Destroy();
            SetFrame(nullptr);
        }
    }
    return true;
}

// ----------------------------------------------------------------------------
// ImageDetailsView
// ----------------------------------------------------------------------------

ImageDetailsView::ImageDetailsView(ImageDetailsDocument *doc)
                : wxView()
{
    SetDocument(doc);

    m_frame = wxGetApp().CreateChildFrame(this, false);
    m_frame->SetTitle("Image Details");

    wxPanel * const panel = new wxPanel(m_frame);
    wxFlexGridSizer * const sizer = new wxFlexGridSizer(2, wxSize(5, 5));
    const wxSizerFlags
        flags = wxSizerFlags().Align(wxALIGN_CENTRE_VERTICAL).Border();

    sizer->Add(new wxStaticText(panel, wxID_ANY, "Image &file:"), flags);
    sizer->Add(new wxStaticText(panel, wxID_ANY, doc->GetFilename()), flags);

    sizer->Add(new wxStaticText(panel, wxID_ANY, "Image &type:"), flags);
    wxString typeStr;
    switch ( doc->GetType() )
    {
        case wxBITMAP_TYPE_PNG:
            typeStr = "PNG";
            break;

        case wxBITMAP_TYPE_JPEG:
            typeStr = "JPEG";
            break;

        default:
            typeStr = "Unknown";
    }
    sizer->Add(new wxStaticText(panel, wxID_ANY, typeStr), flags);

    sizer->Add(new wxStaticText(panel, wxID_ANY, "Image &size:"), flags);
    wxSize size = doc->GetSize();
    sizer->Add(new wxStaticText(panel, wxID_ANY,
                                wxString::Format("%d*%d", size.x, size.y)),
               flags);

    sizer->Add(new wxStaticText(panel, wxID_ANY, "Number of unique &colours:"),
               flags);
    sizer->Add(new wxStaticText(panel, wxID_ANY,
                                wxString::Format("%lu", doc->GetNumColours())),
               flags);

    sizer->Add(new wxStaticText(panel, wxID_ANY, "Uses &alpha:"), flags);
    sizer->Add(new wxStaticText(panel, wxID_ANY,
                                doc->HasAlpha() ? "Yes" : "No"), flags);

    panel->SetSizer(sizer);
    m_frame->SetClientSize(panel->GetBestSize());
    m_frame->Show(true);
}

void ImageDetailsView::OnDraw(wxDC * WXUNUSED(dc))
{
    // nothing to do here, we use controls to show our information
}

bool ImageDetailsView::OnClose(bool deleteWindow)
{
    if ( wxGetApp().GetMode() != MyApp::Mode_Single && deleteWindow )
    {
        delete m_frame;
        m_frame = nullptr;
    }

    return true;
}
