/*
 * Copyright 2016 requery.io
 *
 * 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.
 */

package io.requery.android.sqlcipher;

import android.content.Context;
import android.database.Cursor;
import io.requery.android.DefaultMapping;
import io.requery.android.LoggingListener;
import io.requery.android.sqlite.DatabaseProvider;
import io.requery.android.sqlite.SchemaUpdater;
import io.requery.meta.EntityModel;
import io.requery.sql.Configuration;
import io.requery.sql.ConfigurationBuilder;
import io.requery.sql.Mapping;
import io.requery.sql.Platform;
import io.requery.sql.SchemaModifier;
import io.requery.sql.TableCreationMode;
import io.requery.sql.platform.SQLite;
import io.requery.util.function.Function;
import net.sqlcipher.database.SQLiteDatabase;
import net.sqlcipher.database.SQLiteOpenHelper;

import java.sql.Connection;
import java.sql.SQLException;

public class SqlCipherDatabaseSource extends SQLiteOpenHelper implements
    DatabaseProvider<SQLiteDatabase> {

    private final Platform platform;
    private final EntityModel model;
    private final Mapping mapping;
    private final String password;
    private SQLiteDatabase db;
    private Configuration configuration;
    private boolean loggingEnabled;
    private TableCreationMode mode;

    public SqlCipherDatabaseSource(Context context,
                                   EntityModel model,
                                   String name,
                                   String password,
                                   int version) {
        super(context, name, null, version);
        if (model == null) {
            throw new IllegalArgumentException("null model");
        }
        this.platform = new SQLite();
        this.mapping = onCreateMapping(platform);
        this.model = model;
        this.password = password;
        this.mode = TableCreationMode.CREATE_NOT_EXISTS;
        SQLiteDatabase.loadLibs(context);
    }

    @Override
    public void setLoggingEnabled(boolean enable) {
        this.loggingEnabled = enable;
    }

    @Override
    public void setTableCreationMode(TableCreationMode mode) {
        this.mode = mode;
    }

    protected Mapping onCreateMapping(Platform platform) {
        return new DefaultMapping(platform);
    }

    protected void onConfigure(ConfigurationBuilder builder) {
        if (loggingEnabled) {
            LoggingListener loggingListener = new LoggingListener();
            builder.addStatementListener(loggingListener);
        }
    }

    private Connection getConnection(SQLiteDatabase db) throws SQLException {
        synchronized (this) {
            return new SqlCipherConnection(db);
        }
    }

    @Override
    public Configuration getConfiguration() {
        if (configuration == null) {
            ConfigurationBuilder builder = new ConfigurationBuilder(this, model)
                .setMapping(mapping)
                .setPlatform(platform)
                .setBatchUpdateSize(1000);
            onConfigure(builder);
            configuration = builder.build();
        }
        return configuration;
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        this.db = db;
        new SchemaModifier(getConfiguration()).createTables(TableCreationMode.CREATE);
    }

    @Override
    public void onConfigure(SQLiteDatabase db) {
        //
    }

    @Override
    public void onUpgrade(final SQLiteDatabase db, int oldVersion, int newVersion) {
        this.db = db;
        SchemaUpdater updater = new SchemaUpdater(getConfiguration(),
            new Function<String, Cursor>() {
            @Override
            public Cursor apply(String s) {
                return db.rawQuery(s, null);
            }
        }, mode);
        updater.update();
    }

    @Override
    public Connection getConnection() throws SQLException {
        synchronized (this) {
            if (db == null) {
                db = getWritableDatabase(password);
            }
            return getConnection(db);
        }
    }

    @Override
    public SQLiteDatabase getReadableDatabase() {
        return getReadableDatabase(password);
    }

    @Override
    public SQLiteDatabase getWritableDatabase() {
        return getWritableDatabase(password);
    }
}

@@ -3842,18 +3842,101 @@
         
-//
+if (!db.isReadOnly()) %7B%0A            db.execSQL(%22PRAGMA foreign_keys = ON%22);%0A        %7D
 %0A    %7D%0A%0A

Resolve #364 Foreign keys not enabled in SqlCipher