
package uk.ac.sanger.pathogens.embl;

import java.util.*;

import uk.ac.sanger.pathogens.StringVector;
import uk.ac.sanger.pathogens.ReadOnlyException;
import uk.ac.sanger.pathogens.OutOfRangeException;

public class BioJavaFeature extends EMBLObject implements ComparableFeature
{
    private org.biojava.bio.seq.Feature feature;

    private Entry           nativeEntry;
    private Key             nativeKey;
    private Location        nativeLocation;
    private QualifierVector nativeQualifiers;

    private boolean         featureIsStale = false;

    private static long     id_counter = 0;
    private final long      id = id_counter++;

    public BioJavaFeature(org.biojava.bio.seq.Feature feature)
    {
	this.feature = feature;

	nativeLocation   = makeNativeLocation(feature);
	nativeQualifiers = makeNativeAnnotation(feature);
	nativeKey        = new Key(feature.getType());
    }

    public void set(Key key, Location loc, QualifierVector qualifiers)
	throws EntryInformationException, ReadOnlyException, OutOfRangeException
    {
	throw new ReadOnlyException("This feature is read-only");
    }

    public Key getKey()
    {
	if (featureIsStale)
	    nativeKey = new Key(feature.getType());

	return nativeKey;
    }

    public long getNumericID()
    {
	return id;
    }

    public Feature copy()
    {
	return new BioJavaFeature(feature);
    }

    public Location getLocation()
    {
	if (featureIsStale)
	    nativeLocation = makeNativeLocation(feature);

	return nativeLocation;
    }

    public Entry getEntry()
    {
	return nativeEntry;
    }

    public int getFirstBase()
    {
  	if (featureIsStale)
  	    nativeLocation = makeNativeLocation(feature);

	return nativeLocation.getFirstBase();
    }

    public int getLastBase()
    {
  	if (featureIsStale)
  	    nativeLocation = makeNativeLocation(feature);

	return nativeLocation.getLastBase();
    }

    public Qualifier getQualifierByName(String name)
	throws InvalidRelationException
    {
  	if (featureIsStale)
  	    nativeQualifiers = makeNativeAnnotation(feature);

	return nativeQualifiers.getQualifierByName(name);
    }

    public QualifierVector getQualifiers()
    {
  	if (featureIsStale)
  	    nativeQualifiers = makeNativeAnnotation(feature);

	return nativeQualifiers;
    }

    public Object getUserData()
    {
	return null;
    }

    public void removeQualifierByName(String name)
	throws EntryInformationException, ReadOnlyException
    {
	throw new ReadOnlyException("This feature is read-only");
    }

    public void setKey(Key key)
	throws EntryInformationException, ReadOnlyException
    {
	throw new ReadOnlyException("This feature is read-only");
    }

    public void setLocation(Location loc)
	throws OutOfRangeException, ReadOnlyException
    {
	throw new ReadOnlyException("This feature is read-only");
    }

    public void setQualifier(Qualifier qual)
	throws EntryInformationException, ReadOnlyException
    {
	throw new ReadOnlyException("This feature is read-only");
    }

    public void setQualifiers(QualifierVector qualifiers)
	throws EntryInformationException, ReadOnlyException
    {
	throw new ReadOnlyException("This feature is read-only");
    }

    public boolean isReadOnly () {
        return true;
    }

    private Location makeNativeLocation(org.biojava.bio.seq.Feature feature)
    {
	org.biojava.bio.symbol.Location locs = feature.getLocation();

	boolean complement = false;

	if (org.biojava.bio.seq.StrandedFeature.class.isInstance(feature))
	    if (((org.biojava.bio.seq.StrandedFeature) feature).getStrand().getValue() == -1)
		complement = true;

	RangeVector ranges = new RangeVector();
	for (Iterator li = locs.blockIterator(); li.hasNext();)
	{
	    org.biojava.bio.symbol.Location thisLoc =
		(org.biojava.bio.symbol.Location) li.next();

	    int min = (thisLoc.getMin() == Integer.MIN_VALUE) ?
		1                                             :
		thisLoc.getMin();

	    int max = (thisLoc.getMax() == Integer.MAX_VALUE) ?
		getEntry().getSequence().length()             :
		thisLoc.getMax();

	    try
	    {
		if (min != max)
		{
		    if (complement)
		    {
			ranges.insertElementAt(new Range(min, max), 0);
		    }
		    else
		    {
			ranges.add(new Range(min, max));
		    }
		}
		else
		{
		    if (complement)
		    {
			ranges.insertElementAt(new Range(min), 0);
		    }
		    else
		    {
			ranges.add(new Range(min));
		    }
		}
	    }
	    catch (OutOfRangeException ore)
	    {
		System.err.println("Error converting BioJava range: "
				   + ore.getMessage());
	    }
	}
	return new Location(ranges, complement);
    }

    private QualifierVector makeNativeAnnotation(org.biojava.bio.seq.Feature feature)
    {
	org.biojava.bio.Annotation annotation = feature.getAnnotation();

	QualifierVector qualifiers = new QualifierVector();

	List keys = new ArrayList();
	keys.addAll(annotation.keys());
	Collections.sort(keys);

	for (Iterator ki = keys.iterator(); ki.hasNext();)
	{
	    Object   key = ki.next();
	    Object value = annotation.getProperty(key);

	    if (key.equals(org.biojava.bio.seq.Feature.PROPERTY_DATA_KEY))
		continue;

	    StringVector sv = new StringVector();

	    if (Collection.class.isInstance(value))
	    {
		for (Iterator vi = ((Collection) value).iterator(); vi.hasNext();)
		{
		    sv.add(vi.next().toString());
		}
	    }
	    else
	    {
		sv.add(value.toString());
	    }

	    qualifiers.addQualifierValues(new Qualifier(key.toString(), sv));
	}
	return qualifiers;
    }
}
