root/src/apps/cortex/RouteApp/DormantNodeIO.cpp
/*
 * Copyright (c) 1999-2000, Eric Moon.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions, and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions, and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * 3. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */


// DormantNodeIO.cpp

#include "DormantNodeIO.h"
#include "ImportContext.h"
#include "ExportContext.h"
#include "StringContent.h"

#include "NodeManager.h"

#include <Debug.h>
#include <MediaAddOn.h>
#include <MediaDefs.h>
#include <MediaRoster.h>
#include <Path.h>

#include <cstdlib>

#include "route_app_io.h"

__USE_CORTEX_NAMESPACE

// -------------------------------------------------------- //
// *** ctor/dtor
// -------------------------------------------------------- //

DormantNodeIO::~DormantNodeIO() {}

// initialize for import (to defaults)
DormantNodeIO::DormantNodeIO() :
        m_kinds(0LL),
        m_flavorID(0),
        m_flags(0),
        m_runMode(0),
        m_recordingDelay(0),
        m_cycle(false),
        m_exportValid(false) {}

// initialize for export
DormantNodeIO::DormantNodeIO(
        NodeRef*                                                                ref,
        const char*                                                     nodeKey) :
        m_exportValid(false) {
        
        ASSERT(ref);
        ASSERT(nodeKey);
        status_t err;

        m_nodeKey = nodeKey;

        // * extract dormant-node info  
        dormant_node_info info;
        err = ref->getDormantNodeInfo(&info);
        if(err < B_OK) {
                PRINT(( 
                        "!!! DormantNodeIO(): getDormantNodeInfo() failed:\n"
                        "    %s\n",
                        strerror(err)));
                return;
        }

        dormant_flavor_info flavorInfo;
        err = BMediaRoster::Roster()->GetDormantFlavorInfoFor(
                info, &flavorInfo);
        if(err < B_OK) {
                PRINT(( 
                        "!!! DormantNodeIO(): GetDormantFlavorInfoFor() failed:\n"
                        "    %s\n",
                        strerror(err)));
                return;
        }

        m_dormantName = flavorInfo.name;
        m_flavorID = info.flavor_id;
        m_kinds = flavorInfo.kinds;
        
        m_flags = ref->flags();
        entry_ref file;
        if(ref->getFile(&file) == B_OK)
                m_entry.SetTo(&file);
        
        m_runMode = ref->runMode();
        m_recordingDelay = ref->recordingDelay();
        m_cycle = ref->isCycling();
        
        // done extracting node info; ready for export
        m_exportValid = true;
}

// -------------------------------------------------------- //
// *** document-type setup
// -------------------------------------------------------- //

/*static*/
void DormantNodeIO::AddTo(
        XML::DocumentType*                      docType) {

        // map self
        docType->addMapping(new Mapping<DormantNodeIO>(_DORMANT_NODE_ELEMENT));

//      // map simple (content-only) elements
//      // +++++ should these be added at a higher level, since they're
//      //       shared?  no harm is done if one is added more than once,
//      //       since they'll always map to StringContent.
//      // +++++
//      docType->addMapping(new Mapping<StringContent>(_NAME_ELEMENT));
//      docType->addMapping(new Mapping<StringContent>(_KIND_ELEMENT));
//      docType->addMapping(new Mapping<StringContent>(_FLAVOR_ID_ELEMENT));
//      docType->addMapping(new Mapping<StringContent>(_FLAG_ELEMENT));
//      docType->addMapping(new Mapping<StringContent>(_RUN_MODE_ELEMENT));
//      docType->addMapping(new Mapping<StringContent>(_RECORDING_DELAY_ELEMENT));
//      docType->addMapping(new Mapping<StringContent>(_CYCLE_ELEMENT));
//      docType->addMapping(new Mapping<StringContent>(_REF_ELEMENT));
}

// -------------------------------------------------------- //
// *** operations
// -------------------------------------------------------- //

// call when object imported to create the described node

// +++++

status_t DormantNodeIO::instantiate(
        NodeManager*                                            manager,
        NodeRef**                                                               outRef) {

        status_t err;
        BPath p;
        if(m_entry.InitCheck() == B_OK)
                m_entry.GetPath(&p);

//      PRINT((
//              "DormantNodeIO:\n"
//              "  key:       %s\n"
//              "  name:      %s\n"
//              "  flavor:    %ld\n"
//              "  kinds:     %Lx\n"
//              "  flags:     %lx\n"
//              "  runMode:   %ld\n"
//              "  recDelay:  %lld\n"
//              "  cycle:     %s\n"
//              "  entry:     %s\n\n",
//              m_nodeKey.String(),
//              m_dormantName.String(),
//              m_flavorID,
//              m_kinds,
//              m_flags,
//              m_runMode,
//              m_recordingDelay,
//              m_cycle ? "true" : "false",
//              p.Path()));
        
        // find matching dormant node
        dormant_node_info info;
        err = _matchDormantNode(&info);
        if(err < B_OK) {
                PRINT((
                        "!!! _matchDormantNode() failed: %s\n", strerror(err)));
                return err;
        }
        
        // instantiate node
        err = manager->instantiate(
                info,
                outRef,
                B_INFINITE_TIMEOUT,
                m_flags);
        if(err < B_OK) {
                PRINT((
                        "!!! instantiate() failed: %s\n", strerror(err)));
                return err;
        }
        
        entry_ref mediaRef;
        if(m_entry.InitCheck() == B_OK && m_entry.GetRef(&mediaRef) == B_OK) {
                // set ref
                err = (*outRef)->setFile(mediaRef);
                if(err < B_OK) {
                        PRINT((
                                "!!! WARNING: setFile() failed: %s\n", strerror(err)));
                }
        }

        // set run mode
        if(m_runMode)
                (*outRef)->setRunMode(m_runMode, m_recordingDelay);
        
        // set cycle state
        if(m_cycle)
                (*outRef)->setCycling(true);

        return B_OK;
}

status_t DormantNodeIO::_matchDormantNode(
        dormant_node_info*                      outInfo) {

        status_t err;
        
        // fetch all dormant nodes matching the signature
        const int32 bufferSize = 32;
        dormant_node_info       buffer[bufferSize];
        int32 count = bufferSize;
        err = BMediaRoster::Roster()->GetDormantNodes(
                buffer,
                &count,
                0,
                0,
                m_dormantName.String(),
                m_kinds,
                0 /*~m_kinds*/);
        if(err < B_OK)
                return err;
        
        if(!count)
                return B_NAME_NOT_FOUND;
        
        for(int32 n = 0; n < count; ++n) {
                if(buffer[n].flavor_id == m_flavorID) {
                        *outInfo = buffer[n];
                        return B_OK;
                }
        }

        // didn't match flavor id
        return B_BAD_INDEX;
}


// -------------------------------------------------------- //
// *** IPersistent
// -------------------------------------------------------- //

// -------------------------------------------------------- //
// EXPORT:
// -------------------------------------------------------- //

//inline void _write_simple(
//      const char* element,
//      const char* value,
//      ExportContext& context) {
//
//      context.beginElement(element);
//      context.beginContent();
//      context.writeString(value);
//      context.endElement();
//}

// -------------------------------------------------------- //

void DormantNodeIO::xmlExportBegin(
        ExportContext&                                          context) const {
        
        if(!m_exportValid) {
                context.reportError(
                        "DormantNodeIO::xmlExportBegin():\n"
                        "*** invalid ***\n");
                return;
        }
        
        context.beginElement(_DORMANT_NODE_ELEMENT);
}
        
void DormantNodeIO::xmlExportAttributes(
        ExportContext&                                          context) const {

        context.writeAttr("key", m_nodeKey);    
}

void DormantNodeIO::xmlExportContent(
        ExportContext&                                          context) const {

        context.beginContent();
        BString buffer;
        
        // write dormant-node description
        context.beginElement(_NAME_ELEMENT);
        context.beginContent();
        context.writeString(m_dormantName);
        context.endElement();

        if(m_flavorID > 0) {
                buffer = "";
                buffer << m_flavorID;
                context.beginElement(_FLAVOR_ID_ELEMENT);
                context.beginContent();
                context.writeString(buffer);
                context.endElement();
        }       

        _write_node_kinds(m_kinds, context);
//      if(m_kinds & B_BUFFER_PRODUCER)
//              _write_simple(_KIND_ELEMENT, "B_BUFFER_PRODUCER", context);
//      if(m_kinds & B_BUFFER_CONSUMER)
//              _write_simple(_KIND_ELEMENT, "B_BUFFER_CONSUMER", context);
//      if(m_kinds & B_TIME_SOURCE)
//              _write_simple(_KIND_ELEMENT, "B_TIME_SOURCE", context);
//      if(m_kinds & B_CONTROLLABLE)
//              _write_simple(_KIND_ELEMENT, "B_CONTROLLABLE", context);
//      if(m_kinds & B_FILE_INTERFACE)
//              _write_simple(_KIND_ELEMENT, "B_FILE_INTERFACE", context);
//      if(m_kinds & B_ENTITY_INTERFACE)
//              _write_simple(_KIND_ELEMENT, "B_ENTITY_INTERFACE", context);
//      if(m_kinds & B_PHYSICAL_INPUT)
//              _write_simple(_KIND_ELEMENT, "B_PHYSICAL_INPUT", context);
//      if(m_kinds & B_PHYSICAL_OUTPUT)
//              _write_simple(_KIND_ELEMENT, "B_PHYSICAL_OUTPUT", context);
//      if(m_kinds & B_SYSTEM_MIXER)
//              _write_simple(_KIND_ELEMENT, "B_SYSTEM_MIXER", context);
        
        // write NodeRef flags
        if(m_flags & NodeRef::NO_START_STOP)
                _write_simple(_FLAG_ELEMENT, "NO_START_STOP", context);
        if(m_flags & NodeRef::NO_SEEK)
                _write_simple(_FLAG_ELEMENT, "NO_SEEK", context);
        if(m_flags & NodeRef::NO_PREROLL)
                _write_simple(_FLAG_ELEMENT, "NO_PREROLL", context);
        if(m_flags & NodeRef::NO_STOP)
                _write_simple(_FLAG_ELEMENT, "NO_STOP", context);
        if(m_flags & NodeRef::NO_ROSTER_WATCH)
                _write_simple(_FLAG_ELEMENT, "NO_ROSTER_WATCH", context);
        if(m_flags & NodeRef::NO_POSITION_REPORTING)
                _write_simple(_FLAG_ELEMENT, "NO_POSITION_REPORTING", context);
                
        // write transport settings
        if(m_runMode > 0) {
                switch(m_runMode) {
                        case BMediaNode::B_OFFLINE:
                                _write_simple(_RUN_MODE_ELEMENT, "B_OFFLINE", context);
                                break;
                        case BMediaNode::B_DECREASE_PRECISION:
                                _write_simple(_RUN_MODE_ELEMENT, "B_DECREASE_PRECISION", context);
                                break;
                        case BMediaNode::B_INCREASE_LATENCY:
                                _write_simple(_RUN_MODE_ELEMENT, "B_INCREASE_LATENCY", context);
                                break;
                        case BMediaNode::B_DROP_DATA:
                                _write_simple(_RUN_MODE_ELEMENT, "B_DROP_DATA", context);
                                break;
                        case BMediaNode::B_RECORDING:
                                _write_simple(_RUN_MODE_ELEMENT, "B_RECORDING", context);
                                buffer = "";
                                buffer << m_recordingDelay;
                                _write_simple(_RECORDING_DELAY_ELEMENT, buffer.String(), context);
                                break;
                        default:
                                buffer = "";
                                buffer << m_runMode;
                                _write_simple(_RUN_MODE_ELEMENT, buffer.String(), context);
                }
        }

        if(m_cycle) {
                context.beginElement(_CYCLE_ELEMENT);
                context.endElement();
        }

        BPath p;
        if(
                m_entry.InitCheck() == B_OK &&
                m_entry.GetPath(&p) == B_OK)
                _write_simple(_REF_ELEMENT, p.Path(), context);

}

void DormantNodeIO::xmlExportEnd(
        ExportContext&                                          context) const {

        // finish
        context.endElement();
}


// -------------------------------------------------------- //
// IMPORT:
// -------------------------------------------------------- //

//inline void _read_node_kind(
//      int64& ioKind,
//      const char* data,
//      ImportContext& context) {
//
//      if(!strcmp(data, "B_BUFFER_PRODUCER"))
//              ioKind |= B_BUFFER_PRODUCER;
//      else if(!strcmp(data, "B_BUFFER_CONSUMER"))
//              ioKind |= B_BUFFER_CONSUMER;
//      else if(!strcmp(data, "B_TIME_SOURCE"))
//              ioKind |= B_TIME_SOURCE;
//      else if(!strcmp(data, "B_CONTROLLABLE"))
//              ioKind |= B_CONTROLLABLE;
//      else if(!strcmp(data, "B_FILE_INTERFACE"))
//              ioKind |= B_FILE_INTERFACE;
//      else if(!strcmp(data, "B_ENTITY_INTERFACE"))
//              ioKind |= B_ENTITY_INTERFACE;
//      else if(!strcmp(data, "B_PHYSICAL_INPUT"))
//              ioKind |= B_PHYSICAL_INPUT;
//      else if(!strcmp(data, "B_PHYSICAL_OUTPUT"))
//              ioKind |= B_PHYSICAL_OUTPUT;
//      else if(!strcmp(data, "B_SYSTEM_MIXER"))
//              ioKind |= B_SYSTEM_MIXER;
//      else {
//              BString err;
//              err << "_read_noderef_kind(): unknown node kind '" << data << "'\n";
//              context.reportWarning(err.String());
//      }
//}

inline void _read_noderef_flag(
        int32& ioFlags,
        const char* data,
        ImportContext& context) {

        if(!strcmp(data, "NO_START_STOP"))
                ioFlags |= NodeRef::NO_START_STOP;
        else if(!strcmp(data, "NO_SEEK"))
                ioFlags |= NodeRef::NO_SEEK;
        else if(!strcmp(data, "NO_PREROLL"))
                ioFlags |= NodeRef::NO_PREROLL;
        else if(!strcmp(data, "NO_STOP"))
                ioFlags |= NodeRef::NO_STOP;
        else if(!strcmp(data, "NO_ROSTER_WATCH"))
                ioFlags |= NodeRef::NO_ROSTER_WATCH;
        else if(!strcmp(data, "NO_POSITION_REPORTING"))
                ioFlags |= NodeRef::NO_POSITION_REPORTING;      
        else {
                BString err;
                err << "_read_noderef_flag(): unknown node flag '" << data << "'\n";
                context.reportWarning(err.String());
        }
}

inline void _read_run_mode(
        int32& runMode,
        const char* data,
        ImportContext& context) {

        if(!strcmp(data, "B_OFFLINE"))
                runMode = BMediaNode::B_OFFLINE;
        else if(!strcmp(data, "B_DECREASE_PRECISION"))
                runMode = BMediaNode::B_DECREASE_PRECISION;
        else if(!strcmp(data, "B_INCREASE_LATENCY"))
                runMode = BMediaNode::B_INCREASE_LATENCY;
        else if(!strcmp(data, "B_DROP_DATA"))
                runMode = BMediaNode::B_DROP_DATA;
        else if(!strcmp(data, "B_RECORDING"))
                runMode = BMediaNode::B_RECORDING;
        else {
                BString err;
                err << "_read_run_mode(): unknown run mode '" << data << "'\n";
                context.reportWarning(err.String());
        }
}

inline void _read_entry(
        BEntry& entry,
        const char* data,
        ImportContext& context) {
        
        entry_ref r;
        status_t err = get_ref_for_path(data, &r);
        if(err < B_OK) {
                BString text;
                text << "_read_entry_ref(): get_ref_for_path('" << data << "') failed:\n"
                        "   " << strerror(err) << "\n";
                context.reportWarning(text.String());
        }
        
        entry.SetTo(&r);
}


void DormantNodeIO::xmlImportBegin(
        ImportContext&                                          context) { TOUCH(context); }

void DormantNodeIO::xmlImportAttribute(
        const char*                                                             key,
        const char*                                                             value,
        ImportContext&                                          context) {

        if(!strcmp(key, "key")) {
                m_nodeKey = value;
        }
        else {
                BString err;
                err << "DormantNodeIO: unknown attribute '" << key << "'\n";
                context.reportError(err.String());              
        }       
}

void DormantNodeIO::xmlImportContent(
        const char*                                                             data,
        uint32                                                                          length,
        ImportContext&                                          context) { TOUCH(data); TOUCH(length); TOUCH(context); }

void DormantNodeIO::xmlImportChild(
        IPersistent*                                                    child,
        ImportContext&                                          context) {
        
        StringContent* obj = dynamic_cast<StringContent*>(child);
        if(!obj) {
                BString err;
                err << "DormantNodeIO: unexpected element '" <<
                        context.element() << "'\n";
                context.reportError(err.String());
                return;                 
        }
        
        if(!strcmp(context.element(), _NAME_ELEMENT))
                m_dormantName = obj->content;
        else if(!strcmp(context.element(), _KIND_ELEMENT))
                _read_node_kind(m_kinds, obj->content.String(), context);
        else if(!strcmp(context.element(), _FLAVOR_ID_ELEMENT))
                m_flavorID = atol(obj->content.String());
        else if(!strcmp(context.element(), _FLAG_ELEMENT))
                _read_noderef_flag(m_flags, obj->content.String(), context);
        else if(!strcmp(context.element(), _RUN_MODE_ELEMENT))
                _read_run_mode(m_runMode, obj->content.String(), context);
        else if(!strcmp(context.element(), _RECORDING_DELAY_ELEMENT))
                m_recordingDelay = strtoll(obj->content.String(), 0, 10);
        else if(!strcmp(context.element(), _CYCLE_ELEMENT))
                m_cycle = true;
        else if(!strcmp(context.element(), _REF_ELEMENT))
                _read_entry(m_entry, obj->content.String(), context);
        else {
                BString err;
                err << "DormantNodeIO: unexpected element '" << 
                        context.element() << "'\n";
                context.reportError(err.String());
        }

        delete child;
}
                
void DormantNodeIO::xmlImportComplete(
        ImportContext&                                          context) { TOUCH(context); } //nyi; +++++ final checks?


// END -- DormantNodeIO.cpp --