package com.robert.maps.applib.utils;
import java.io.File;
import java.util.Locale;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException;
import com.robert.maps.applib.tileprovider.TileSource;
public class SQLiteMapDatabase implements ICacheProvider {
private static final String SQL_CREATE_tiles = "CREATE TABLE IF NOT EXISTS tiles (x int, y int, z int, s int, image blob, PRIMARY KEY (x,y,z,s));";
private static final String SQL_CREATE_info = "CREATE TABLE IF NOT EXISTS info (maxzoom Int, minzoom Int, params VARCHAR);";
private static final String SQL_SELECT_PARAMS = "SELECT * FROM info";
private static final String SQL_UPDATE_PARAMS = "UPDATE info SET params = ?";
private static final String SQL_SELECT_IMAGE = "SELECT image as ret FROM tiles WHERE x = ? AND y = ? AND z = ?";
private static final String SQL_FINDTHEMAP = "SELECT x, y FROM tiles WHERE z = ? LIMIT 1";
private static final String SQL_DROP_tiles = "DROP TABLE IF EXISTS tiles";
private static final String SQL_DROP_info = "DROP TABLE IF EXISTS info";
private static final String SQL_tiles_count = "SELECT COUNT(*) cnt FROM tiles";
private static final String SQL_INIT_INFO = "INSERT OR IGNORE INTO info (rowid, minzoom, maxzoom) SELECT 1, 0, 0;";
private static final String SQL_UPDZOOM_UPDMIN = "UPDATE info SET minzoom = (SELECT DISTINCT z FROM tiles ORDER BY z ASC LIMIT 1);";
private static final String SQL_UPDZOOM_UPDMAX = "UPDATE info SET maxzoom = (SELECT DISTINCT z FROM tiles ORDER BY z DESC LIMIT 1);";
private static final String SQL_GET_MINZOOM = "SELECT DISTINCT 17 - z FROM tiles ORDER BY z DESC LIMIT 1;";
private static final String SQL_GET_MAXZOOM = "SELECT DISTINCT 17 - z FROM tiles ORDER BY z ASC LIMIT 1;";
private static final String RET = "ret";
private static final long MAX_DATABASE_SIZE = 1945 * 1024 * 1024; // 1.9GB
private static final String JOURNAL = "-journal";
private static final String SQLITEDB = "sqlitedb";
private static final String SQL_DELTILE_WHERE = "x = ? AND y = ? AND z = ?";
private static final String TILES = "tiles";
private static final String PARAMS = "params";
private SQLiteDatabase[] mDatabase = new SQLiteDatabase[0];
private SQLiteDatabase mDatabaseWritable;
private int mCurrentIndex = 0;
private File mBaseFile = null;
private int mBaseFileIndex = 0;
private int[] mMinMaxZoom = null;
public String getID(String pref) {
return Ut.FileName2ID(pref+mBaseFile.getName());
}
private void initDatabaseFiles(final String aFileName, final boolean aCreateNewDatabaseFile) throws RException {
for(int i = 0; i < mDatabase.length; i++)
if (mDatabase[i] != null)
mDatabase[i].close();
//RException aException = null;
mBaseFile = new File(aFileName);
final File folder = mBaseFile.getParentFile();
if(folder != null) {
File[] files = folder.listFiles();
if(files != null) {
int j = 0;
mBaseFileIndex = 0;
// ���������� ���������� ���������� ������
for (int i = 0; i < files.length; i++) {
if(files[i].getName().startsWith(mBaseFile.getName()) && !files[i].getName().endsWith(JOURNAL)) {
j = j + 1;
try {
final int index = Integer.getInteger(files[i].getName().replace(mBaseFile.getName(), ""));
if(index > mBaseFileIndex)
mBaseFileIndex = index;
} catch (Exception e) {
}
}
}
final int dbFilesCnt = j;
// ���� ����� ������� ��� ����, �� ����������� ��� ���� �����
if(aCreateNewDatabaseFile || j == 0)
j = j + 1;
// ������� ������ ������������� �������
mDatabase = new SQLiteDatabase[j];
// ��������� ������
j = 0; long minsize = 0;
for (int i = 0; i < files.length; i++) {
if(files[i].getName().startsWith(mBaseFile.getName()) && !files[i].getName().endsWith(JOURNAL)) {
try {
mDatabase[j] = new CashDatabaseHelper(null, files[i].getAbsolutePath()).getWritableDatabase();
mDatabase[j].setMaximumSize(MAX_DATABASE_SIZE);
if(mDatabaseWritable == null) {
mDatabaseWritable = mDatabase[j];
minsize = files[i].length();
} else {
if(files[i].length() < minsize) {
mDatabaseWritable = mDatabase[j];
minsize = files[i].length();
}
}
j = j + 1;
} catch (Throwable e) {
//aException = new RException(R.string.error_diskio, files[i].getAbsolutePath());
}
}
}
if(dbFilesCnt == 0) {
mDatabase[0] = new CashDatabaseHelper(null, mBaseFile.getAbsolutePath()).getWritableDatabase();
mDatabaseWritable = mDatabase[0];
}
if(aCreateNewDatabaseFile) {
mDatabase[j] = new CashDatabaseHelper(null, mBaseFile.getAbsolutePath() + (mBaseFileIndex + 1)).getWritableDatabase();
mDatabaseWritable = mDatabase[j];
}
}
}
// if(aException != null)
// throw aException;
}
public synchronized void setFile(final String aFileName) throws SQLiteException, RException {
initDatabaseFiles(aFileName, false);
}
public synchronized void setFile(final File aFile) throws SQLiteException, RException {
setFile(aFile.getAbsolutePath());
}
protected class CashDatabaseHelper extends RSQLiteOpenHelper {
public CashDatabaseHelper(final Context context, final String name) {
super(context, name, null, 3);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(SQL_CREATE_tiles);
db.execSQL(SQL_CREATE_info);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
public void updateMapParams(TileSource tileSource) {
tileSource.ZOOM_MINLEVEL = getMinZoom();
tileSource.ZOOM_MAXLEVEL = getMaxZoom();
}
public synchronized void updateMinMaxZoom() {
if(mMinMaxZoom == null)
mMinMaxZoom = new int[2];
mMinMaxZoom[0] = 22; //min
mMinMaxZoom[1] = 0; //max
int zoom;
for(int i = 0; i < mDatabase.length; i++)
if(mDatabase[i] != null) {
try {
zoom = (int) this.mDatabase[i].compileStatement(SQL_GET_MINZOOM).simpleQueryForLong();
if(zoom < mMinMaxZoom[0])
mMinMaxZoom[0] = zoom;
} catch (SQLException e) {
}
try {
zoom = (int) this.mDatabase[i].compileStatement(SQL_GET_MAXZOOM).simpleQueryForLong();
if(zoom > mMinMaxZoom[1])
mMinMaxZoom[1] = zoom;
} catch (SQLException e) {
}
}
}
public synchronized int getMaxZoom() {
if(mMinMaxZoom == null)
updateMinMaxZoom();
return mMinMaxZoom[1];
}
public synchronized int getMinZoom() {
if(mMinMaxZoom == null)
updateMinMaxZoom();
return mMinMaxZoom[0];
}
public synchronized void putTile(final int aX, final int aY, final int aZ, final byte[] aData) throws RException {
if (this.mDatabaseWritable != null) {
final ContentValues cv = new ContentValues();
cv.put("x", aX);
cv.put("y", aY);
cv.put("z", 17 - aZ);
cv.put("s", System.currentTimeMillis() / 1000);
cv.put("image", aData);
try {
this.mDatabaseWritable.insertOrThrow(TILES, null, cv);
} catch (SQLException e) {
initDatabaseFiles(mBaseFile.getAbsolutePath(), true);
}
}
}
public synchronized byte[] getTile(final int aX, final int aY, final int aZ) {
byte[] ret = null;
int j = 0;
for(int i = 0; i < mDatabase.length; i++) {
j = mCurrentIndex + i;
if(j >= mDatabase.length)
j = j - mDatabase.length;
if (this.mDatabase[j] != null && this.mDatabase[j].isOpen() && !this.mDatabase[j].isDbLockedByOtherThreads()) {
final String[] args = {""+aX, ""+aY, ""+(17 - aZ)};
try {
final Cursor c = this.mDatabase[j].rawQuery(SQL_SELECT_IMAGE, args);
if (c != null) {
if (c.moveToFirst()) {
ret = c.getBlob(c.getColumnIndexOrThrow(RET));
c.close();
if(ret != null)
if(ret.length == 0) {
mDatabase[j].delete(TILES, SQL_DELTILE_WHERE, args);
ret = null;
}
mCurrentIndex = j;
break;
} else
c.close();
}
} catch (Throwable e) {
e.printStackTrace();
}
}
}
return ret;
}
@Override
public synchronized void deleteTile(String aURLstring, int aX, int aY, int aZ) {
final String[] args = {""+aX, ""+aY, ""+(17 - aZ)};
for(int i = 0; i < mDatabase.length; i++) {
if(mDatabase[i] != null)
mDatabase[i].delete(TILES, SQL_DELTILE_WHERE, args);
}
}
public synchronized boolean existsTile(final int aX, final int aY, final int aZ) {
final String[] args = {""+aX, ""+aY, ""+(17 - aZ)};
boolean ret = false;
for(int i = 0; i < mDatabase.length; i++) {
if(mDatabase[i] != null) {
final Cursor c = this.mDatabase[i].rawQuery(SQL_SELECT_IMAGE, args);
if(c != null) {
if(c.moveToFirst())
ret = true;
c.close();
}
}
if(ret) break;
}
return ret;
}
@Override
protected void finalize() throws Throwable {
for(int i = 0; i < mDatabase.length; i++) {
if(mDatabase[i] != null)
mDatabase[i].close();
}
super.finalize();
}
public synchronized void freeDatabases() {
for (int i = 0; i < mDatabase.length; i++) {
if (mDatabase[i] != null)
if (mDatabase[i].isOpen()) {
mDatabase[i].close();
}
}
}
public synchronized byte[] getTile(String aURLstring, int aX, int aY, int aZ) {
return getTile(aX, aY, aZ);
}
public synchronized void putTile(String aURLstring, int aX, int aY, int aZ, byte[] aData) throws RException {
putTile(aX, aY, aZ, aData);
}
public synchronized void Free() {
freeDatabases();
}
public void clearTiles() {
for (int i = 0; i < mDatabase.length; i++) {
if (mDatabase[i] != null) {
mDatabase[i].execSQL(SQL_DROP_tiles);
mDatabase[i].execSQL(SQL_CREATE_tiles);
}
}
}
public double getTileLenght() {
double ret = 0L;
if(mDatabase.length > 0 && mDatabase[0] != null) {
final long cnt = mDatabase[0].compileStatement(SQL_tiles_count).simpleQueryForLong();
if(cnt > 0) {
final File file = new File(mDatabase[0].getPath());
ret = file.length()/cnt;
};
}
return ret;
}
public JSONObject getParams() {
JSONObject json = null;
for (int i = 0; i < mDatabase.length; i++) {
if (mDatabase[i] != null)
if (mDatabase[i].getPath().toLowerCase(Locale.US).endsWith(SQLITEDB)) {
final Cursor c = this.mDatabase[i].rawQuery(SQL_SELECT_PARAMS, null);
if (c != null) {
if (c.moveToFirst()) {
final int col = c.getColumnIndex(PARAMS);
if (col >= 0) {
final String val = c.getString(col);
if (val != null) {
try {
json = new JSONObject(val);
break;
} catch (JSONException e) {
}
}
}
}
c.close();
}
}
}
if (json == null)
json = new JSONObject();
return json;
}
public static final String MAPID = "mapid";
public static final String MAPNAME = "mapname";
public static final String ZOOM = "zoom";
public static final String COORDS = "coords";
public static final String ZOOMS = "zooms";
public void setParams(String mapID, String mapName, int[] coordArr, int[] zoomArr, int zoom) {
final JSONObject json = getParams();
try {
json.put(MAPID, mapID);
json.put(MAPNAME, mapName);
json.put(ZOOM, zoom);
{
final JSONArray jarr = new JSONArray();
for(int i = 0; i < coordArr.length; i++)
jarr.put(coordArr[i]);
json.put(COORDS, jarr);
}
{
final JSONArray jarr = new JSONArray();
for(int i = 0; i < zoomArr.length; i++)
jarr.put(zoomArr[i]);
json.put(ZOOMS, jarr);
}
} catch (JSONException e) {
}
for (int i = 0; i < mDatabase.length; i++) {
if(mDatabase[i] != null)
if(mDatabase[i].getPath().toLowerCase(Locale.US).endsWith(SQLITEDB)) {
final String[] arg = {json.toString()};
try {
this.mDatabase[i].execSQL(SQL_UPDATE_PARAMS, arg);
} catch (SQLException e) {
try {
this.mDatabase[i].execSQL(SQL_DROP_info);
this.mDatabase[i].execSQL(SQL_CREATE_info);
this.mDatabase[i].execSQL(SQL_INIT_INFO);
this.mDatabase[i].execSQL(SQL_UPDZOOM_UPDMIN);
this.mDatabase[i].execSQL(SQL_UPDZOOM_UPDMAX);
this.mDatabase[i].execSQL(SQL_UPDATE_PARAMS, arg);
} catch (SQLException e1) {
}
}
break;
}
}
}
public int[] findTheMap(int zoomLevel) {
int[] coord = new int[2];
final String[] args = {""+(17 - zoomLevel)};
boolean ret = false;
for(int i = 0; i < mDatabase.length; i++) {
if(mDatabase[i] != null) {
final Cursor c = this.mDatabase[i].rawQuery(SQL_FINDTHEMAP, args);
if(c != null) {
if(c.moveToFirst()) {
coord[0] = c.getInt(1);
coord[1] = c.getInt(0);
}
c.close();
}
}
if(ret) break;
}
return coord;
}
}