/*
 * Copyright 2014 WNCOutdoors.info
 * portions Copyright (C) 2011 readyState Software Ltd, 2007 The Android Open Source Project
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *     http://www.apache.org/licenses/LICENSE-2.0
 *     
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 * MBTilesDatabase.java
 * Class which controls extraction of the MBTiles databases for offline maps
 * and provides access to the resulting file.
 */


package info.northcarolinawaterfalls.app;

import android.content.Context;
import android.os.Environment;
import android.util.Log;

import com.android.vending.expansion.zipfile.APKExpansionSupport;
import com.android.vending.expansion.zipfile.ZipResourceFile;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

public class MBTilesDatabase {
    private static final String TAG = "MBTilesDatabase";

    // Versions are pinned to the version at time of expansion file upload.
    private static final int MAIN_VERSION = 14;
    private static final int PATCH_VERSION = 14;

    private final Context mContext;

    private final String mDatabaseName;
    private final String mInternalMBTilesPath;

    private final String mExternalFilesDirPath;
    private final String mExternalFilesDatabasePath;
    
    private File mMBTilesDBFile;

    public MBTilesDatabase(Context context, String databaseName){
        mContext = context;
        mDatabaseName = databaseName;  // mbtiles export/database name (without extension)
        mInternalMBTilesPath = "mbtiles/" + databaseName + ".mbtiles";  // Path within expansion zip file
        mExternalFilesDirPath = context.getExternalFilesDir(null) + "/mbtiles/";  // External dir we will unzip into
        mExternalFilesDatabasePath = mExternalFilesDirPath + databaseName + ".mbtiles";
        
        mMBTilesDBFile = new File(mExternalFilesDatabasePath);
    }

    public boolean dbFileExists(){
        return mMBTilesDBFile.exists();
    }

    public File getDBFile(){
        return mMBTilesDBFile;
    }

    public boolean extractDBFile(){
        // Run in an async task.
        if(!dbFileExists()){
            Log.d(TAG, "MBTiles db does not exist.");
            try {
                copyDatabaseFromExpansion();
                return true;
            } catch (MBTilesDatabaseException e) {
                Log.e(TAG, "Failed to copy from expansion file", e);               
                return false;
            }
        } else {
            Log.d(TAG, "MBTilesDatabase already unpacked.");
            return false;
        }
    }

    private void copyDatabaseFromExpansion() throws MBTilesDatabaseException{
        // To abort loading of MBTiles layer, throw MBTilesDatabaseException here,
        // which will result in a toast being displayed (but other map layers should
        // still load).
        Log.d(TAG, "Copying database from expansion file.");
        if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){
            Log.d(TAG, "External storage is accessible.");
            try{
                ZipResourceFile expansionFile = APKExpansionSupport.getAPKExpansionZipFile(
                    mContext, MAIN_VERSION, MAIN_VERSION);
                if(expansionFile == null){
                    MBTilesDatabaseException me = new MBTilesDatabaseException(
                            "getAPKExpansionZipFile returned null");
                    throw me;
                }
                Log.d(TAG, "Getting " + mInternalMBTilesPath + " from expansion file.");
                InputStream mbTileStream = expansionFile.getInputStream(mInternalMBTilesPath);
                if(mbTileStream == null){
                    MBTilesDatabaseException me = new MBTilesDatabaseException(
                            "expansionFile.getInputStream(" + mInternalMBTilesPath + ") returned null");
                    throw me;
                }

                // Create the external files dir if it doesn't exist.
                File f = new File(mExternalFilesDirPath);
                if (!f.exists()) {
                    Log.d(TAG, "External files dir does not exist; creating.");
                    f.mkdir();
                }
                FileOutputStream outStream = new FileOutputStream(mExternalFilesDatabasePath);
                writeExtractedFileToDisk(mbTileStream, outStream);
                Log.d(TAG, "Database copy complete.");
            } catch (FileNotFoundException fe) {
                MBTilesDatabaseException me = new MBTilesDatabaseException(
                        "Database file not found in expansion archive or destination not writeable.");
                me.setStackTrace(fe.getStackTrace());
                throw me;
            } catch (IOException ie){
                MBTilesDatabaseException me = new MBTilesDatabaseException(
                        "Unable to extract mbtiles db to external data dir.");
                me.setStackTrace(ie.getStackTrace());
                throw me;
            }
        } else {
            Log.d(TAG, "External storage is not mounted; cannot copy mbtiles db.");
        }
    }
    
    private void writeExtractedFileToDisk(InputStream zin, OutputStream outs) throws IOException {
        Log.d(TAG, "Writing extracted mbtiles file to external files dir.");
        byte[] buffer = new byte[1024];
        int length;
        while ((length = zin.read(buffer))>0){
                outs.write(buffer, 0, length);
        }
        outs.flush();
        outs.close();
        zin.close();
        Log.d(TAG, "Done extracting mbtiles file.");
    }
}

@@ -845,17 +845,16 @@
 e.%0A */%0A%0A
-%0A
 package 
@@ -1702,29 +1702,29 @@
 ng mExternal
-Files
+Cache
 DirPath;%0A   
@@ -1746,37 +1746,38 @@
 String mExternal
-Files
+Cached
 DatabasePath;%0A  
@@ -2104,16 +2104,124 @@
 ip file%0A
+        // TODO: This returns null when external storage is not mounted.%0A        // Warn the user and stop.%0A
         
@@ -2221,37 +2221,37 @@
        mExternal
-Files
+Cache
 DirPath = contex
@@ -2267,21 +2267,17 @@
 rnal
-FilesDir(null
+CacheDir(
 ) + 
@@ -2285,17 +2285,16 @@
 /mbtiles
-/
 %22;  // E
@@ -2333,37 +2333,38 @@
        mExternal
-Files
+Cached
 DatabasePath = m
@@ -2367,29 +2367,29 @@
  = mExternal
-Files
+Cache
 DirPath + da
@@ -2457,37 +2457,38 @@
 w File(mExternal
-Files
+Cached
 DatabasePath);%0A 
@@ -4565,29 +4565,29 @@
 he external 
-files
+CACHE
  dir if it d
@@ -4643,21 +4643,21 @@
 External
-Files
+Cache
 DirPath)
@@ -4730,29 +4730,29 @@
 , %22External 
-files
+cache
  dir does no
@@ -4901,13 +4901,14 @@
 rnal
-Files
+Cached
 Data
@@ -6221,10 +6221,70 @@
 ;%0A    %7D%0A
+    %0A    private void clearExtractedFiles()%7B%0A        %0A    %7D%0A
 %7D%0A

Use external cache dir instead of files dir