root/src/add-ons/media/media-add-ons/esound_sink/ESDSinkAddOn.cpp
/*
 * ESounD media addon for BeOS
 *
 * Copyright (c) 2006 François Revol (revol@free.fr)
 * 
 * Based on Multi Audio addon for Haiku,
 * Copyright (c) 2002, 2003 Jerome Duval (jerome.duval@free.fr)
 *
 * All rights reserved.
 * Redistribution and use in source and binary forms, with or without modification, 
 * are permitted provided that the following conditions are met:
 *
 * - Redistributions of source code must retain the above copyright notice, 
 *   this list of conditions and the following disclaimer.
 * - 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.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS 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.
 *
 */
#define _ZETA_TS_FIND_DIR_ 1
#include <MediaDefs.h>
#include <MediaAddOn.h>
#include <Errors.h>
#include <Node.h>
#include <Mime.h>
#include <StorageDefs.h>
#include <Path.h>
#include <Directory.h>
#include <Entry.h>
#include <FindDirectory.h>

#include "ESDSinkNode.h"
#include "ESDSinkAddOn.h"
#include "ESDEndpoint.h"

#include <limits.h>
#include <stdio.h>
#include <string.h>
//#undef DEBUG
//#define DEBUG 4
#include "debug.h"
#include <Debug.h>

//#define MULTI_SAVE

// instantiation function
extern "C" _EXPORT BMediaAddOn * make_media_addon(image_id image) {
        CALLED();
        return new ESDSinkAddOn(image);
}

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

ESDSinkAddOn::~ESDSinkAddOn()
{
        CALLED();
        
        void *device = NULL;
        for ( int32 i = 0; (device = fDevices.ItemAt(i)); i++ )
                delete (ESDEndpoint *)device;
                
        SaveSettings();
}

ESDSinkAddOn::ESDSinkAddOn(image_id image) :
        BMediaAddOn(image),
        fDevices()
{
        CALLED();
        fInitCheckStatus = B_NO_INIT;
        
        LoadSettings();
        
        if(SetupDefaultSinks()!=B_OK)
                return;
                
        fInitCheckStatus = B_OK;
}

// -------------------------------------------------------- //
// BMediaAddOn impl
// -------------------------------------------------------- //

status_t ESDSinkAddOn::InitCheck(
        const char ** out_failure_text)
{
        CALLED();
        return B_OK;
}

int32 ESDSinkAddOn::CountFlavors()
{
        CALLED();
        //return fDevices.CountItems();
        return 1;
}

status_t ESDSinkAddOn::GetFlavorAt(
        int32 n,
        const flavor_info ** out_info)
{
        CALLED();
        //if (n < 0 || n > fDevices.CountItems() - 1) {
        if (n < 0 || n > 1) {
                fprintf(stderr,"<- B_BAD_INDEX\n");
                return B_BAD_INDEX;
        }
                
        //ESDEndpoint *device = (ESDEndpoint *) fDevices.ItemAt(n);
                
        flavor_info * infos = new flavor_info[1];
        ESDSinkNode::GetFlavor(&infos[0], n);
//      infos[0].name = device->MD.friendly_name;
        (*out_info) = infos;
        return B_OK;
}

BMediaNode * ESDSinkAddOn::InstantiateNodeFor(
                                const flavor_info * info,
                                BMessage * config,
                                status_t * out_error)
{
        CALLED();
                
        BString name = "ESounD Sink";
#ifdef MULTI_SAVE
        ESDEndpoint *device = (ESDEndpoint *) fDevices.ItemAt(info->internal_id);
        if (device)
                device->GetFriendlyName(name);
        if(fSettings.FindMessage(name.String(), config)==B_OK) {
                fSettings.RemoveData(name.String());
        }
#endif
        
        
        ESDSinkNode * node
                = new ESDSinkNode(this,
                                                  (char *)name.String(),
                                                  config);
        if (node == 0) {
                *out_error = B_NO_MEMORY;
                fprintf(stderr,"<- B_NO_MEMORY\n");
        } else { 
                *out_error = node->InitCheck();
        }
        return node;    
}

status_t
ESDSinkAddOn::GetConfigurationFor(BMediaNode * your_node, BMessage * into_message)
{
        CALLED();
#ifdef MULTI_SAVE
                if (!into_message)
                        into_message = new BMessage();
                ESDSinkNode * node = dynamic_cast<ESDSinkNode*>(your_node);
                if (node == 0) {
                        fprintf(stderr,"<- B_BAD_TYPE\n");
                        return B_BAD_TYPE;
                }
                if(node->GetConfigurationFor(into_message)==B_OK) {
                        fSettings.AddMessage(your_node->Name(), into_message);
                }               
                return B_OK;
#endif  
        // currently never called by the media kit. Seems it is not implemented.
#if 0
        ESDSinkNode * node = dynamic_cast<ESDSinkNode*>(your_node);
        if (node == 0) {
                fprintf(stderr,"<- B_BAD_TYPE\n");
                return B_BAD_TYPE;
        }
        return node->GetConfigurationFor(into_message);
#endif
        return B_ERROR;
}

#if 0
bool ESDSinkAddOn::WantsAutoStart()
{
        CALLED();
        return true;//false;
}

status_t ESDSinkAddOn::AutoStart(
                                int in_count,
                                BMediaNode ** out_node,
                                int32 * out_internal_id,
                                bool * out_has_more)
{
        CALLED();
        const flavor_info *fi;
        status_t err;
        
        // XXX: LEAK!
        PRINT(("AutoStart: in_count=%d\n", in_count));
//      if (in_count < 1)
//              return EINVAL;
        *out_internal_id = 0;
        *out_has_more = false;
        err = GetFlavorAt(0, (const flavor_info **)&fi);
        if (err < 0)
                return err;
        *out_node = InstantiateNodeFor((const flavor_info *)fi, NULL, &err);
        delete fi;
        if (err < 0) 
                return err;
        return B_OK+1;
}
#endif

status_t
ESDSinkAddOn::SetupDefaultSinks()
{
        CALLED();
#if 0   
        BDirectory root;
        if(rootEntry!=NULL)
                root.SetTo(rootEntry);
        else if(rootPath!=NULL) {
                root.SetTo(rootPath);
        } else {
                PRINT(("Error in ESDSinkAddOn::RecursiveScan null params\n"));
                return B_ERROR;
        }
        
        BEntry entry;
        
        while(root.GetNextEntry(&entry) > B_ERROR) {

                if(entry.IsDirectory()) {
                        RecursiveScan(rootPath, &entry);
                } else {
                        BPath path;
                        entry.GetPath(&path);
                        ESDEndpoint *device = new ESDEndpoint(path.Path() + strlen(rootPath), path.Path());
                        if (device) {
                                if (device->InitCheck() == B_OK)
                                        fDevices.AddItem(device);
                                else
                                        delete device;
                        }
                }
        }
        
#endif
        return B_OK;
}


void
ESDSinkAddOn::SaveSettings(void)
{
        CALLED();
        BPath path;
        if(find_directory(B_USER_SETTINGS_DIRECTORY, &path) == B_OK) {
                path.Append(SETTINGS_FILE);
                BFile file(path.Path(),B_READ_WRITE|B_CREATE_FILE|B_ERASE_FILE);
                if(file.InitCheck()==B_OK)
                        fSettings.Flatten(&file);
        }
}


void
ESDSinkAddOn::LoadSettings(void)
{
        CALLED();
        fSettings.MakeEmpty();
        
        BPath path;
        if(find_directory(B_USER_SETTINGS_DIRECTORY, &path) == B_OK) {
                path.Append(SETTINGS_FILE);
                BFile file(path.Path(),B_READ_ONLY);
                if((file.InitCheck()==B_OK)&&(fSettings.Unflatten(&file)==B_OK))
                {
                        //fSettings.PrintToStream();
                } else {
                        PRINT(("Error unflattening settings file %s\n",path.Path()));
                }       
        }
}