// Copyright 2001 freehep
package org.freehep.graphicsio.pdf;

import java.io.IOException;
import java.util.Map;
import java.util.Hashtable;
import java.util.Vector;
import java.util.Iterator;
import java.util.Collection;
import java.util.LinkedList;

/**
 *  This class keeps track of all kinds of objects written to a pdf file
 *  and avoids to write them several times instead of referencing the same
 *  object several times.
 *  Right now only encoding tables are supported.
 *
 *  An implementation for images and paint would be possible.
 *
 *  @author Simon Fischer
 *  @version $Id: PDFRedundanceTracker.java,v 1.1.1.1 2004/05/31 05:08:52 kuromaru Exp $
 */
public class PDFRedundanceTracker {

    /**
     * To be implemented by Writers which write objects that may already have been written.
     */
    public interface Writer {
    	public void writeObject(Object o, PDFRef reference, PDFWriter pdf) throws IOException;
    }

    private class Entry {
    	private static final String REF_PREFIX = "PDF_RTObj";
    	private Object object;
    	private Writer writer;
    	private boolean written;
    	private PDFRef reference;
    	private Object groupID;
    	private Entry(Object o, Object groupID, Writer w) {
    	    this.object = o;
    	    this.groupID = groupID;
    	    this.writer = w;
    	    this.written = false;
    	    this.reference = pdf.ref(REF_PREFIX+(refCount++));
    	}
    }

    private static int refCount = 1;

    private PDFWriter pdf;
    private Map objects;
    private Vector orderedObjects;    // to keep order

    public PDFRedundanceTracker(PDFWriter pdf) {
    	this.pdf = pdf;
    	objects = new Hashtable();
    	orderedObjects = new Vector();
    }

    /** Returns a reference that points to <tt>object</tt>. When this method
     *  is called several times for the same object (according to its hash code)
     *  the same reference is returned. When <tt>writeAll()</tt> is called the writer's
     *  <tt>writeObject()</tt> method will be called once with <tt>object</tt> as
     *  argument.<br>
     *  The groupID is only used for <tt>getGroup()</tt>*/
    public PDFRef getReference(Object object, Object groupID, Writer writer) {
    	Object o = objects.get(object);
    	if (o != null) {
    	    return ((Entry)o).reference;
    	} else {
    	    Entry entry = new Entry(object, groupID, writer);
    	    objects.put(object, entry);
    	    orderedObjects.add(entry);
    	    return entry.reference;
    	}
    }

    public PDFRef getReference(Object object, Writer writer) {
    	return getReference(object, null, writer);
    }

    /** Writes all objects that are not yet written. If the method is called several times
     *  then each times only the new objects are written. */
    public void writeAll() {
    	Iterator i = orderedObjects.iterator();
    	while (i.hasNext()) {
    	    Entry entry = (Entry)i.next();
    	    if (!entry.written) {
        		try {
        		    //System.out.println("PDFRT: Writing: " + entry.object);
        		    entry.writer.writeObject(entry.object, entry.reference, pdf);
        		    entry.written = true;
        		} catch (IOException e) {
        		    e.printStackTrace();
        		}
    	    }
    	}
    }

    /** Returns all objects belonging to a particular group. */
    public Collection getGroup(Object groupID) {
    	Collection result = new LinkedList();
    	Iterator i = orderedObjects.iterator();
    	while (i.hasNext()) {
    	    Entry entry = (Entry)i.next();
    	    if (groupID.equals(entry.groupID)) {
    		    result.add(entry.object);
    		}
    	}
    	return result;
    }
}
