package co.epitre.aelf_lectures.data;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.StreamCorruptedException;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.List;

import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteStatement;


/**
 * Internal cache manager (SQLite)
 */

final class AelfCacheHelper extends SQLiteOpenHelper {
	public static final String TAG = "AELFCacheHelper";
    private static final int DB_VERSION = 2;
    private static final String DB_NAME = "aelf_cache.db";

    private static final String DB_TABLE_CREATE = "CREATE TABLE IF NOT EXISTS `%s` (date INTEGER PRIMARY KEY, lectures BLOB)";
    private static final String DB_TABLE_SET = "INSERT OR REPLACE INTO `%s` VALUES (?,?)";

    // TODO: prepare requests

    public AelfCacheHelper(Context context) {
    	super(context, DB_NAME, null, DB_VERSION);
    }

    /**
     * Api
     */

    private long computeKey(GregorianCalendar when) {
    	return new GregorianCalendar(
    			when.get(Calendar.YEAR),
    			when.get(Calendar.MONTH),
    			when.get(Calendar.DAY_OF_MONTH)).getTimeInMillis();
    }

    public void store(LecturesController.WHAT what, GregorianCalendar when, List<LectureItem> lectures) {
    	long key  = computeKey(when);
    	byte[] blob = null;

    	// build blob
    	try {
    		ByteArrayOutputStream bos = new ByteArrayOutputStream();
    		ObjectOutputStream oos = null;
    		oos = new ObjectOutputStream(bos);
    		oos.writeObject(lectures);
    		blob = bos.toByteArray();
    	} catch (IOException e) {
    		throw new RuntimeException(e);
    		// FIXME: error recovery
    	}

    	// insert into the database
    	String sql = String.format(DB_TABLE_SET, what);
    	SQLiteStatement stmt = getWritableDatabase().compileStatement(sql);
    	stmt.bindLong(1, key);
    	stmt.bindBlob(2, blob);
        stmt.execute();
    }

    // cleaner helper method
    public void truncateBefore(LecturesController.WHAT what, GregorianCalendar when) {
    	String key = Long.toString(computeKey(when));
    	SQLiteDatabase db = getWritableDatabase();
    	db.delete(what.toString(), "`date` < ?", new String[] {key});
    }

    // cast is not checked when decoding the blob but we where responsible for its creation so... dont care
    @SuppressWarnings("unchecked")
    public List<LectureItem> load(LecturesController.WHAT what, GregorianCalendar when) {
    	String key  = Long.toString(computeKey(when));
    	byte[] blob = null;

    	// load from db
    	SQLiteDatabase db = getReadableDatabase();
    	Cursor cur = db.query(what.toString(), new String[] {"lectures"}, "`date`=?", new String[] {key}, null, null, null, "1");
    	if(cur != null && cur.getCount() > 0) {
    		// any records ? load it
    		cur.moveToFirst();
    		blob = cur.getBlob(0);

    		try {
    			ByteArrayInputStream bis = new ByteArrayInputStream(blob);
    			ObjectInputStream ois = new ObjectInputStream(bis);

    			return (List<LectureItem>)ois.readObject();
    		} catch (StreamCorruptedException e) {
    			throw new RuntimeException(e);
    			// FIXME: error recovery
    		} catch (IOException e) {
    			throw new RuntimeException(e);
    			// FIXME: error recovery
    		} catch (ClassNotFoundException e) {
    			throw new RuntimeException(e);
    			// FIXME: error recovery
    		}

    	} else {
    		return null;
    	}
    }

    /**
     * Internal logic
     */
    
    public void createCache(SQLiteDatabase db, LecturesController.WHAT what) {
    	String sql = String.format(DB_TABLE_CREATE, what);
		db.execSQL(sql);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
    	for (LecturesController.WHAT what : LecturesController.WHAT.class.getEnumConstants()) {
    		createCache(db, what);
    	}
    }
    
    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    	if(oldVersion == 1) {
    		createCache(db, LecturesController.WHAT.METAS);
    	}
    }

}
@@ -443,16 +443,64 @@
 tabase;%0A
+import android.database.sqlite.SQLiteException;%0A
 import a
@@ -540,16 +540,16 @@
 Helper;%0A
-
 import a
@@ -588,16 +588,41 @@
 tement;%0A
+import android.util.Log;%0A
 %0A%0A/**%0A *
@@ -1465,24 +1465,440 @@
 lis();%0A    %7D
+%0A    %0A    private boolean _execute_stmt(SQLiteStatement stmt, int max_retries) %7B%0A    %09// Attempt to run this statement up to max_retries times%0A    %09do %7B%0A    %09%09try %7B%0A    %09%09%09stmt.execute();%0A    %09%09%09return true;%0A    %09%09%7D catch(SQLiteException e) %7B%0A    %09%09%09Log.v(TAG, %22Failed to save item in cache: %22+e.toString());%0A    %09%09%7D%0A    %09%09%0A    %09%7D while (--max_retries %3E 0);%0A    %09%0A    %09// All attempts failed%0A    %09return false;%0A    %7D
 %0A%0A    public
@@ -2621,25 +2621,112 @@
     
+%09%0A
     
-stmt.execute(
+%09// Multiple attempts. On failure ignore. This is cache --%3E best effort%0A    %09_execute_stmt(stmt, 3
 );%0A 

fix sqlite exception