/*
 Copyright (C) 2002 Andreas Thiede ( a.thiede@berlin.de )

 This program is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2 of the License, or
 (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

#include <IO/Manager/IOManager.hpp>
#include <IO/Manager/IONode.hpp>
#include <IO/Manager/IOStream.hpp>
#include <IO/Manager/IODictionary.hpp>
#include <IO/Manager/IOString.hpp>
#include <util/Loader.h>

#include <assert.h>
#include <sys/types.h>
#include <dirent.h>


extern const char * MakePluginPath();

static IOManager * theIOManager = 0;

static int           gArgc = 0;
static const char ** gArgv = 0; 

IOManager::IOManager()
{
    assert( theIOManager == 0 );

    theIOManager = this;
    
    init();
    
}

IOManager::IOManager( const IOManager & )
{
    assert( "IOManager::IOManager( const IOManager & ) should not be called" == 0 );
}

IOManager::~IOManager()
{
    assert( theIOManager != 0 );
    
    finit();
    
    theIOManager = 0;

}

IOManager & IOManager::instance()
{
    if( theIOManager == 0 )
        new IOManager();
        
    assert( theIOManager != 0 );
    
    return *theIOManager;
}

IOManager::DeviceList & IOManager::getDeviceList()
{
    return mDevices;
}

void IOManager::init()
{
}

void IOManager::finit()
{
}

bool IOManager::registerObject( IOObject  * object )
{
    IOObject * o = findObject( object->getName() );
    
    if( o ) {
        // raise error
        return false;
    }

    object->retain();
    
    mObjects.push_back( object );

    // printf( "registered object %s\n", object->getName().c_str()  );
    
    return true;
}

bool IOManager::registerNode ( IONode  * node )
{
    IONode * n = findNode( node->getName(), node->getDeviceID() );
    
    if( n ) {
        // raise error
        return false;
    }

    node->retain();
    
    mDevices.push_back( node );

    // printf( "registered node %s\n", node->getName().c_str()  );
    
    return true;
}

bool IOManager::registerStream ( IOStream  * stream )
{
    IOStream * s = findStream( stream->getName() );
    
    if( s ) {
        // raise error
        return false;
    }

    stream->retain();
    
    mStreams.push_back( stream );

    // printf( "registered stream %s\n", stream->getName().c_str()  );

    return true;

}

bool IOManager::unregisterObject( IOObject  * object )
{
    IOObject * o = findObject( object->getName() );
    
    if( o ) {

        mObjects.remove( object );
        
        object->release();
        
        return true;
    }

    return false;
}

bool IOManager::unregisterNode ( IONode  * node )
{
    IONode * n = findNode( node->getName(), node->getDeviceID() );
    
    if( n ) {

        mDevices.remove( node );
        node->release();
        return true;
    }

    return false;
}

bool IOManager::unregisterStream ( IOStream  * stream )
{
    IOStream * s = findStream( stream->getName() );
    
    if( s ) {
        mStreams.remove( stream );
        stream->release();
        return true;
    }

    return false;

}
        
IONode  * IOManager::openNode( const string  & name, int minor )
{	
    string deviceName = name;
    
    char tmp[ 1024 ];
        
    strcpy( tmp, name.c_str() );
    
    char * dot = strchr( tmp, '.' );
    
    if( dot != 0 ) {

        * dot = 0;
        
        string pluginName = tmp;
        
        pluginName += "Plugin";
        
        LoadPlugin( "", pluginName );
        
        dot ++;
        
        deviceName = dot;
    }
    
    IONode * n = findNode( deviceName, minor );

    if( n == 0 ) {
        // raise error
        printf("Could not find node\n");
    }
    else {
        n->retain();
    }
    
    return n;
}

IOStream  * IOManager::openStream( const string  & name )
{
    IOStream * s = findStream( name) ;

    if( s == 0 ) {
        // raise error
    }
    else {
        s->retain();
    }
    
    return s;
}

void IOManager::closeNode( IONode * node )
{
    if( node != 0 ) {

        node->release();
    }
}

void IOManager::closeStream( IOStream * stream )
{
    if( stream != 0 ) {
        stream->release();
    }
}
        
IOStream* IOManager::findStream( const string & aName )
{
    StreamList::iterator iter( mStreams.begin() );
    
    while( iter != mStreams.end() ) {
    
        IOStream * s = *iter;
        
        if( s->getName() == aName ) {
            return s;
        }
        iter++;
    }
    
    // raise error
    return 0;
    
}

IOObject * IOManager::findObject( const string & aName  )
{
    ObjectList::iterator iter( mObjects.begin() );
    
    while( iter != mObjects.end() ) {
    
        IOObject * o = *iter;

        //printf("search(%s,%d) this(%s,%d)\n", aName.c_str(), minor, n->getName().c_str(), n->getDeviceID() );
        
        if( o->getName() == aName) {
        
                return o;
        }
        iter++;
    }
    
    // raise error
    return 0;
    
}

IONode * IOManager::findNode( const string & aName, int minor )
{
    DeviceList::iterator iter( mDevices.begin() );
    
    while( iter != mDevices.end() ) {
    
        IONode * n = *iter;
        
        if( n->getName() == aName) {
            // printf("node = %s, minor = %d\n", aName.c_str(), minor);
        
            if( n->getDeviceID() == (unsigned) minor || minor == -1 ) {
                return n;
            }
        }
        iter++;
    }
    
    // raise error
    return 0;
    
}

IOString * IOManager::findAlias( const string & aName )
{
    StringList::iterator iter( mAliases.begin() );
    
    while( iter != mAliases.end() ) {
    
        IOString * n = *iter;
        
        if( n->getName() == aName ) {
            return n;
        }
        iter++;
    }
    
    // raise error
    return 0;
}
        
void IOManager::setAlias( const string & aliasName, const string & realName )
{
    IOString  * s = findAlias( aliasName );
    
    if( s ) {
        s->setString( realName );
    }
    else {
        s = new IOString( aliasName, realName );
        
        mAliases.push_back( s );
    }
}
 
void IOManager::operator=(const IOManager & )
{
}

bool IOManager::sendCommand( const string & nodeName, const string & command, 
                             const string & param1, const string & param2, const string & param3, const string & param4 )
{
    // printf("IOManager::command for(%s) is(%s) param1(%s)\n", nodeName.c_str() , command.c_str() , param1.c_str() );

    if( nodeName == "IOManager" ) {
    
        if( command == "quit" ) {

            DeviceList::iterator iter( mDevices.begin() );
    
            while( iter != mDevices.end() ) {
    
                IONode * n = *iter;

                if( n ) {
        
                    n->command("beforeExit");
                }
                iter++;
            }   
            exit(0); 
        }
    }
    else {
    
        IONode * node = findNode( nodeName );

        if( node == 0 ) {
    
            IOString * alias = findAlias( nodeName );
    
            if( alias ) {
                node = findNode( alias->getString() );
            }
        }
    
        if( node ) {
            return node->command( command, param1, param2, param3, param4 );
        }
        else {
        
            IOObject * obj = findObject( nodeName );
            
            if( obj ) {
                return obj->command( command, param1, param2, param3, param4 );
            }
        }
        
        printf("Node(%s) not found\n", nodeName.c_str() );
    }
    
    return false;
}

string IOManager::getValue( const string & nodeName, const string & valueName )
{
    IONode * node = findNode( nodeName );
    
    if( node == 0 ) {	
        
        IOString * alias = findAlias( nodeName );
        
        if( alias ) {
            node = findNode( alias->getString() );
        }
    }
    
    if( node ) {
        return node->getValue( valueName );
    }
    else {

        IOObject * obj = findObject(nodeName );

        if( obj != 0 ) {
            return obj->getValue( valueName );
        }
    }
    return "";
}

void IOManager::LoadPlugin( const string & path, const string & name )
{
    char buf[256];

    strcpy( buf, name.c_str() );

    char * p = strstr( buf, ".bundle" );
    
    if( p ) {	
        * p = 0;
    }
    
    printf("IOManager::LoadPlugin %s\n", buf );
 
    bool result = loadPlugin( buf, gArgc, gArgv );
    
    if( result ) {
        printf("IOManager::LoadPlugin %s succeded\n",  buf );
    }
    else {
        printf("IOManager::LoadPlugin %s failed\n",  buf );
    }
}

void IOManager::start()
{

    DeviceList::iterator iter( mDevices.begin() );
    
    while( iter != mDevices.end() ) {
    
        IONode * n = *iter;

        if( n ) {
        
            n->command("beforeRunning");
        }
        iter++;
    }    
}

void IOManager::stop()
{

    DeviceList::iterator iter( mDevices.begin() );
    
    while( iter != mDevices.end() ) {
    
        IONode * n = *iter;

        if( n ) {
        
            n->command("beforeExit");
        }
        iter++;
    }    
}

extern "C" int initIO( int argc, const char ** argv )
{
    gArgc = argc;
    gArgv = argv;
    
    IOManager::instance().start();
    
    return( theIOManager != 0 );
}