/*
 *    GeoTools - OpenSource mapping toolkit
 *    http://geotools.org
 *    (C) 2002-2006, GeoTools Project Managment Committee (PMC)
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License as published by the Free Software Foundation;
 *    version 2.1 of the License.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 */
package org.geotools.data.property;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.NoSuchElementException;

import junit.framework.TestCase;

import org.geotools.data.DataUtilities;
import org.geotools.data.DefaultQuery;
import org.geotools.data.DefaultTransaction;
import org.geotools.data.FeatureReader;
import org.geotools.data.FeatureSource;
import org.geotools.data.FeatureStore;
import org.geotools.data.Query;
import org.geotools.data.Transaction;
import org.geotools.factory.CommonFactoryFinder;
import org.geotools.feature.AttributeType;
import org.geotools.feature.Feature;
import org.geotools.feature.FeatureCollection;
import org.geotools.feature.FeatureIterator;
import org.geotools.feature.FeatureType;
import org.geotools.feature.IllegalAttributeException;
import org.opengis.filter.Filter;
import org.opengis.filter.FilterFactory2;

/**
 * Test functioning of PropertyDataStore.
 * 
 * @author Jody Garnett, Refractions Research Inc.
 * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/demo/property/src/test/java/org/geotools/data/property/PropertyDataStoreTest.java $
 */
public class PropertyDataStoreTest extends TestCase {
    PropertyDataStore store;
    
    static FilterFactory2 ff = (FilterFactory2) CommonFactoryFinder.getFilterFactory(null);

    /**
     * Constructor for SimpleDataStoreTest.
     * @param arg0
     */
    public PropertyDataStoreTest(String arg0) {
        super(arg0);
    }
    protected void setUp() throws Exception {
        File dir = new File(".", "propertyTestData" );
        dir.mkdir();
               
        File file = new File( dir ,"road.properties");
        if( file.exists()){
            file.delete();
        }        
        BufferedWriter writer = new BufferedWriter( new FileWriter( file ) );
        writer.write("_=id:Integer,name:String"); writer.newLine();
        writer.write("fid1=1|jody"); writer.newLine();
        writer.write("fid2=2|brent"); writer.newLine();
        writer.write("fid3=3|dave"); writer.newLine();
        writer.write("fid4=4|justin");
        writer.close();
        store = new PropertyDataStore( dir );
        super.setUp();
    }
    protected void tearDown() throws Exception {
        File dir = new File( "propertyTestData" );
        File list[]=dir.listFiles();
        for( int i=0; i<list.length;i++){
            list[i].delete();
        }
        dir.delete();
        super.tearDown();                
    }

    public void testGetTypeNames() {
        String names[] = store.getTypeNames();
        assertEquals( 1, names.length );
        assertEquals( "road", names[0] );                
    }

    public void testGetSchema() throws IOException {
        FeatureType type = store.getSchema( "road" );
        assertNotNull( type );
        assertEquals( "road", type.getTypeName() );
        assertEquals( "propertyTestData", type.getNamespace().toString() );
        assertEquals( 2, type.getAttributeCount() );
        
        AttributeType id = type.getAttributeType(0);        
        AttributeType name = type.getAttributeType(1);
        
        assertEquals( "id", id.getName() );
        assertEquals( "class java.lang.Integer", id.getType().toString() );
                
        assertEquals( "name", name.getName() );
        assertEquals( "class java.lang.String", name.getType().toString() );                        
    }
    public void testGetFeaturesFeatureTypeFilterTransaction1() throws Exception {
        FeatureType type = store.getSchema( "road" );
        Query roadQuery = new DefaultQuery("road");
        FeatureReader reader = store.getFeatureReader( roadQuery, Transaction.AUTO_COMMIT );
        int count = 0;
        try {
            while( reader.hasNext() ){
                reader.next();
                count++;
            }
        }
        finally {
            reader.close();
        }
        assertEquals( 4, count );
        
        Filter selectFid1;
        
        selectFid1 = ff.id( Collections.singleton( ff.featureId("fid1") ) );
        reader = store.getFeatureReader( new DefaultQuery("road", selectFid1 ), Transaction.AUTO_COMMIT );
        assertEquals( 1, count( reader ) );
        
        Transaction transaction = new DefaultTransaction();
        reader = store.getFeatureReader( roadQuery, transaction );
        assertEquals( 4, count( reader ));
        
        reader = store.getFeatureReader( roadQuery, transaction );
        List list = new ArrayList();
        try {
            while( reader.hasNext() ){
                list.add( reader.next().getID() );
            }
        }
        finally {
            reader.close();
        }
        assertEquals( "[fid1, fid2, fid3, fid4]", list.toString() );        
    }
    /*
     * Test for FeatureReader getFeatureReader(String)
     */
    public void testGetFeatureReaderString() throws NoSuchElementException, IOException, IllegalAttributeException {
        FeatureReader reader = store.getFeatureReader("road");
        int count = 0;
        try {
            while( reader.hasNext() ){
                reader.next();                
                count++;
            }
        }
        finally {
            reader.close();
        }
        assertEquals( 4, count );
    }
    private int count( FeatureReader reader ) throws Exception {
        int count = 0;
        try {
            while( reader.hasNext() ){
                reader.next();
                count++;
            }
        }
        finally {
            reader.close();
        }
        return count;        
    }    
    private int count( String typeName ) throws Exception {
        return count( store.getFeatureReader( typeName ) );                
    }
    
    public void testWriterSkipThrough() throws Exception {
        PropertyFeatureWriter writer = (PropertyFeatureWriter)
            store.getFeatureWriter("road");
            
        File in = writer.read;
        File out = writer.write;
        
        int count = 0;
        while( writer.hasNext() ){
            writer.next();
            count++;
        }
        assertEquals( 4, count );
        assertTrue( in.exists() );
        assertTrue( out.exists() );
        writer.close();
        assertTrue( in.exists() );        
        
        assertEquals( 4, count( "road" ) );
    }
    public void testWriterChangeName() throws Exception{
        PropertyFeatureWriter writer = (PropertyFeatureWriter)
            store.getFeatureWriter("road");
            
        int count = 0;
        while( writer.hasNext() ){
            Feature f = writer.next();
            f.setAttribute(1,"name "+(count+1));
            writer.write();
            count++;
        }                
        writer.close();        
        assertEquals( 4, count );
        assertEquals( 4, count( "road" ));                
    }
    public void testWriterChangeFirstName() throws Exception{
        PropertyFeatureWriter writer = (PropertyFeatureWriter)
                    store.getFeatureWriter("road");
        Feature f;
        f = writer.next();
        f.setAttribute(1,"changed");
        writer.write();
        writer.close();                           
        assertEquals( 4, count( "road" ));    
    }
    public void testWriterChangeLastName() throws Exception{
        PropertyFeatureWriter writer = (PropertyFeatureWriter)
                    store.getFeatureWriter("road");
        Feature f;
        writer.next();
        writer.next();
        writer.next();        
        f = writer.next();
        f.setAttribute(1,"changed");
        writer.write();
        writer.close();                           
        assertEquals( 4, count( "road" ));    
    }    
    public void testWriterChangeAppend() throws Exception{
        PropertyFeatureWriter writer = (PropertyFeatureWriter)
                    store.getFeatureWriter("road");
        Feature f;
        writer.next();
        writer.next();
        writer.next();
        writer.next();
        assertFalse( writer.hasNext() );
        f = writer.next();
        assertNotNull( f );
        f.setAttribute(0,new Integer(-1));        
        f.setAttribute(1,"new");
        writer.write();
        writer.close();
        assertEquals( 5, count( "road" ));    
    }
    public void testWriterChangeRemoveFirst() throws Exception{
        PropertyFeatureWriter writer = (PropertyFeatureWriter)
                    store.getFeatureWriter("road");
        
        writer.next();
        writer.remove();
        writer.close();
        assertEquals( 3, count( "road" ));    
    }
    public void testWriterChangeRemoveLast() throws Exception{
        PropertyFeatureWriter writer = (PropertyFeatureWriter)
                    store.getFeatureWriter("road");
        
        writer.next();
        writer.next();
        writer.next();
        writer.remove();
        writer.close();
        assertEquals( 3, count( "road" ));    
    }
    public void testWriterChangeRemoveAppend() throws Exception{
        PropertyFeatureWriter writer = (PropertyFeatureWriter)
                    store.getFeatureWriter("road");
        Feature f;
        writer.next();
        writer.next();
        writer.next();
        writer.next();        
                
        assertFalse( writer.hasNext() );
        f = writer.next();
        assertNotNull( f );
        f.setAttribute(0,new Integer(-1));        
        f.setAttribute(1,"new");
        writer.remove();
        writer.close();
        assertEquals( 4, count( "road" ));                    
    }
    public void testWriterChangeIgnoreAppend() throws Exception{
        PropertyFeatureWriter writer = (PropertyFeatureWriter)
                    store.getFeatureWriter("road");
        Feature f;
        writer.next();
        writer.next();
        writer.next();
        writer.next();
        assertFalse( writer.hasNext() );
        f = writer.next();
        assertNotNull( f );
        f.setAttribute(0,new Integer(-1));        
        f.setAttribute(1,"new");        
        writer.close();
        assertEquals( 4, count( "road" ));                    
    }
    public void testGetFeatureSource() throws Exception {
        FeatureSource road = store.getFeatureSource( "road" );
        FeatureCollection features = road.getFeatures();
        FeatureIterator reader = features.features();
        List list = new ArrayList();
        try {
            while( reader.hasNext() ){
                list.add( reader.next().getID() );                
            }
        } finally {
            reader.close();
        }
        assertEquals( "[fid1, fid2, fid3, fid4]", list.toString() );
        assertEquals( 4, road.getCount(Query.ALL) );
        assertEquals( null, road.getBounds(Query.ALL) );
        assertEquals( 4, features.size() );
        assertTrue( features.getBounds().isNull() );
        assertEquals( 4, features.size() );
                
    }
    private void dir( File file ){
        File dir;
        if( file.isDirectory() ){
            dir = file;
        }
        else{
            dir = file.getParentFile();
        }
        if( dir != null){
            String ls[] = dir.list();
            System.out.println( "Directory "+dir );
            for( int i=0; i<ls.length;i++){
                System.out.println( ls[i] );
            }
        }
    }

    public void testTransactionIndependence() throws Exception {
        FeatureType ROAD = store.getSchema( "road" );
        Feature chrisFeature =
            ROAD.create( new Object[]{ new Integer(5), "chris"}, "fid5" );
        
        FeatureStore roadAuto = (FeatureStore) store.getFeatureSource("road");
        
        FeatureStore roadFromClient1 = (FeatureStore) store.getFeatureSource("road");
        Transaction transaction1 = new DefaultTransaction("Transaction Used by Client 1");
        roadFromClient1.setTransaction( transaction1 );
        
        FeatureStore roadFromClient2 = (FeatureStore) store.getFeatureSource("road");
        Transaction transaction2 = new DefaultTransaction("Transaction Used by Client 2");
        roadFromClient2.setTransaction( transaction2 );

        FilterFactory2 ff = (FilterFactory2) CommonFactoryFinder.getFilterFactory(null);
        Filter selectFid1 = ff.id( Collections.singleton( ff.featureId("fid1") ));
        Filter selectFid2 = ff.id( Collections.singleton( ff.featureId("fid2") ));        
        
            
        // Before we edit everything should be the same
        assertEquals( "auto before", 4, roadAuto.getFeatures().size() );
        assertEquals( "client 1 before", 4, roadFromClient1.getFeatures().size() );
        assertEquals( "client 2 before", 4, roadFromClient2.getFeatures().size() );

        // Remove Feature with Fid1
        roadFromClient1.removeFeatures( selectFid1 ); // road1 removes fid1 on t1
        
        assertEquals( "auto after client 1 removes fid1", 4, roadAuto.getFeatures().size() );
        assertEquals( "client 1 after client 1 removes fid1", 3, roadFromClient1.getFeatures().size() );
        assertEquals( "client 2 after client 1 removes fid1", 4, roadFromClient2.getFeatures().size() );               
        
        
        roadFromClient2.addFeatures( DataUtilities.collection( chrisFeature )); // road2 adds fid5 on t2    
        assertEquals( "auto after client 1 removes fid1 and client 2 adds fid5", 4, roadAuto.getFeatures().size() );
        assertEquals( "client 1 after client 1 removes fid1 and client 2 adds fid5", 3, roadFromClient1.getFeatures().size() );
        assertEquals( "cleint 2 after client 1 removes fid1 and client 2 adds fid5", 5, roadFromClient2.getFeatures().size() );        

        transaction1.commit();
        assertEquals( "auto after client 1 commits removal of fid1 (client 2 has added fid5)", 3, roadAuto.getFeatures().size() );
        assertEquals( "client 1 after commiting removal of fid1 (client 2 has added fid5)", 3, roadFromClient1.getFeatures().size() );
        assertEquals( "client 2 after client 1 commits removal of fid1 (client 2 has added fid5)", 4, roadFromClient2.getFeatures().size() );                
            
        transaction2.commit();
        assertEquals( "auto after client 2 commits addition of fid5 (fid1 previously removed)", 4, roadAuto.getFeatures().size() );
        assertEquals( "client 1 after client 2 commits addition of fid5 (fid1 previously removed)", 4, roadFromClient1.getFeatures().size() );
        assertEquals( "client 2 after commiting addition of fid5 (fid1 previously removed)", 4, roadFromClient2.getFeatures().size() );
    }
}