// Logger.java

package ed.log;

import java.util.*;

import ed.js.*;
import ed.js.func.*;
import ed.js.engine.*;
import ed.lang.*;

public class Logger extends JSFunctionCalls2 {

    public static Logger getLogger( String fullName ){
        Logger l = _fullNameToLogger.get( fullName );
        if ( l != null )
            return l;


        String base = fullName;

        int idx = fullName.indexOf( "." );
        if ( idx > 0 )
            base = fullName.substring( 0 , idx );

        l = _fullNameToLogger.get( base );
        if ( l == null )
            l = new Logger( null , base );

        String pcs[] = fullName.split( "\\." );
        for ( int i=1; i<pcs.length; i++ )
            l = (Logger)l.get( pcs[i] );

        _fullNameToLogger.put( fullName , l );
        return l;
    }

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

    public Logger( Logger parent , String name ){
        _parent = parent;
        _name = name;
        _fullName = _parent == null ? name : _parent._fullName + "." + _name;
        _sentEmail = new JSDate(0);

        if ( ! _fullName.contains( "." ) )
            _fullNameToLogger.put( _fullName , this );

        if ( _parent == null )
            _appenders = new ArrayList<Appender>( _defaultAppenders );
    }

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

    public void debug( String msg ){
        log( Level.DEBUG , msg , null );
    }
    public void debug( String msg , Throwable t ){
        log( Level.DEBUG , msg , t );
    }

    public void info( String msg ){
        log( Level.INFO , msg , null );
    }
    public void info( String msg , Throwable t ){
        log( Level.INFO , msg , t );
    }

    public void error( String msg ){
        log( Level.ERROR , msg , null );
    }
    public void error( String msg , Throwable t ){
        log( Level.ERROR , msg , t );
    }

    public void fatal( String msg ){
        log( Level.FATAL , msg , null );
    }
    public void fatal( String msg , Throwable t ){
        log( Level.FATAL , msg , t );
    }

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

    public Object call( Scope s , Object oName , Object oT , Object foo[] ){
        log( Level.INFO , (oName != null) ? oName.toString() : "null" , (Throwable)oT );
        return null;
    }

    public Logger getChild( String s ){
        return (Logger)get( s );
    }

    public Object get( Object n ){
        String s = n.toString();

        if ( s.equals( "log" ) )
            return null;

        if ( s.equals( "debug" )
             || s.equals( "info" )
             || s.equals( "error" )
             || s.equals( "fatal" ) ){
            return _getLevelLogger( s );
        }
        //return Level.forName( s ).func;

        if ( s.equals( "LEVEL" ) )
            return Level.me;

        if ( s.equals( "level" ) )
            return _level;

        Object foo = super.get( n );
        if ( foo != null )
            return foo;

        if ( s.equals( "appenders" ) ){
            if ( _appenders == null )
                _appenders = new ArrayList<Appender>();
            JSArray a = new JSArray( _appenders );
            super.set( "appenders" , a );
            return a;
        }

        Logger child = new Logger( this , s );
        set( s , child );
        return child;
    }

    public Object set( Object n , Object v ){
        String s = n.toString();

        if ( s.equals( "level" ) ){
            _level = (Level)v;
            return _level;
        }

        if ( s.equals( "appenders" ) )
            throw new RuntimeException( "can't change" );

        return super.set( n , v );
    }

    JSFunction _getLevelLogger( String levelName ){
        final Level level = Level.forName( levelName );
        if ( level == null )
            throw new RuntimeException( "don't know about level [" + levelName + "]" );
        JSFunction f = _levelLoggers.get( level );
        if ( f == null ){
            f = new JSFunctionCalls2(){
                    public Object call( Scope s , Object msgObject , Object excObject , Object extra[] ){

                        if ( msgObject == null )
                            msgObject = "null";
                        
                        log( level , msgObject.toString() , (Throwable)excObject );
                        return true;
                    }

                    public Object get( Object n ){
                        if ( n.toString().equals( "__puts__" ) ||
                             n.toString().equals( "puts" ) ||
                             n.toString().equals( "print" ) )
                            return this;
                        return super.get( n );
                    }
                };
            _levelLoggers.put( level , f );
        }
        return f;
    }
    

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

    public Level getEffectiveLevel(){
        if ( _level != null )
            return _level;

        Logger t = this._parent;
        while ( t != null ){
            if ( t._level != null )
                return t._level;
            t = t._parent;
        }

        return Level.DEBUG;
    }

    public void log( Level level , String msg , Throwable throwable ){
        Level eLevel = getEffectiveLevel();
        if ( eLevel.compareTo( level ) > 0 )
            return;

        JSDate date = new JSDate();
        Thread thread = Thread.currentThread();
        
        try {
            StackTraceHolder.getInstance().fix( throwable );
        }
        catch ( Throwable t ){
            System.err.println( "couldn't fix stack trace" );
            t.printStackTrace();
        }

        Logger l = this;
        while ( l != null ){

            if ( l._appenders != null ){
                for ( int i=0; i < l._appenders.size(); i++ ){
                    try {
                        l._appenders.get(i).append( _fullName , date , level , msg , throwable , thread );
                    }
                    catch ( Throwable t ){
                        System.err.println( "error running appender" );
                        try {
                            StackTraceHolder.getInstance().fix( t );
                        }
                        catch ( Throwable tt ){
                            tt.printStackTrace();
                        }
                    
                        t.printStackTrace(); // Logger
                    }
                }
            }
            
            l = l._parent;
        }
    }

    public void setLevel( Level l ){
        _level = l;
    }

    public String getFullName(){
        return _fullName;
    }

    public String toString(){
        return "Logger:" + _fullName;
    }

    final Logger _parent;
    final String _name;
    final String _fullName;
    private JSDate _sentEmail;
    List<Appender> _appenders;
    Level _level = null;
    final Map<Level,JSFunction> _levelLoggers = Collections.synchronizedMap( new TreeMap<Level,JSFunction>() );

    private final static Map<String,Logger> _fullNameToLogger = Collections.synchronizedMap( new HashMap<String,Logger>() );
    private final static List<Appender> _defaultAppenders;
    static {
        List<Appender> lst = new ArrayList<Appender>();
        lst.add( new ConsoleAppender() );
        _defaultAppenders = Collections.unmodifiableList( lst );
    }
}

@@ -2171,17 +2171,21 @@
 null%22 , 
-(
+_make
 Throwabl
@@ -2189,11 +2189,14 @@
 able
-)oT
+( oT )
  );%0A
@@ -4203,17 +4203,21 @@
 ing() , 
-(
+_make
 Throwabl
@@ -4217,17 +4217,18 @@
 hrowable
-)
+( 
 excObjec
@@ -4228,16 +4228,18 @@
 xcObject
+ )
  );%0A    
@@ -6639,24 +6639,239 @@
 ame;%0A    %7D%0A%0A
+    Throwable _makeThrowable( Object o )%7B%0A        if ( o instanceof Throwable )%0A            return (Throwable)o;%0A%0A        JSException e = new JSException( o );%0A        e.fillInStackTrace();%0A        return e;%0A    %7D%0A%0A
     final Lo

make sure exception makes sense, or at least as close as possible