package com.chatley.magicbeans;

import java.lang.reflect.*;
import java.net.URLClassLoader;
import java.net.URL;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.Enumeration;
import java.util.List;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;

public class Component extends URLClassLoader {

    private static JarClassLoader s_jcl;

    private String o_name;

    private List o_interfaces;
    private List o_conc_classes;
    private ObserverList o_unresolved;

    public Component( String p_jar , Component p_comp ) {

	super( new URL[]{} , p_comp );
	addURL( getJarURL( p_jar ) );

	if ( s_jcl == null ) {
	    s_jcl = new JarClassLoader( p_jar );
	} else {
	    s_jcl.addJar( p_jar );
	}

	//	System.out.println("created jcl/added jar");

	o_name = p_jar;
	System.out.println("file -- " + o_name );
	o_interfaces = new ArrayList();
	o_conc_classes = new ArrayList();
	o_unresolved = new ObserverList();
	interrogate();
    }


    public Component( File p_jar , Component p_comp ) {

	super( new URL[]{} , p_comp );
	addURL( getJarURL( p_jar ) );

	if ( s_jcl == null ) {
	    s_jcl = new JarClassLoader( p_jar.getPath() );
	} else {
	    s_jcl.addJar( p_jar.getPath() );
	}

	o_name = p_jar.getPath();
	System.out.println("file -- " + o_name );
	o_interfaces = new ArrayList();
	o_conc_classes = new ArrayList();
	o_unresolved = new ObserverList();
	interrogate();
    }

    public Component( String p_jar , ClassLoader p_cl ) {

	super( new URL[]{} , p_cl );
	addURL( getJarURL( p_jar ) );

	if ( s_jcl == null ) {
	    s_jcl = new JarClassLoader( p_jar );
	} else {
	    s_jcl.addJar( p_jar );
	}

	o_name = p_jar;
	System.out.println("file -- " + o_name );
	o_interfaces = new ArrayList();
	o_conc_classes = new ArrayList();
	o_unresolved = new ObserverList();
	interrogate();
    }

    public String getName() { return o_name; }

    public List getHoles() { return o_interfaces; }
    public List getPegs() { return o_conc_classes; }

    public void update( Component p_loader ) {

	List x_update = o_unresolved.clonelist();

 	for ( Iterator i = x_update.iterator() ; i.hasNext() ; ) {

	    checkClass( (String)i.next() , p_loader );
	}
    }

    private URL getJarURL( String p_jar ) {

	File x_file = new File( p_jar );
	return getJarURL( x_file );
    }

    private URL getJarURL( File p_file ) {

	try {
	    return p_file.toURL();
	} catch ( java.net.MalformedURLException p_mue ) {
	    return null;
	}
    }

    void interrogate() {

	//	JarClassLoader x_jcl = new JarClassLoader( o_name );

	//	System.out.println("beginning of interrogate");

	Enumeration x_enum = s_jcl.enumerateResources();
	
	while( x_enum.hasMoreElements() ) {
	    
	    String x_res = x_enum.nextElement().toString();
	    
	    if ( x_res.indexOf( '$' ) != -1 ) continue;   // no inner classes
	    
	    if ( x_res.endsWith( ".class" ) ) {
		
		String x_class = x_res.substring( 0 , x_res.lastIndexOf(".") );

		checkClass( x_class , null );
	    }
	}
    }

    private void checkClass( String p_class , Component p_loader ) {

	try {

	    Class x_cl;

	    if ( p_loader == null ) {
		x_cl = loadClass( p_class , false );   // was s_jcl.loadClass
	    } else {
		x_cl = p_loader.loadClass( p_class , false );   // was s_jcl.loadClass
	    }

	    //if it's an interface, add it to the hole list

	    if ( x_cl.isInterface() ) {

		Hole x_hole = new Hole( x_cl.getName() , this );

		try {

		    Field x_field = x_cl.getField( "cardinality" );
		    x_hole.setCardinality( x_field.getInt( null ) );

		} catch ( NoSuchFieldException p_nsfe ) {
		} catch ( SecurityException p_se ) {
		}

		//	System.out.println("added hole : " + x_hole.getName() );

		o_interfaces.add( x_hole );

	    } else {


		List x_interfaces = implementedInterfaces( x_cl );

		if ( x_cl.getModifiers() != Modifier.ABSTRACT && x_interfaces.size() > 0 ) {

		    o_conc_classes.add( new Peg( x_cl.getName() , this , x_interfaces ));  
		}
	    }

	    o_unresolved.remove( p_class );
		    
	} catch ( NoClassDefFoundError p_ncdf ) { 
	    
	    o_unresolved.add( p_class );

	} catch ( Exception e ) { e.printStackTrace(); 
	}
    }


//     private List implementedInterfaces( Class p_cl ) {

// 	return Arrays.asList( p_cl.getInterfaces() ); 
//     }

    private List implementedInterfaces( Class p_cl ) {
	
	return implementedInterfaces( p_cl , new ArrayList() );
    }
    
    private List implementedInterfaces( Class p_cl , List p_int ) {
	
 	if ( p_cl == null ) { return p_int; }
 	else { 
 	    p_int.addAll( Arrays.asList( p_cl.getInterfaces() )); 
 	    return implementedInterfaces( p_cl.getSuperclass() , p_int ); 
 	}
    }
    
    public String toString() { return getName(); }
    
    public boolean equals( Object o ) { return toString().equals( o.toString() ) && o instanceof Component; } 
}
