/////////////////////////////////////////////////////////////////////////////
// Name:        contrib/samples/ogl/studio/csprint.cpp
// Purpose:     Printing and clipboard functionality
// Author:      Julian Smart
// Modified by:
// Created:     12/07/98
// RCS-ID:      $Id$
// Copyright:   (c) Julian Smart
// Licence:     wxWindows licence
/////////////////////////////////////////////////////////////////////////////

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

#ifdef __BORLANDC__
#pragma hdrstop
#endif

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

#include "wx/ogl/ogl.h" // base header of OGL, includes and adjusts wx/deprecated/setup.h

#include "wx/clipbrd.h"

#ifdef __WXMSW__
#include "wx/metafile.h"
#endif

#include "studio.h"
#include "doc.h"
#include "shapes.h"
#include "view.h"

IMPLEMENT_DYNAMIC_CLASS(wxDiagramClipboard, wxDiagram)

// Copy selection
bool wxDiagramClipboard::Copy(wxDiagram* diagram)
{
    DeleteAllShapes();

    return DoCopy(diagram, this, false, NULL);
}

// Copy contents to the diagram, with new ids.

bool wxDiagramClipboard::Paste(wxDiagram* diagram, wxDC* dc, int offsetX, int offsetY)
{
    return DoCopy(this, diagram, true, dc, offsetX, offsetY);
}

// Universal copy function (to or from clipboard).
// TODO:
// Note that this only works for non-composites so far (nested shapes
// don't have their old-to-new object mappings stored).
// Also, lines don't yet get their attachment points moved to the new offset position
// if they have more than 2 points.
bool wxDiagramClipboard::DoCopy(wxDiagram* diagramFrom, wxDiagram* diagramTo, bool newIds,
    wxDC* dc, int offsetX, int offsetY)
{
    OnStartCopy(diagramTo);

    wxHashTable mapping(wxKEY_INTEGER);

    // First copy all node shapes.
    wxList* shapeList = diagramFrom->GetShapeList();
    wxObjectList::compatibility_iterator node = shapeList->GetFirst();
    while (node)
    {
        wxShape* shape = (wxShape*) node->GetData();
        if (((diagramFrom == this) || shape->Selected()) && !shape->IsKindOf(CLASSINFO(wxLineShape)))
        {
            wxShape* newShape = shape->CreateNewCopy();
            newShape->GetLines().Clear();
            if (newIds)
            {
                newShape->AssignNewIds();
            }
            mapping.Put((long) shape, (wxObject*) newShape);

            newShape->SetX(newShape->GetX() + offsetX);
            newShape->SetY(newShape->GetY() + offsetY);

            OnAddShape(diagramTo, newShape, dc);

        }
        node = node->GetNext();
    }

    node = shapeList->GetFirst();
    while (node)
    {
        wxShape* shape = (wxShape*) node->GetData();
        if (((diagramFrom == this) || shape->Selected()) && shape->IsKindOf(CLASSINFO(wxLineShape)))
        {
            wxLineShape* lineShape = (wxLineShape*) shape;
            // Only copy a line if its ends are selected too.
            if ((diagramFrom == this) || (lineShape->GetTo()->Selected() && lineShape->GetFrom()->Selected()))
            {
                wxLineShape* newShape = (wxLineShape*) shape->CreateNewCopy();
                mapping.Put((long) shape, (wxObject*) newShape);

                if (newIds)
                    newShape->AssignNewIds();

                wxShape* fromShape = (wxShape*) mapping.Get((long) lineShape->GetFrom());
                wxShape* toShape = (wxShape*) mapping.Get((long) lineShape->GetTo());

                wxASSERT_MSG( (fromShape != NULL), _T("Could not find 'from' shape"));
                wxASSERT_MSG( (toShape != NULL), _T("Could not find 'to' shape"));

                fromShape->AddLine(newShape, toShape, newShape->GetAttachmentFrom(),
                  newShape->GetAttachmentTo());

                OnAddShape(diagramTo, newShape, dc);

            }
        }
        node = node->GetNext();
    }

    // Now make sure line ordering is correct
    node = shapeList->GetFirst();
    while (node)
    {
        wxShape* shape = (wxShape*) node->GetData();
        if (((diagramFrom == this) || shape->Selected()) && !shape->IsKindOf(CLASSINFO(wxLineShape)))
        {
            wxShape* newShape = (wxShape*) mapping.Get((long) shape);

            // Make a list of all the new lines, in the same order as the old lines.
            // Then apply the list of new lines to the shape.
            wxList newLines;
            wxObjectList::compatibility_iterator lineNode = shape->GetLines().GetFirst();
            while (lineNode)
            {
                wxLineShape* lineShape = (wxLineShape*) lineNode->GetData();
                if ((diagramFrom == this) || (lineShape->GetTo()->Selected() && lineShape->GetFrom()->Selected()))
                {
                    wxLineShape* newLineShape = (wxLineShape*) mapping.Get((long) lineShape);

                    wxASSERT_MSG( (newLineShape != NULL), _T("Could not find new line shape"));

                    newLines.Append(newLineShape);
                }

                lineNode = lineNode->GetNext();
            }

            if (newLines.GetCount() > 0)
                newShape->ApplyAttachmentOrdering(newLines);
        }
        node = node->GetNext();
    }

    OnEndCopy(diagramTo);

    return true;
}

#ifdef __WXMSW__
// Draw contents to a Windows metafile device context and a bitmap, and copy
// these to the Windows clipboard
bool wxDiagramClipboard::CopyToClipboard(double scale)
{
#if wxUSE_METAFILE
  // Make a metafile DC
  wxMetaFileDC mfDC;
  if (mfDC.Ok())
  {
    mfDC.SetUserScale(scale, scale);

    // Draw on metafile DC
    Redraw(mfDC);

    // int printWidth = mfDC.MaxX() - mfDC.MinX();
    // int printHeight = mfDC.MaxY() - mfDC.MinY();
    int maxX = (int)mfDC.MaxX();
    int maxY = (int)mfDC.MaxY();
    wxMetaFile *mf = mfDC.Close();

    // Set to a bitmap memory DC
    wxBitmap *newBitmap = new wxBitmap((int)(maxX + 10), (int)(maxY + 10));
    if (!newBitmap->Ok())
    {
      delete newBitmap;

      wxChar buf[200];
      wxSprintf(buf, _T("Sorry, could not allocate clipboard bitmap (%dx%d)"), (maxX+10), (maxY+10));
      wxMessageBox(buf, _T("Clipboard copy problem"));
      return false;
    }

    wxMemoryDC memDC;
    memDC.SelectObject(*newBitmap);
    memDC.Clear();

    // Now draw on memory bitmap DC
    Redraw(memDC);

    memDC.SelectObject(wxNullBitmap);

    // Open clipboard and set the data
    if (wxOpenClipboard())
    {
        wxEmptyClipboard();

        // Copy the bitmap to the clipboard
        wxSetClipboardData(wxDF_BITMAP, newBitmap, 0, 0);

#if 0 // TODO: replace this code (wxEnhMetaFile doesn't have SetClipboard)
        if (mf)
        {
            // Copy the metafile to the clipboard
            // Allow a small margin
            bool success = mf->SetClipboard((int)(mfDC.MaxX() + 15), (int)(mfDC.MaxY() + 15));
        }
#endif

        // Close clipboard
        wxCloseClipboard();
    }

    delete newBitmap;
    delete mf;

  }
  return true;
#else
  wxMessageBox("wxUSE_METAFILE in build required to use Clipboard", _T("Clipboard copy problem"));
  return false;
#endif
}
#endif
    // __WXMSW__

// Override this to e.g. have the shape added through a Do/Undo command system.
// By default, we'll just add it directly to the destination diagram.
bool wxDiagramClipboard::OnAddShape(wxDiagram* diagramTo, wxShape* newShape, wxDC* dc)
{
    diagramTo->AddShape(newShape);

    if (dc && (diagramTo != this))
    {
        newShape->Select(true, dc);
    }

    return true;
}

/*
 * csDiagramClipboard
 */

IMPLEMENT_DYNAMIC_CLASS(csDiagramClipboard, wxDiagramClipboard)

// Start/end copying
bool csDiagramClipboard::OnStartCopy(wxDiagram* diagramTo)
{
    // Do nothing if copying to the clipboard
    if (diagramTo == this)
        return true;

    // Deselect all objects initially.

    csDiagram* diagram = (csDiagram*) diagramTo;
    csDiagramDocument* doc = diagram->GetDocument();
    ((csDiagramView*)doc->GetFirstView())->SelectAll(false);

    m_currentCmd = new csDiagramCommand(_T("Paste"), doc);

    return true;
}

bool csDiagramClipboard::OnEndCopy(wxDiagram* diagramTo)
{
    // Do nothing if copying to the clipboard
    if (diagramTo == this)
        return true;

    csDiagram* diagram = (csDiagram*) diagramTo;
    csDiagramDocument* doc = diagram->GetDocument();

    if (m_currentCmd)
    {
        if (m_currentCmd->GetStates().GetCount() == 0)
        {
            delete m_currentCmd;
        }
        else
        {
            doc->GetCommandProcessor()->Submit(m_currentCmd);
            m_currentCmd = NULL;
        }
    }
    return true;
}

// Use the command framework to add the shapes, if we're copying to a diagram and
// not the clipboard.
bool csDiagramClipboard::OnAddShape(wxDiagram* diagramTo, wxShape* newShape, wxDC* WXUNUSED(dc))
{
    if (diagramTo == this)
    {
        diagramTo->AddShape(newShape);
    }
    else
    {
        csDiagram* diagram = (csDiagram*) diagramTo;
        /* csDiagramDocument* doc = */ diagram->GetDocument();

        if (newShape->IsKindOf(CLASSINFO(wxLineShape)))
            m_currentCmd->AddState(new csCommandState(ID_CS_ADD_LINE_SELECT, newShape, NULL));
        else
            m_currentCmd->AddState(new csCommandState(ID_CS_ADD_SHAPE_SELECT, newShape, NULL));
    }

    return true;
}