root/src/apps/icon-o-matic/import_export/Exporter.cpp
/*
 * Copyright 2006, 2011, Stephan Aßmus <superstippi@gmx.de>.
 * All rights reserved. Distributed under the terms of the MIT License.
 */


#include "Exporter.h"

#include <fs_attr.h>
#include <new>
#include <stdio.h>

#include <Alert.h>
#include <Catalog.h>
#include <File.h>
#include <Locale.h>
#include <Node.h>
#include <NodeInfo.h>
#include <Path.h>
#include <Roster.h>
#include <String.h>

#include "CommandStack.h"
#include "Document.h"
#include "Icon.h"


#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "Icon-O-Matic-Exporter"


using std::nothrow;


Exporter::Exporter()
        : fDocument(NULL),
          fClonedIcon(NULL),
          fRef(),
          fExportThread(-1),
          fSelfDestroy(false)
{
}


Exporter::~Exporter()
{
        WaitForExportThread();

        delete fClonedIcon;
}


status_t
Exporter::Export(Document* document, const entry_ref& ref)
{
        if (!document || ref.name == NULL)
                return B_BAD_VALUE;

        fDocument = document;
        fClonedIcon = fDocument->Icon()->Clone();
        if (!fClonedIcon)
                return B_NO_MEMORY;

        fRef = ref;

        fExportThread = spawn_thread(_ExportThreadEntry, "export",
                                                                 B_NORMAL_PRIORITY, this);
        if (fExportThread < 0)
                return (status_t)fExportThread;

        resume_thread(fExportThread);

        return B_OK;
}


void
Exporter::SetSelfDestroy(bool selfDestroy)
{
        fSelfDestroy = selfDestroy;
}


void
Exporter::WaitForExportThread()
{
        if (fExportThread >= 0 && find_thread(NULL) != fExportThread) {
                status_t ret;
                wait_for_thread(fExportThread, &ret);
                fExportThread = -1;
        }
}


// #pragma mark -


int32
Exporter::_ExportThreadEntry(void* cookie)
{
        Exporter* exporter = (Exporter*)cookie;
        return exporter->_ExportThread();
}


int32
Exporter::_ExportThread()
{
        status_t ret = _Export(fClonedIcon, &fRef);
        if (ret < B_OK) {
                // inform user of failure at this point
                BString helper(B_TRANSLATE("Saving your document failed!"));
                helper << "\n\n" << ErrorCodeToString(ret);
                BAlert* alert = new BAlert(B_TRANSLATE_CONTEXT("Bad news", "Title of error alert"),
                        helper.String(),
                        B_TRANSLATE_COMMENT("Bleep!", "Exporter - Continue in error dialog"),
                        NULL, NULL, B_WIDTH_AS_USUAL, B_STOP_ALERT);
                // launch alert asynchronously
                alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
                alert->Go(NULL);
        } else {
                // success
        
                // add to recent document list
                be_roster->AddToRecentDocuments(&fRef);
                // mark command stack state as saved,
                fDocument->CommandStack()->Save();
                        // NOTE: CommandStack is thread safe
        
                if (fDocument->WriteLock()) {
                        // set ref and name of document
//                      fDocument->SetRef(fRef);
                        fDocument->SetName(fRef.name);

                        fDocument->WriteUnlock();
                }
        }

        if (fSelfDestroy)
                delete this;

        return ret;
}


status_t
Exporter::_Export(const Icon* icon, const entry_ref* docRef)
{
        // TODO: reenable the commented out code, but make it work
        // the opposite direction, ie *copy* the file contents

        BEntry entry(docRef, true);
        if (entry.IsDirectory())
                return B_BAD_VALUE;

        const entry_ref* ref = docRef;
//      entry_ref tempRef;
//
//      if (entry.Exists()) {
//              // if the file exists create a temporary file in the same folder
//              // and hope that it doesn't already exist...
//              BPath tempPath(docRef);
//              if (tempPath.GetParent(&tempPath) >= B_OK) {
//                      BString helper(docRef->name);
//                      helper << system_time();
//                      if (tempPath.Append(helper.String()) >= B_OK
//                              && entry.SetTo(tempPath.Path()) >= B_OK
//                              && entry.GetRef(&tempRef) >= B_OK) {
//                              // have the output ref point to the temporary
//                              // file instead
//                              ref = &tempRef;
//                      }
//              }
//      }

        status_t ret = B_BAD_VALUE;

        // do the actual save operation into a file
        BFile outFile(ref, B_CREATE_FILE | B_READ_WRITE | B_ERASE_FILE);
        ret = outFile.InitCheck();
        if (ret == B_OK) {
                try {
                        // export using the virtual Export() version
                        ret = Export(icon, &outFile);
                } catch (...) {
                        printf("Exporter::_Export() - "
                                   "unkown exception occured!\n");
                        ret = B_ERROR;
                }
                if (ret < B_OK) {
                        printf("Exporter::_Export() - "
                                   "failed to export icon: %s\n", strerror(ret));
                }
        } else {
                printf("Exporter::_Export() - "
                           "failed to create output file: %s\n", strerror(ret));
        }
        outFile.Unset();

//      if (ret < B_OK && ref != docRef) {
//              // in case of failure, remove temporary file
//              entry.Remove();
//      }
//
//      if (ret >= B_OK && ref != docRef) {
//              // move temp file overwriting actual document file
//              BEntry docEntry(docRef, true);
//              // copy attributes of previous document file
//              BNode sourceNode(&docEntry);
//              BNode destNode(&entry);
//              if (sourceNode.InitCheck() >= B_OK && destNode.InitCheck() >= B_OK) {
//                      // lock the nodes
//                      if (sourceNode.Lock() >= B_OK) {
//                              if (destNode.Lock() >= B_OK) {
//                                      // iterate over the attributes
//                                      char attrName[B_ATTR_NAME_LENGTH];
//                                      while (sourceNode.GetNextAttrName(attrName) >= B_OK) {
////                                            // skip the icon, since we probably wrote that
////                                            // before
////                                            if (strcmp(attrName, "BEOS:ICON") == 0)
////                                                    continue;
//                                              attr_info info;
//                                              if (sourceNode.GetAttrInfo(attrName, &info) >= B_OK) {
//                                                      char *buffer = new (nothrow) char[info.size];
//                                                      if (buffer && sourceNode.ReadAttr(attrName, info.type, 0,
//                                                                                                                        buffer, info.size) == info.size) {
//                                                              destNode.WriteAttr(attrName, info.type, 0,
//                                                                                                 buffer, info.size);
//                                                      }
//                                                      delete[] buffer;
//                                              }
//                                      }
//                                      destNode.Unlock();
//                              }
//                              sourceNode.Unlock();
//                      }
//              }
//              // clobber the orginal file with the new temporary one
//              ret = entry.Rename(docRef->name, true);
//      }

        if (ret >= B_OK && MIMEType()) {
                // set file type
                BNode node(docRef);
                if (node.InitCheck() == B_OK) {
                        BNodeInfo nodeInfo(&node);
                        if (nodeInfo.InitCheck() == B_OK)
                                nodeInfo.SetType(MIMEType());
                }
        }
        return ret;
}