/*
 * Copyright (C) 2017-2018 Manbang Group
 *
 * 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 com.wlqq.phantom.library.proxy;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.res.AssetManager;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.IdRes;
import android.support.annotation.LayoutRes;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.RequiresApi;
import android.support.annotation.RequiresPermission;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.util.AttributeSet;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import com.wlqq.phantom.library.utils.IntentUtils;
import com.wlqq.phantom.library.utils.SuppressFBWarnings;
import com.wlqq.phantom.library.utils.VLog;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;

/**
 * 实现对插件Activity方法的拦截
 */
public class PluginInterceptActivity extends FragmentActivity {
    private ContextProxy<Activity> mContentProxy;
    private OnCreateCallback mOnCreateCallback;
    private boolean mUseCompatTheme;
    private LayoutInflater mLayoutInflater;
    private int mThemeId;
    private Resources.Theme mTheme;
    private Intent mIntent;

    public void setContextProxy(ContextProxy<Activity> contextProxy) {
        mContentProxy = contextProxy;
    }

    public ContextProxy getContextProxy() {
        return mContentProxy;
    }

    //由于setTheme的调用需要在super.onCreate()调用之前才生效,这里增加一个回调在插件Activity调用
    //super.onCreate()时通知ActivityHostProxy,ActivityHostProxy在回调中调用自己的super.onCreate()
    public void setOnCreateCallback(OnCreateCallback callback) {
        mOnCreateCallback = callback;
    }

    /**
     * 与宿主占位Activity共享window,decorview,FragmentManager(系统的和support-v4包中的)后,不再需要调用其super方法,
     * 以及相关的生命周期回调方法的super方法
     */
    @SuppressWarnings("all")
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        //共享window,FragmentManager后不需要调用super方法,否则会引起一些错误
        mOnCreateCallback.onCreateCalled();
    }

    @SuppressWarnings("all")
    @Override
    protected void onPostCreate(@Nullable Bundle savedInstanceState) {
        //共享window,FragmentManager后不需要调用super方法,否则会引起一些错误
    }

    @SuppressWarnings("all")
    @Override
    protected void onStart() {
        //共享window,FragmentManager后不需要调用super方法,否则会引起一些错误
        if (mContentProxy.getContext() instanceof ActivityHostProxy) {
            ((ActivityHostProxy) mContentProxy.getContext()).callSuperOnStart();
        }
    }

    @SuppressWarnings("all")
    @Override
    protected void onResume() {
        //共享window,FragmentManager后不需要调用super方法,否则会引起一些错误
        if (mContentProxy.getContext() instanceof ActivityHostProxy) {
            ((ActivityHostProxy) mContentProxy.getContext()).callSuperOnResume();
        }
    }

    @SuppressWarnings("all")
    @Override
    protected void onPostResume() {
        //共享window,FragmentManager后不需要调用super方法,否则会引起一些错误
    }

    @SuppressWarnings("all")
    @Override
    protected void onPause() {
        //共享window,FragmentManager后不需要调用super方法,否则会引起一些错误
        if (mContentProxy.getContext() instanceof ActivityHostProxy) {
            ((ActivityHostProxy) mContentProxy.getContext()).callSuperOnPause();
        }
    }

    @SuppressWarnings("all")
    @Override
    protected void onStop() {
        //共享window,FragmentManager后不需要调用super方法,否则会引起一些错误
        if (mContentProxy.getContext() instanceof ActivityHostProxy) {
            ((ActivityHostProxy) mContentProxy.getContext()).callSuperOnStop();
        }
    }

    @SuppressWarnings("all")
    @Override
    protected void onDestroy() {
        //共享window,FragmentManager后不需要调用super方法,否则会引起一些错误
        if (mContentProxy.getContext() instanceof ActivityHostProxy) {
            ((ActivityHostProxy) mContentProxy.getContext()).callSuperOnDestroy();
        }
    }

    @Override
    public AssetManager getAssets() {
        return mContentProxy.getAssets();
    }

    @Override
    public Resources getResources() {
        return mContentProxy.getResources();
    }

    @Override
    public Context getApplicationContext() {
        return mContentProxy.getApplicationContext();
    }

    @Override
    public void attachBaseContext(Context newBase) {
        super.attachBaseContext(newBase);
    }

    @Override
    public ClassLoader getClassLoader() {
        return mContentProxy.getClassLoader();
    }

    @Override
    public void startActivity(Intent intent) {
        mContentProxy.startActivity(intent);
    }

    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
    @Override
    public void startActivity(Intent intent, @Nullable Bundle options) {
        mContentProxy.startActivity(intent, options);
    }

    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
    @Override
    public void startActivities(Intent[] intents, @Nullable Bundle options) {
        mContentProxy.startActivities(intents, options);
    }

    @Override
    public void startActivities(Intent[] intents) {
        mContentProxy.startActivities(intents);
    }

    @Override
    public void startActivityFromFragment(Fragment fragment, Intent intent, int requestCode) {
        if (mContentProxy.getContext() instanceof FragmentActivity) {
            ((FragmentActivity) mContentProxy.getContext()).startActivityFromFragment(fragment,
                    mContentProxy.setActivityIntentExtra(intent), requestCode);
        }
    }

    @Override
    public void startActivityFromFragment(Fragment fragment, Intent intent, int requestCode, @Nullable Bundle options) {
        if (mContentProxy.getContext() instanceof FragmentActivity) {
            ((FragmentActivity) mContentProxy.getContext())
                    .startActivityFromFragment(fragment,
                            mContentProxy.setActivityIntentExtra(intent), requestCode, options);
        }
    }

    @Override
    public void startActivityFromFragment(@NonNull android.app.Fragment fragment, Intent intent, int requestCode) {
        mContentProxy.getContext().startActivityFromFragment(fragment,
                mContentProxy.setActivityIntentExtra(intent), requestCode);
    }

    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
    @Override
    public void startActivityFromFragment(@NonNull android.app.Fragment fragment, Intent intent,
                                          int requestCode, @Nullable Bundle options) {
        mContentProxy.getContext()
                .startActivityFromFragment(fragment,
                        mContentProxy.setActivityIntentExtra(intent), requestCode, options);
    }

    @Override
    public void startActivityFromChild(@NonNull Activity child, @RequiresPermission Intent intent, int requestCode) {
        mContentProxy.getContext().startActivityFromChild(child,
                mContentProxy.setActivityIntentExtra(intent), requestCode);
    }

    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
    @Override
    public void startActivityFromChild(@NonNull Activity child, @RequiresPermission Intent intent,
                                       int requestCode, @Nullable Bundle options) {
        mContentProxy.getContext().startActivityFromChild(child,
                mContentProxy.setActivityIntentExtra(intent), requestCode, options);
    }

    @Override
    public boolean startActivityIfNeeded(@RequiresPermission @NonNull Intent intent, int requestCode) {
        return mContentProxy.getContext()
                .startActivityIfNeeded(mContentProxy.setActivityIntentExtra(intent), requestCode);
    }

    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
    @Override
    public boolean startActivityIfNeeded(@RequiresPermission @NonNull Intent intent, int requestCode,
                                         @Nullable Bundle options) {
        return mContentProxy.getContext().startActivityIfNeeded(mContentProxy.setActivityIntentExtra(intent),
                requestCode, options);
    }

    @Override
    public void startActivityForResult(Intent intent, int requestCode) {
        mContentProxy.getContext().startActivityForResult(mContentProxy.setActivityIntentExtra(intent), requestCode);
    }

    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
    @Override
    public void startActivityForResult(Intent intent, int requestCode, @Nullable Bundle options) {
        mContentProxy.getContext()
                .startActivityForResult(mContentProxy.setActivityIntentExtra(intent), requestCode, options);
    }


    @Override
    public ComponentName startService(Intent service) {
        return mContentProxy.startService(service);
    }

    @Override
    public boolean stopService(Intent name) {
        return mContentProxy.stopService(name);
    }

    @Override
    public boolean bindService(Intent service, ServiceConnection conn, int flags) {
        return mContentProxy.bindService(service, conn, flags);
    }

    @Override
    public Object getSystemService(@NonNull String name) {
        if (Context.LAYOUT_INFLATER_SERVICE.equals(name)) {
            return getLayoutInflater();
        }
        return super.getSystemService(name);
    }

    @NonNull
    @Override
    public LayoutInflater getLayoutInflater() {
        if (null == mLayoutInflater) {
            initLayoutInflater();
        }
        return mLayoutInflater;
    }

    private void initLayoutInflater() {
        mLayoutInflater = (LayoutInflater) super.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        if (null != mLayoutInflater) {
            mLayoutInflater.setFactory2(this);
        }
    }

    @SuppressLint("MissingSuperCall")
    @Override
    protected void onSaveInstanceState(Bundle outState) {
        //共享window,FragmentManager后不需要调用super方法,否则会引起一些错误
    }

    @Override
    protected void onRestoreInstanceState(Bundle savedInstanceState) {
        //共享window,FragmentManager后不需要调用super方法,否则会引起一些错误
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        //共享window,FragmentManager后不需要调用super方法,否则会引起一些错误
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
            @NonNull int[] grantResults) {
        //共享window,FragmentManager后不需要调用super方法,否则会引起一些错误
    }

    @Override
    public View findViewById(@IdRes int id) {
        return mContentProxy.getContext().findViewById(id);
    }

    @Nullable
    @Override
    public View getCurrentFocus() {
        return mContentProxy.getContext().getCurrentFocus();
    }

    /**
     * 设置 Activity 切换动画
     * <p>
     * 动画资源不能放到插件中,只能使用 Android 系统提供的动画资源或将动画资源放到宿主中
     *
     * @param enterAnim A resource ID of the animation resource to use for the incoming activity.  Use 0 for no
     *                  animation.
     * @param exitAnim  A resource ID of the animation resource to use for the outgoing activity.  Use 0 for no
     *                  animation.
     */

    @Override
    public void overridePendingTransition(int enterAnim, int exitAnim) {
        mContentProxy.getContext().overridePendingTransition(enterAnim, exitAnim);
    }

    @Override
    public void setContentView(@LayoutRes int layoutResID) {
        LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        if (null == inflater) {
            return;
        }
        setContentView(inflater.inflate(layoutResID, null));
    }

    @Override
    public void setContentView(View view) {
        mContentProxy.getContext().setContentView(view);
    }

    @Override
    public void setContentView(View view, ViewGroup.LayoutParams params) {
        mContentProxy.getContext().setContentView(view, params);
    }

    @Override
    public void addContentView(View view, ViewGroup.LayoutParams params) {
        mContentProxy.getContext().addContentView(view, params);
    }

    @Override
    public FragmentManager getSupportFragmentManager() {
        return mContentProxy.getContext() instanceof FragmentActivity
                ? ((FragmentActivity) mContentProxy.getContext()).getSupportFragmentManager() : null;
    }

    @Override
    public android.app.FragmentManager getFragmentManager() {
        return mContentProxy.getContext().getFragmentManager();
    }

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        //按键事件由宿主处理,这里需要调用宿主的super方法,而不是本类的super方法
        if (mContentProxy.getContext() instanceof ActivityHostProxy) {
            return ((ActivityHostProxy) mContentProxy.getContext()).callSuperOnKeyDown(keyCode, event);
        } else {
            return false;
        }
    }

    @Override
    public boolean onKeyUp(int keyCode, KeyEvent event) {
        //按键事件由宿主处理,这里需要调用宿主的super方法,而不是本类的super方法
        if (mContentProxy.getContext() instanceof ActivityHostProxy) {
            return ((ActivityHostProxy) mContentProxy.getContext()).callSuperOnKeyUp(keyCode, event);
        } else {
            return false;
        }
    }

    @Override
    public void onBackPressed() {
        //按键事件由宿主处理,这里需要调用宿主的super方法,而不是本类的super方法
        if (mContentProxy.getContext() instanceof ActivityHostProxy) {
            ((ActivityHostProxy) mContentProxy.getContext()).callSuperOnBackPressed();
        }
    }

    @SuppressWarnings("unchecked")
    @SuppressFBWarnings(value = "REC_CATCH_EXCEPTION",
            justification = "exception would be thrown when View class is not in plugin")
    @Override
    public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
        if (!mUseCompatTheme && !name.contains(".")) {
            return null;
        }

        try {
            Class viewCls;
            //android5.0及以上默认就是material风格
            if (mUseCompatTheme && Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
                viewCls = mContentProxy.getClassLoader().findClassFast(getCompatV7ViewClass(name));
            } else {
                viewCls = mContentProxy.getClassLoader().findClassFast(name);
            }
            if (null != viewCls) {
                Constructor<? extends View> constructor = viewCls.getConstructor(
                        Context.class, AttributeSet.class);
                constructor.setAccessible(true);
                return constructor.newInstance(context, attrs);
            }
        } catch (Exception e) {
            // 忽略掉该异常,杨锋的解释
            // LayoutInflater 会调用这个函数创建很多 View,但是并不是所有的 View 都能在这个函数中创
            // 建出来,当 Activity 的 onCreateView 返回 null,LayoutInflater 就会继续调用其他函数
            // 来创建 View, 比如这里的参数 viewClsStr 的值是 LinearLayout,这个时候 findClass 是
            // 会出错的,而LayoutInflater 是根据 LinearLayout 这个字符串来调用其他函数创建出 LinearLayout
        }

        return null;
    }

    /**
     * 检查当前Activity是否是使用appcompat-v7主题,
     * 判断方式参考appcompat-v7 25.3.1版本,其他版本判断方式可能不同
     *
     * @return true使用appcompat-v7主题,false其他主题
     */
    @SuppressFBWarnings("REC_CATCH_EXCEPTION")
    private boolean useAppCompatTheme() {
        try {
            Class styleCls = mContentProxy.getClassLoader().findClassFast("android.support.v7.appcompat.R$styleable");
            Field themeField = styleCls.getDeclaredField("AppCompatTheme");
            themeField.setAccessible(true);
            int[] compatTheme = (int[]) themeField.get(null);
            Field actionBarField = styleCls.getDeclaredField("AppCompatTheme_windowActionBar");
            actionBarField.setAccessible(true);
            int compatActionBar = actionBarField.getInt(null);
            TypedArray a = obtainStyledAttributes(compatTheme);
            boolean res = a.hasValue(compatActionBar);
            a.recycle();
            return res;
        } catch (Exception e) {
            // 当插件中没有使用 appcompat 主题时,会进入到该异常分支。属于正常情况,不需要输出日志
            return false;
        }
    }

    /**
     * 获取基本控件对应的appcompat包中的类
     *
     * @param name 要创建控件的名字
     * @return appcompat包中对应的类名
     */
    private String getCompatV7ViewClass(String name) {
        String cls = name;
        switch (name) {
            case "TextView":
                cls = "android.support.v7.widget.AppCompatTextView";
                break;
            case "ImageView":
                cls = "android.support.v7.widget.AppCompatImageView";
                break;
            case "Button":
                cls = "android.support.v7.widget.AppCompatButton";
                break;
            case "EditText":
                cls = "android.support.v7.widget.AppCompatEditText";
                break;
            case "Spinner":
                cls = "android.support.v7.widget.AppCompatSpinner";
                break;
            case "ImageButton":
                cls = "android.support.v7.widget.AppCompatImageButton";
                break;
            case "CheckBox":
                cls = "android.support.v7.widget.AppCompatCheckBox";
                break;
            case "RadioButton":
                cls = "android.support.v7.widget.AppCompatRadioButton";
                break;
            case "CheckedTextView":
                cls = "android.support.v7.widget.AppCompatCheckedTextView";
                break;
            case "AutoCompleteTextView":
                cls = "android.support.v7.widget.AppCompatAutoCompleteTextView";
                break;
            case "MultiAutoCompleteTextView":
                cls = "android.support.v7.widget.AppCompatMultiAutoCompleteTextView";
                break;
            case "RatingBar":
                cls = "android.support.v7.widget.AppCompatRatingBar";
                break;
            case "SeekBar":
                cls = "android.support.v7.widget.AppCompatSeekBar";
                break;
            default:
        }

        return cls;
    }

    @Override
    public Intent getIntent() {
        if (mContentProxy.getContext() instanceof ActivityHostProxy) {
            if (null == mIntent) {
                mIntent = new Intent(mContentProxy.getContext().getIntent());
                final PluginClassLoader pluginClassLoader = mContentProxy.getClassLoader();
                if (pluginClassLoader == null) {
                    VLog.w("invoke getIntent, pluginClassLoader is null !!!");
                } else {
                    IntentUtils.mergeIntentExtras(mIntent, pluginClassLoader);
                }
            }

            return mIntent;
        }

        return mContentProxy.getContext().getIntent();
    }

    @Override
    public void setTheme(int themeId) {
        if (mThemeId == themeId) {
            return;
        }

        mThemeId = themeId;
        final boolean first = mTheme == null;
        if (first) {
            mTheme = getResources().newTheme();
            final Resources.Theme theme = getBaseContext().getTheme();
            if (theme != null) {
                mTheme.setTo(theme);
            }
        }
        onApplyThemeResource(mTheme, mThemeId, first);

        mUseCompatTheme = useAppCompatTheme();
        if (mContentProxy.getContext() instanceof ActivityHostProxy) {
            mContentProxy.getContext().setTheme(themeId);
        }
    }

    @Override
    public Resources.Theme getTheme() {
        return mTheme;
    }

    @Override
    public boolean isFinishing() {
        return mContentProxy.getContext().isFinishing();
    }

    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR1)
    @Override
    public boolean isDestroyed() {
        return mContentProxy.getContext().isDestroyed();
    }

    @Override
    public boolean isChangingConfigurations() {
        return mContentProxy.getContext().isChangingConfigurations();
    }
}