package starfish.test;

import java.sql.Connection;
import java.util.List;

import javax.sql.DataSource;

import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;

import starfish.GenericOpsRead;
import starfish.GenericOpsWrite;
import starfish.IOpsRead;
import starfish.IOpsWrite;
import starfish.helper.ConnectionActivity;
import starfish.helper.ConnectionActivityWithoutResult;
import starfish.helper.DataSourceTemplate;
import starfish.helper.JdbcUtil;
import starfish.type.TableMetadata;

public class OpsTest {

    final DataSource ds = TestUtil.makeTestDataSource();
    final DataSourceTemplate dst = new DataSourceTemplate(ds);

    final TableMetadata meta = TableMetadata.create("session", "id", "value", "version", "updated");
    final IOpsWrite<Integer, String> writer = new GenericOpsWrite<Integer, String>(meta);
    final IOpsRead<Integer, String> reader = new GenericOpsRead<Integer, String>(meta, Integer.class, String.class);

    private <K> long findRowCountForKey(final K key) {
        return JdbcUtil.withConnection(ds, new ConnectionActivity<List<Long>>() {
            public List<Long> execute(Connection conn) {
                return JdbcUtil.queryVals(conn, "SELECT COUNT(*) FROM session WHERE ID = ?", new Object[] { key },
                        JdbcUtil.makeColumnExtractor(Long.class, 1));
            }
        }).get(0).longValue();
    }

    @BeforeClass
    public static void setUpBeforeClass() throws Exception {
    }

    @Test
    public void crudTest() {
        // ----- INSERT -----

        // write (actually insert, because the value doesn't exist) key-value pair
        final int key = 1;
        final String newValue1 = "abc";
        final Long version1 = dst.withConnection(new ConnectionActivity<Long>() {
            public Long execute(Connection conn) {
                return writer.save(conn, key, newValue1);
            }
        });
        Assert.assertNotNull(version1);

        // read value for given key
        final String savedValue1 = dst.withConnection(new ConnectionActivity<String>() {
            public String execute(Connection conn) {
                return reader.read(conn, key);
            }
        });
        Assert.assertEquals(newValue1, savedValue1);

        // make sure database table has the value
        Assert.assertEquals(1, findRowCountForKey(key));

        // ----- UPDATE -----

        // write again (it is an update this time)
        final String newValue2 = "xyz";
        final Long version2 = dst.withConnection(new ConnectionActivity<Long>() {
            public Long execute(Connection conn) {
                return writer.save(conn, key, newValue2);
            }
        });
        Assert.assertNotNull(version2);
        Assert.assertNotEquals(version1, version2);

        // read value again for given key
        final String savedValue2 = dst.withConnection(new ConnectionActivity<String>() {
            public String execute(Connection conn) {
                return reader.read(conn, key);
            }
        });
        Assert.assertEquals(newValue2, savedValue2);

        // make sure database table has the value
        Assert.assertEquals(1, findRowCountForKey(key));

        // ----- DELETE -----

        // delete key-value pair
        dst.withConnectionWithoutResult(new ConnectionActivityWithoutResult() {
            public void execute(Connection conn) {
                writer.delete(conn, key);
            }
        });

        // read value again
        final String deletedValue = dst.withConnection(new ConnectionActivity<String>() {
            public String execute(Connection conn) {
                return reader.read(conn, key);
            }
        });
        Assert.assertNull(deletedValue);

        // make sure database table has the value
        Assert.assertEquals(0, findRowCountForKey(key));
    }

}

@@ -1389,24 +1389,276 @@
 e();%0A    %7D%0A%0A
+    private String readValue(final Integer key) %7B%0A        return dst.withConnection(new ConnectionActivity%3CString%3E() %7B%0A            public String execute(Connection conn) %7B%0A                return reader.read(conn, key);%0A            %7D%0A        %7D);%0A    %7D%0A%0A
     @BeforeC
@@ -1792,16 +1792,23 @@
 - INSERT
+ (SAVE)
  -----%0A%0A
@@ -2255,223 +2255,8 @@
 key%0A
-        final String savedValue1 = dst.withConnection(new ConnectionActivity%3CString%3E() %7B%0A            public String execute(Connection conn) %7B%0A                return reader.read(conn, key);%0A            %7D%0A        %7D);%0A
     
@@ -2294,19 +2294,22 @@
 e1, 
-save
+rea
 dValue
-1
+(key)
 );%0A%0A
@@ -2439,16 +2439,23 @@
 - UPDATE
+ (SAVE)
  -----%0A%0A
@@ -2909,32 +2909,318 @@
     
-final String savedValue2
+Assert.assertEquals(newValue2, readValue(key));%0A%0A        // make sure database table has the value%0A        Assert.assertEquals(1, findRowCountForKey(key));%0A%0A        // ----- SWAP -----%0A%0A        // swap using invalid version, which should fail%0A        final String newValue3 = %22pqr%22;%0A        final Long version3
  = d
@@ -3252,36 +3252,34 @@
 nectionActivity%3C
-Stri
+Lo
 ng%3E() %7B%0A        
@@ -3281,36 +3281,34 @@
          public 
-Stri
+Lo
 ng execute(Conne
@@ -3344,37 +3344,58 @@
  return 
-reader.read(conn, key
+writer.swap(conn, key, newValue3, version1
 );%0A     
@@ -3407,32 +3407,69 @@
   %7D%0A        %7D);%0A
+        Assert.assertNull(version3);%0A
         Assert.a
@@ -3495,19 +3495,22 @@
 e2, 
-save
+rea
 dValue
-2
+(key)
 );%0A%0A
@@ -3524,96 +3524,462 @@
  // 
-make sure database table has the value%0A        Assert.assertEquals(1, findRowCountForKey
+swap using valid version, which should succeed%0A        final String newValue4 = %22pqr%22;%0A        final Long version4 = dst.withConnection(new ConnectionActivity%3CLong%3E() %7B%0A            public Long execute(Connection conn) %7B%0A                return writer.swap(conn, key, newValue4, version2);%0A            %7D%0A        %7D);%0A        Assert.assertNotNull(version4);%0A        Assert.assertNotEquals(version2, version4);%0A        Assert.assertEquals(newValue4, readValue
 (key
@@ -4279,224 +4279,8 @@
 ain%0A
-        final String deletedValue = dst.withConnection(new ConnectionActivity%3CString%3E() %7B%0A            public String execute(Connection conn) %7B%0A                return reader.read(conn, key);%0A            %7D%0A        %7D);%0A
     
@@ -4305,20 +4305,22 @@
 ull(
-delete
+rea
 dValue
+(key)
 );%0A%0A

add test case for swap()