root/src/apps/icon-o-matic/import_export/message/MessageExporter.cpp
/*
 * Copyright 2006, 2023, Haiku. All rights reserved.
 * Distributed under the terms of the MIT License.
 *
 * Authors:
 *              Stephan Aßmus <superstippi@gmx.de>
 *              Zardshard
 */

#include "MessageExporter.h"

#include <ByteOrder.h>
#include <DataIO.h>
#include <Message.h>
#include <TypeConstants.h>

#include "Container.h"
#include "Defines.h"
#include "Icon.h"
#include "PathSourceShape.h"
#include "ReferenceImage.h"
#include "Shape.h"
#include "Style.h"
#include "Transformer.h"
#include "VectorPath.h"

// constructor
MessageExporter::MessageExporter()
{
}

// destructor
MessageExporter::~MessageExporter()
{
}

// Export
status_t
MessageExporter::Export(const Icon* icon, BPositionIO* stream)
{
        status_t ret = B_OK;
        BMessage archive;

        const Container<VectorPath>* paths = icon->Paths();
        const Container<Style>* styles = icon->Styles();

        // paths
        if (ret == B_OK) {
                BMessage allPaths;
                int32 count = paths->CountItems();
                for (int32 i = 0; i < count; i++) {
                        VectorPath* path = paths->ItemAtFast(i);
                        BMessage pathArchive;
                        ret = _Export(path, &pathArchive);
                        if (ret < B_OK)
                                break;
                        ret = allPaths.AddMessage("path", &pathArchive);
                        if (ret < B_OK)
                                break;
                }
        
                if (ret == B_OK)
                        ret = archive.AddMessage("paths", &allPaths);
        }

        // styles
        if (ret == B_OK) {
                BMessage allStyles;
                int32 count = styles->CountItems();
                for (int32 i = 0; i < count; i++) {
                        Style* style = styles->ItemAtFast(i);
                        BMessage styleArchive;
                        ret = _Export(style, &styleArchive);
                        if (ret < B_OK)
                                break;
                        ret = allStyles.AddMessage("style", &styleArchive);
                        if (ret < B_OK)
                                break;
                }
        
                if (ret == B_OK)
                        ret = archive.AddMessage("styles", &allStyles);
        }

        // shapes
        if (ret == B_OK) {
                BMessage allShapes;
                const Container<Shape>* shapes = icon->Shapes();
                int32 count = shapes->CountItems();
                for (int32 i = 0; i < count; i++) {
                        Shape* shape = shapes->ItemAtFast(i);
                        BMessage shapeArchive;
                        ret = _Export(shape, paths, styles, &shapeArchive);
                        if (ret < B_OK)
                                break;
                        ret = allShapes.AddMessage("shape", &shapeArchive);
                        if (ret < B_OK)
                                break;
                }
        
                if (ret == B_OK)
                        ret = archive.AddMessage("shapes", &allShapes);
        }

        // prepend the magic number to the file which
        // later tells us that this file is one of us
        if (ret == B_OK) {
                ssize_t size = sizeof(uint32);
                uint32 magic = B_HOST_TO_BENDIAN_INT32(kNativeIconMagicNumber);
                ssize_t written = stream->Write(&magic, size);
                if (written != size) {
                        if (written < 0)
                                ret = (status_t)written;
                        else
                                ret = B_IO_ERROR;
                }
        }

        if (ret == B_OK)
                ret = archive.Flatten(stream);

        return ret;
}

// MIMEType
const char*
MessageExporter::MIMEType()
{
        return kNativeIconMimeType;
}

// #pragma mark -

// _Export
status_t
MessageExporter::_Export(const VectorPath* path, BMessage* into) const
{
        return path->Archive(into, true);
}

// _Export
status_t
MessageExporter::_Export(const Style* style, BMessage* into) const
{
        return style->Archive(into, true);
}

// _Export
status_t
MessageExporter::_Export(const Shape* shape,
                                                 const Container<VectorPath>* globalPaths,
                                                 const Container<Style>* globalStyles,
                                                 BMessage* into) const
{
        status_t ret = B_OK;

        const PathSourceShape* pathSourceShape = dynamic_cast<const PathSourceShape*>(shape);
        if (pathSourceShape != NULL) {
                ret = into->AddInt32("type", PathSourceShape::archive_code);

                // NOTE: when the same path is used in two different
                // documents, and these are to be merged, each path
                // having a "globally unique" id would make it possible
                // to reference the same path across documents...
                // For now, we simply use the index of the path in the
                // globalPaths container.

                // index of used style
                if (ret == B_OK) {
                        Style* style = pathSourceShape->Style();
                        ret = into->AddInt32("style ref", globalStyles->IndexOf(style));
                }

                // indices of used paths
                if (ret == B_OK) {
                        int32 count = pathSourceShape->Paths()->CountItems();
                        for (int32 i = 0; i < count; i++) {
                                VectorPath* path = pathSourceShape->Paths()->ItemAtFast(i);
                                ret = into->AddInt32("path ref", globalPaths->IndexOf(path));
                                if (ret < B_OK)
                                        break;
                        }
                }
        }

        const ReferenceImage* referenceImage = dynamic_cast<const ReferenceImage*>(shape);
        if (referenceImage != NULL) {
                ret = into->AddInt32("type", ReferenceImage::archive_code);
        }

        // Shape properties
        if (ret == B_OK)
                ret = shape->Archive(into);

        return ret;
}