/*
 * Copyright 2014 http://Bither.net
 *
 * 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 net.bither;

import android.app.Activity;
import android.app.ActivityManager;
import android.app.Application;
import android.content.Context;
import android.content.Intent;
import android.database.sqlite.SQLiteOpenHelper;
import android.os.Build;
import android.os.StrictMode;
import android.support.multidex.MultiDex;

import net.bither.activity.cold.ColdActivity;
import net.bither.activity.hot.HotActivity;
import net.bither.bitherj.AbstractApp;
import net.bither.bitherj.BitherjSettings;
import net.bither.bitherj.core.AddressManager;
import net.bither.bitherj.crypto.mnemonic.MnemonicCode;
import net.bither.bitherj.utils.Threading;
import net.bither.db.AddressDatabaseHelper;
import net.bither.db.AndroidDbImpl;
import net.bither.db.TxDatabaseHelper;
import net.bither.exception.UEHandler;
import net.bither.mnemonic.MnemonicCodeAndroid;
import net.bither.preference.AppSharedPreference;
import net.bither.service.BlockchainService;
import net.bither.xrandom.LinuxSecureRandom;

import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.IOException;

import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.android.LogcatAppender;
import ch.qos.logback.classic.encoder.PatternLayoutEncoder;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.rolling.RollingFileAppender;
import ch.qos.logback.core.rolling.TimeBasedRollingPolicy;

public class BitherApplication extends Application {

    private ActivityManager activityManager;

    private static org.slf4j.Logger log = LoggerFactory.getLogger(BitherApplication.class);
    private static BitherApplication mBitherApplication;
    public static HotActivity hotActivity;
    public static ColdActivity coldActivity;
    public static UEHandler ueHandler;
    public static Activity initialActivity;
    public static boolean isFirstIn = false;
    public static long reloadTxTime = -1;
    public static Context mContext;
    public static SQLiteOpenHelper mTxDbHelper;
    public static SQLiteOpenHelper mAddressDbHelper;

    private static int FEE_UPDATE_CODE = 0;


    @Override
    public void onCreate() {
        new LinuxSecureRandom();
        super.onCreate();
        mContext = getApplicationContext();
        ueHandler = new UEHandler();
        Thread.setDefaultUncaughtExceptionHandler(ueHandler);
        activityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
        mAddressDbHelper = new AddressDatabaseHelper(mContext);
        mTxDbHelper = new TxDatabaseHelper(mContext);
        AndroidDbImpl androidDb = new AndroidDbImpl();
        androidDb.construct();
        AndroidImplAbstractApp appAndroid = new AndroidImplAbstractApp();
        appAndroid.construct();
        AbstractApp.notificationService.removeAddressLoadCompleteState();
        StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder().detectAll()
                .permitDiskReads().permitDiskWrites().penaltyLog().build());
        Threading.throwOnLockCycles();
        initApp();
        mBitherApplication = this;
        upgrade();
    }

    @Override
    public void onTerminate() {
        super.onTerminate();
        mTxDbHelper.close();
    }

    public static BitherApplication getBitherApplication() {
        return mBitherApplication;
    }

    public static void startBlockchainService() {
            mContext.startService(new Intent(mContext, BlockchainService.class));
    }


    public static boolean canReloadTx() {
        if (reloadTxTime == -1) {
            return true;
        } else {
            return reloadTxTime + 60 * 60 * 1000 < System.currentTimeMillis();
        }
    }

    public static File getLogDir() {
        final File logDir = mContext.getDir("log", Context.MODE_PRIVATE);
        return logDir;
    }

    private void initLogging() {
        final File logDir = getLogDir();
        final File logFile = new File(logDir, "bitherj.log");
        final LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory();
        final PatternLayoutEncoder filePattern = new PatternLayoutEncoder();
        filePattern.setContext(context);
        filePattern.setPattern("%d{HH:mm:ss.SSS} [%thread] %logger{0} - %msg%n");
        filePattern.start();

        final RollingFileAppender<ILoggingEvent> fileAppender = new
                RollingFileAppender<ILoggingEvent>();
        fileAppender.setContext(context);
        fileAppender.setFile(logFile.getAbsolutePath());

        final TimeBasedRollingPolicy<ILoggingEvent> rollingPolicy = new
                TimeBasedRollingPolicy<ILoggingEvent>();
        rollingPolicy.setContext(context);
        rollingPolicy.setParent(fileAppender);
        rollingPolicy.setFileNamePattern(logDir.getAbsolutePath() + "/bitherj.%d.log.gz");
        rollingPolicy.setMaxHistory(7);
        rollingPolicy.start();

        fileAppender.setEncoder(filePattern);
        fileAppender.setRollingPolicy(rollingPolicy);
        fileAppender.start();

        final PatternLayoutEncoder logcatTagPattern = new PatternLayoutEncoder();
        logcatTagPattern.setContext(context);
        logcatTagPattern.setPattern("%logger{0}");
        logcatTagPattern.start();

        final PatternLayoutEncoder logcatPattern = new PatternLayoutEncoder();
        logcatPattern.setContext(context);
        logcatPattern.setPattern("[%thread] %msg%n");
        logcatPattern.start();

        final LogcatAppender logcatAppender = new LogcatAppender();
        logcatAppender.setContext(context);
        logcatAppender.setTagEncoder(logcatTagPattern);
        logcatAppender.setEncoder(logcatPattern);
        logcatAppender.start();

        final ch.qos.logback.classic.Logger log = context.getLogger(Logger.ROOT_LOGGER_NAME);
        log.addAppender(fileAppender);
        log.addAppender(logcatAppender);
        log.setLevel(Level.INFO);
    }

    private void initApp() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    MnemonicCodeAndroid.setMnemonicCode(AppSharedPreference.getInstance().getMnemonicWordList());
                } catch (IOException e) {
                    e.printStackTrace();
                }
                AddressManager.getInstance();
                initLogging();
            }
        }).start();
    }

    /**
     * Workaround for bug pre-Froyo, see here for more info:
     * http://android-developers.blogspot.com/2011/09/androids-http-clients.html
     */
//    public static void disableConnectionReuseIfNecessary() {
//        // HTTP connection reuse which was buggy pre-froyo
//        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.FROYO) {
//            System.setProperty("http.keepAlive", "false");
//        }
//    }

    private void upgrade() {
        AppSharedPreference appSharedPreference = AppSharedPreference.getInstance();
        int updateCode = appSharedPreference.getUpdateCode();

        if (updateCode == -1) {
            appSharedPreference.setTransactionFeeMode(BitherjSettings.TransactionFeeMode.Normal);
            appSharedPreference.setUpdateCode(FEE_UPDATE_CODE);
        }
    }

    @Override
    protected void attachBaseContext(Context base) {
        super.attachBaseContext(base);
        MultiDex.install(this);
    }
}