package org.geotools.data.ogr;

import java.io.IOException;

import org.gdal.gdal.gdal;
import org.gdal.ogr.Layer;
import org.geotools.data.DataSourceException;
import org.geotools.data.DataUtilities;
import org.geotools.data.FeatureWriter;
import org.geotools.data.jdbc.MutableFIDFeature;
import org.geotools.feature.DefaultFeatureType;
import org.geotools.feature.Feature;
import org.geotools.feature.FeatureType;
import org.geotools.feature.IllegalAttributeException;

import com.vividsolutions.jts.geom.GeometryFactory;

/**
 * OGR feature writer leveraging OGR capabilities to rewrite a file using random
 * access and in place deletes
 * 
 * @author aaime
 */
public class OGRDirectFeatureWriter implements FeatureWriter {

	private OGRFeatureReader reader;

	private FeatureType featureType;

	private Feature original;

	private MutableFIDFeature live;

	private Layer layer;

	private FeatureMapper mapper;
	
	private boolean deletedFeatures;

	/**
	 * Creates a new direct OGR feature writer, with the specified
	 * OGRFeatureReader and destination layer (it may be a different layer from
	 * the one the reader is working against)
	 * 
	 * @param reader
	 * @param featureType
	 * @param layer
	 */
	public OGRDirectFeatureWriter(OGRFeatureReader reader) {
		this.reader = reader;
		this.featureType = reader.getFeatureType();
		this.layer = reader.layer;
		this.mapper = new FeatureMapper(new GeometryFactory());
		this.deletedFeatures = false;
	}

	public void close() throws IOException {
		if (reader != null) {
			original = null;
			live = null;
			if("ESRI Shapefile".equals(reader.ds.GetDriver().getName()) && deletedFeatures)
				reader.ds.ExecuteSQL("REPACK " + reader.layer.GetName(), null, null);
			reader.layer.SyncToDisk();
			reader.close();
		}
	}

	public FeatureType getFeatureType() {
		return featureType;
	}

	public boolean hasNext() throws IOException {
		return reader.hasNext();
	}

	public Feature next() throws IOException {
		if (live != null) {
			write();
		}
		
		try {
			if(reader.hasNext()) {
				original = reader.next();
				live = new MutableFIDFeature((DefaultFeatureType) original.getFeatureType(), original
						.getAttributes(new Object[original.getFeatureType().getAttributeCount()]),
						original.getID());
			} else {
				original = null;
				live = new MutableFIDFeature((DefaultFeatureType) featureType, 
						DataUtilities.defaultValues(featureType), null);
			}
			
			return live;
		} catch (IllegalAttributeException e) {
			throw new DataSourceException("Could not build next feature", e);
		}
	}

	public void remove() throws IOException {
		int ogrId = mapper.convertGTFID(original);
		if(layer.DeleteFeature(ogrId) != 0) {
			throw new IOException(gdal.GetLastErrorMsg());
		}
		deletedFeatures = true;
	}

	public void write() throws IOException {
		if (live == null)
			throw new IOException("No current feature to write");

		// this will return true only in update mode, otherwise original is null
		boolean changed = live.equals(original);
		if (!changed && original != null && layer == reader.layer) {
			// nothing to do, just skip
		} else if (changed && original != null && layer == reader.layer) {
			// not equals, we're updating an existing one
			layer.SetFeature(mapper.convertGTFeature(layer.GetLayerDefn(), live));
		} else {
			org.gdal.ogr.Feature ogrFeature = mapper.convertGTFeature(layer.GetLayerDefn(),
					original != null ? original : live);
			layer.CreateFeature(ogrFeature);
			live.setID(mapper.convertOGRFID(featureType, ogrFeature));
			ogrFeature.delete();
		}

		// reset state
		live = null;
		original = null;
	}

}
