package org.khelekore.jsizer;

import java.io.DataInputStream;
import java.io.IOException;
import java.net.URLClassLoader;
import java.util.HashSet;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.commons.EmptyVisitor;

public class AsmSizer implements ClassSizer {

    private byte[] buf = new byte[64 * 1024];

    public void setup (URLClassLoader cl) {
    }

    public ClassSize size (JarFile jf, JarEntry je, String cname, JSizer sizer) {
	DataInputStream is = null;
	int size = -1;
	try {
	    is = new DataInputStream (jf.getInputStream (je));
	    size = (int)je.getSize ();
	    if (buf.length < size)
		buf = new byte[size];
	    is.readFully (buf, 0, size);
	    ClassSizeVisitor sv = new ClassSizeVisitor ();
	    ClassReader cr = new ClassReader (buf, 0, size);
	    cr.accept (sv, 0);
	    return new ClassSize (cname, sv.getSuperName (),
				  sv.getNumFields (), 
				  sv.getNumMethods (), 
				  sv.getNumMethodLines ());
	} catch (IOException e) {
	    System.err.println ("failed to read: " + je + ", size: " + size);
	    e.printStackTrace ();
	}
	return null;
    }

    private static class ClassSizeVisitor extends EmptyVisitor {
	private String superName;
	private int numFields;
	private int numMethods;
	private int numMethodLines;
	
	public void visit(int version, int access, String name,
			  String signature, String superName,
			  String[] interfaces) {
	    this.superName = superName;
	}

	public String getSuperName () {
	    return superName;
	}
	
	public int getNumFields () {
	    return numFields;
	}

	public int getNumMethods () {
	    return numMethods;
	}

	public int getNumMethodLines () {
	    return numMethodLines;
	}

	public void addLineNumbers (int numLines) {
	    numMethodLines += numLines;
	}

	public FieldVisitor visitField (int access, String name, String desc, 
					String signature, Object value) {
	    numFields++;
	    return null;
	}

	public MethodVisitor visitMethod (int access, String name, 
					  String desc, String signature, 
					  String[] exceptions) {
	    numMethods++;
	    LineCounter lc = new LineCounter (this);
	    return lc;
	}
    }

    private static class LineCounter extends EmptyVisitor {
	private ClassSizeVisitor csv;
	private Set<Integer> s = new HashSet<Integer> ();
	
	public LineCounter (ClassSizeVisitor csv) {
	    this.csv = csv;
	}

	public void visitLineNumber (int line, Label start) {
	    s.add (line);
	}

	public void visitEnd () {
	    csv.addLineNumbers (s.size ());
	}
    }
}
