package com.rnboat.framework.base;

import android.app.Application;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.text.TextUtils;
import android.widget.Toast;

import com.blankj.utilcode.util.AppUtils;
import com.blankj.utilcode.util.Utils;
import com.facebook.react.ReactPackage;
import com.rnboat.framework.ConfigManager;
import com.rnboat.framework.R;
import com.rnboat.framework.diskcache.AsyncDiskCache;
import com.rnboat.framework.model.AppConfig;
import com.rnboat.framework.util.LogcatHelper;
import com.rnboat.process.RNBoatDaemonManager;
import com.rnboat.process.services.RNBoatDaemonService;
import com.tencent.bugly.crashreport.CrashReport;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 * Created by Castiel on 2018/3/31.
 *
             nHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHn.
          .MS?MMMMMMMMMMMMMMMMMM?MM~MMMMMMMMMSHMMMMMMMM(?"~\
          MMMMMH?MMMMMMMX*MM?MMX%MM/MMMMMM"HMMMMMMMMMMMMMMH
         MMMMMMMMMMMMMMMMMX*MX*MMMX?MMMMM(M!XMMMMMMMMMMMMMMMX
        XMC)?MMMMMMMMMMMMMMMhX?!?MMMMX#MM!MXMMMMMMMMMMMML   '~
'\      MMMMMMMMMMMMMMMMMMMMMMMM!~`````-`~!?MMMM)MMMMMMMMx
   `~""MMM)MMMMMMMMMMMMMMMHhHH!~           `#MM(MMMMMMMMMM>
      HM!HMMMMMMMMMMMMMMMM*?)?`                `"MMMMMMMMMX       .
     XM!MMMMMMMMMMMMMMMMMMM?~                     'MMMMMMMM:..xx!`
     M!MMMMMMMMMMMMMMMMMXH!                        MMMMXMMP"`
    \!MMMMMMMSMHHHMM?XMM?~    -:::xx..             M?XMM?".x(
    MXMMMMMMMMMM!XHMMMM":       ... `"%x          XHHHMMM*"
   \!MMMMMMMM?XMMMMMMX!'~L     '%%%+:.  `       ..MMMMM"
   'HMMMMMM?HMMMMM*XM!    h     ~\).^\~     .%""`MM?"
   'MMMMMMMMMMMMMXMMM!    -X               +%%!.MMMXk
   ?MMMMMMMMMMMXMMMMM `.   ~               `""'XMMMMX
   !MMMMMMMMMMMMMMMMMX.    '                  XMkMMX>
   XMMMMMMMMMMMMMMM?MXXXx.-`                  XXMMM!
   MMMMMMMMMMMMMMMMXMXXXXXXx.         ~~      MMMMM
   XMMMMMMMMMMMM?MMXXXXXXXXX!`         '+^  .MMM!P
   'MMM!MMMMMMMMMi?M!"`        `~%HHHHxx.  xMMMM"
   :MMMMMMMMMMMMMMM"               `\XMM .MMMMM
   XMMMMMMMMMX?MM!                   `( HMMMMM
  XMMMM)MMM"   \~                     'MMMMM*
 'MMMMfMMM"  \~                        XMMM*
.MMMMMXMM"  ^                          `MMM
XMMMM!MM"                               MM>
HMMMMXM~                                MM>
?MMMMM~                                 Xf%
 MMMMf                                  %% \
 4MMM                                    %
   `M                                     %
     %                                    %
     %                                     %
     %                 !                   %
      %                 !                   %
      %                 %                   !?%.
      %                  %                   X. %%.
       %                  %                  X!    %%.
       %                  %                  '!       %.
        %                  %                  !!         %.
        %                  %                  '!          `%
         %                  %                  !>          /%
         %                   %                 !!          % %
          %                   %                 !          \%
          %                   %                 !!          %
           %                   %                '!         %~
            %                   %                !!       %~
            %%                  %                `!     %%
            %%%                  %                %++4MMf
             ?MMx                 %                %. MMX
              *MMMx               %                 !\'MMM>
               MMMMMHx    .....xxnH                  %HMMM>
                MMMMMMMMMMMMMMMMMMM>                  MMMMX
                'MMMMMMMMMMMMMMMMMMk                  'MMMM
                 'MMMMMMMMMMMMMMMMMM                   MMMM>
                  ?MMMMMMMMMMMMMMMMMM                  'MMMX
                   MMMMMMMMMMMMMMMMMMM                  MMMM
                   XMMMMM

 */
public abstract class RNBoatApplication extends Application {
    public abstract List<ReactPackage> getPackages();
    private BroadcastReceiver mFinishReceiver;
    public static String APP_FINISH_ACTION = "APP_FINISH_ACTION";

    @Override
    public void onCreate() {
        super.onCreate();

        initConfigManager();
        Utils.init(this);
        if (AppUtils.isAppDebug())
            Toast.makeText(this, "versionCode:" + AppUtils.getAppVersionCode(),  Toast.LENGTH_LONG).show();
        initBugly();
        initUpdateService();
        configureDaemonService();

        mFinishReceiver = new FinishReceiver();
        IntentFilter filter = new IntentFilter();
        filter.addAction(APP_FINISH_ACTION);
        registerReceiver(mFinishReceiver, filter);
    }

    private void initConfigManager() {
        ConfigManager.setCacheFilesDir(this);
        ConfigManager.APPCONFIG = AsyncDiskCache.syncReadCache(ConfigManager.APP_CONFIG_KEY, AppConfig.class);
    }

    private void initBugly() {
        final Context context = getApplicationContext();
        // 设置是否为上报进程
        CrashReport.UserStrategy strategy = new CrashReport.UserStrategy(context);
        strategy.setUploadProcess(isInUIProcess(context));
        strategy.setCrashHandleCallback(new CrashReport.CrashHandleCallback() {
            public Map<String, String> onCrashHandleStart(int crashType, String errorType,
                                                          String errorMessage, String errorStack) {
                LinkedHashMap<String, String> map = new LinkedHashMap<>();
                map.put("crashType", String.valueOf(crashType));
                map.put("errorType", String.valueOf(errorType));
                map.put("errorMessage", String.valueOf(errorMessage));
                map.put("errorStack", String.valueOf(errorStack));
                return map;
            }

            @Override
            public byte[] onCrashHandleStart2GetExtraDatas(int crashType, String errorType,
                                                           String errorMessage, String errorStack) {
                try {
                    return "Extra data.".getBytes("UTF-8");
                } catch (Exception e) {
                    return null;
                }
            }
        });

        // 初始化Bugly
        // 如果通过“AndroidManifest.xml”来配置APP信息，初始化方法如下
        CrashReport.initCrashReport(context, strategy);
    }

    public void configureDaemonService() {
        if (!isInUIProcess(getApplicationContext()))
            return;

        Intent daemonIntent = new Intent(getApplicationContext(), RNBoatDaemonService.class);

        if (ConfigManager.APPCONFIG != null && ConfigManager.APPCONFIG.getApp() != null && ConfigManager.APPCONFIG.getApp().isDaemon()) {
            if (!RNBoatDaemonManager.isServiceRunning(this, "com.rnboat.process.services.RNBoatDaemonService")) {
                startService(daemonIntent);
            }
        }
        else {
            RNBoatDaemonManager.stopAllDaemonService(this);
        }
    }

    private void initUpdateService() {
        if (!isInUIProcess(getApplicationContext()))
            return;

        try {
            if (AsyncDiskCache.syncReadCache("bundleID", String.class) == null ||
                    AsyncDiskCache.syncReadCache("sv", Integer.class) == null ||
                    AsyncDiskCache.syncReadCache("env", Integer.class) == null) {
                Class<?> beReflectedClass = Class.forName(String.format("%s.R", ConfigManager.APP_PACKAGE_NAME));
                Class<?> rStringClass = null;
                for (Class<?> clz : beReflectedClass.getDeclaredClasses()) {
                    if (clz.getSimpleName().equals("string")) {
                        rStringClass = clz;
                    }
                }

                if (rStringClass == null) {
                    Toast.makeText(this, R.string.StringXMLNotInit, Toast.LENGTH_LONG).show();
                    return;
                }

                Field bundleIDField = rStringClass.getDeclaredField("bundleID");
                Field svField = rStringClass.getDeclaredField("sv");
                Field envField = rStringClass.getDeclaredField("env");
                bundleIDField.setAccessible(true);
                svField.setAccessible(true);
                envField.setAccessible(true);

                ConfigManager.BUNDLE_ID = getResources().getString((int) bundleIDField.get(null));
                ConfigManager.SV = Integer.parseInt(getResources().getString((int) svField.get(null)));
                ConfigManager.ENV = Integer.parseInt(getResources().getString((int) envField.get(null)));

                if (ConfigManager.BUNDLE_ID == null) {
                    Toast.makeText(this, R.string.BundleIDNotInit, Toast.LENGTH_LONG).show();
                    return;
                }

                if (ConfigManager.SV == -1) {
                    Toast.makeText(this, R.string.SVNotInit, Toast.LENGTH_LONG).show();
                    return;
                }

                if (ConfigManager.ENV == -1) {
                    Toast.makeText(this, R.string.ENVNotInit, Toast.LENGTH_LONG).show();
                    return;
                }

                AsyncDiskCache.syncWriteCache("bundleID", ConfigManager.BUNDLE_ID);
                AsyncDiskCache.syncWriteCache("sv", ConfigManager.SV);
                AsyncDiskCache.syncWriteCache("env", ConfigManager.ENV);
            } else {
                ConfigManager.BUNDLE_ID = AsyncDiskCache.syncReadCache("bundleID", String.class);
                ConfigManager.SV = AsyncDiskCache.syncReadCache("sv", Integer.class);
                ConfigManager.ENV = AsyncDiskCache.syncReadCache("env", Integer.class);
            }
        } catch (Exception e) {
            Toast.makeText(this, e.getMessage(), Toast.LENGTH_LONG).show();
            e.printStackTrace();
        }
    }

    private static boolean isInUIProcess(Context context) {
        // 获取当前包名
        String packageName = context.getPackageName();
        // 获取当前进程名
        String processName = getProcessName(android.os.Process.myPid());

        return processName == null || processName.equals(packageName);
    }


    /**
     * 获取进程号对应的进程名
     *
     * @param pid 进程号
     * @return 进程名
     */
    private static String getProcessName(int pid) {
        BufferedReader reader = null;
        try {
            reader = new BufferedReader(new FileReader("/proc/" + pid + "/cmdline"));
            String processName = reader.readLine();
            if (!TextUtils.isEmpty(processName)) {
                processName = processName.trim();
            }
            return processName;
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        } finally {
            try {
                if (reader != null) {
                    reader.close();
                }
            } catch (IOException exception) {
                exception.printStackTrace();
            }
        }
        return null;
    }

    public class FinishReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            if (APP_FINISH_ACTION.equals(action)) {
                System.exit(0);
            }
        }
    }
}
