
namespace Darwin {
    
    class C extends Farm.RuleSet.SourceRule {
        
        static function constructor() { Farm.RuleSet.registerSourceHandler( new Darwin.C() ); }
        
        function types() {
            return [ 'c' ];
        }
        function build( object target, string file ) {
            string objectPath = .buildOutput( target, file );
            string flags = target.getProperty('C');
            
            if( Farm.invokeExternalProgram( "gcc $flags -o $objectPath -c $file" ) )
                return objectPath;
            return "";   
        }
        function buildOutput( object target, string file ) {
            return "${Farm.compiledObjectsDirForTarget(target)}/${(Farm.basename(file) - '.c')}.o";
        }
    }
    
    class CPP extends Farm.RuleSet.SourceRule {     
        
        static function constructor() { Farm.RuleSet.registerSourceHandler( new Darwin.CPP() ); }
        
        function types() {
            return [ 'cpp', 'cc', 'c++' ];
        }
        function build( object target, string file ) {
            string objectPath = .buildOutput( target, file );               
            string flags = target.getProperty('CPP');
            
            if( Farm.invokeExternalProgram( "g++ $flags -o $objectPath -c $file" ) )
                return objectPath;
            return "";   
        }
        function buildOutput( object target, string file ) {
            return "${Farm.compiledObjectsDirForTarget(target)}/${(Farm.basename(file) - '.cpp')}.o";
        }
    }
    
    
    class SharedLibrary extends Farm.RuleSet.BuildRule {
        
        static function constructor() { Farm.RuleSet.registerBuildHandler( new Darwin.SharedLibrary() ); }

        function type() {
            return Farm.RuleSet.SharedLibrary;
        }

        function combinationName( object target ) {
            return "${Farm.productsDirForTarget(target)}/lib${target.getProperty('name')}.${target.getProperty('version')}.dylib";
        }

        function combine( object target, array files ) {
            string objects = Array.join( files, " " );
            string flags = target.getProperty( 'LD' );
            number built = Farm.invokeExternalProgram( "gcc -dynamiclib ${'${wl}-flat_namespace ${wl}-undefined ${wl}suppress'} -o ${.combinationName(target)} $flags $objects" );
            if( built and true ) { 
                // disable this for the moment
                string c = Directory.getCurrent();
                array  vparts = String.toArray(target.getProperty('version'), '.');
                Directory.setCurrent("${Farm.productsDirForTarget(target)}");
                for( number i = 0; i < (Array.size(vparts) - 1); i++ ) {
                    Farm.invokeExternalProgram( "ln -s lib${target.getProperty('name')}.${target.getProperty('version')}.dylib " +
                                                "lib${target.getProperty('name')}.${Array.join(vparts[0..i], '.')}.dylib"); 
                }
                Farm.invokeExternalProgram( "ln -s lib${target.getProperty('name')}.${target.getProperty('version')}.dylib " +
                                            "lib${target.getProperty('name')}.dylib"); 
            }
            return built;
        }        
    }

    class ModuleLibrary extends Farm.RuleSet.BuildRule {
        
        static function constructor() { Farm.RuleSet.registerBuildHandler( new Darwin.ModuleLibrary() ); }

        function type() {
            return Farm.RuleSet.ModuleLibrary;
        }

        function combinationName( object target ) {
            return "${Farm.productsDirForTarget(target)}/${target.getProperty('name')}.so";
        }

        function combine( object target, array files ) {
            string objects = Array.join( files, " " );
            target.addProperty('LD', ' ${wl}-flat_namespace ${wl}-undefined ${wl}suppress' );
            return Farm.invokeExternalProgram( "gcc -o ${.combinationName(target)} -bundle ${target.getProperty( 'LD' )} $objects" );
        }        
    }
    
    class Executable extends Farm.RuleSet.BuildRule {
        
        static function constructor() { Farm.RuleSet.registerBuildHandler( new Darwin.Executable() ); }
        
        function type() {
            return Farm.RuleSet.Executable;
        }
        
        function combinationName( object target ) {
            return "${Farm.productsDirForTarget(target)}/${target.getProperty('name')}";
        }
        
        function combine( object target, array files ) {
            string objects = Array.join( files, " " );
            string flags = target.getProperty( 'LD' );
            return Farm.invokeExternalProgram( "gcc -o ${.combinationName(target)} $flags $objects" );
        }        
    }    
}
