/*
 * Copyright (c) 2018 The sky Authors.
 *
 * 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.sky.xposed.aweme.hook;

import android.app.Activity;
import android.app.Dialog;
import android.app.FragmentManager;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.IBinder;
import android.os.UserHandle;
import android.text.TextUtils;
import android.view.Gravity;
import android.view.View;
import android.widget.HorizontalScrollView;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;

import com.sky.xposed.aweme.BuildConfig;
import com.sky.xposed.aweme.Constant;
import com.sky.xposed.aweme.R;
import com.sky.xposed.aweme.hook.base.BaseHook;
import com.sky.xposed.aweme.hook.handler.AutoAttentionHandler;
import com.sky.xposed.aweme.hook.handler.AutoCommentHandler;
import com.sky.xposed.aweme.hook.handler.AutoDownloadHandler;
import com.sky.xposed.aweme.hook.handler.AutoLikeHandler;
import com.sky.xposed.aweme.hook.handler.AutoPlayHandler;
import com.sky.xposed.aweme.ui.dialog.SettingsDialog;
import com.sky.xposed.aweme.util.ToStringUtil;
import com.sky.xposed.common.ui.util.LayoutUtil;
import com.sky.xposed.common.util.Alog;
import com.sky.xposed.common.util.DisplayUtil;
import com.sky.xposed.common.util.ResourceUtil;
import com.sky.xposed.common.util.ToastUtil;
import com.sky.xposed.javax.MethodHook;
import com.squareup.picasso.Picasso;

import java.util.ArrayList;
import java.util.List;

import de.robv.android.xposed.XC_MethodHook;
import de.robv.android.xposed.XposedHelpers;
import de.robv.android.xposed.callbacks.XC_LoadPackage;

public class AweMeHook extends BaseHook {

    private AutoPlayHandler mAutoPlayHandler;
    private AutoLikeHandler mAutoLikeHandler;
    private AutoAttentionHandler mAutoAttentionHandler;
    private AutoCommentHandler mAutoCommentHandler;
    private AutoDownloadHandler mAutoDownloadHandler;

    private XC_MethodHook.Unhook mCutVideoUnhook;

    private VersionManager.Config mVersionConfig = mVersionManager.getSupportConfig();

    @Override
    public void onHandleLoadPackage(XC_LoadPackage.LoadPackageParam param) {

        mAutoPlayHandler = new AutoPlayHandler(mHookManager);
        mAutoLikeHandler = new AutoLikeHandler(mHookManager);
        mAutoAttentionHandler = new AutoAttentionHandler(mHookManager);
        mAutoCommentHandler = new AutoCommentHandler(mHookManager);
        mAutoDownloadHandler = new AutoDownloadHandler(mHookManager);

        if (BuildConfig.DEBUG) testHook();

        // 自动播放Hook
        autoPlayHook();

        // 视频切换Hook
        videoSwitchHook();

        // 设置入口Hook
        settingsHook();

        // 保存视频Hook
        saveVideoHook();

        // 移除视频限制
        removeLimitHook();

        // 移除广告Hook
        removeAdHook();

        // 更新Hook
        updateHook();

        // 其他的Hook
        otherHook();
    }

    public void onModifyValue(String key, Object value) {

        if (Constant.Preference.AUTO_PLAY.equals(key)) {
            // 设置自动播放
            mAutoPlayHandler.setPlay((Boolean) value);
        } else if (Constant.Preference.AUTO_PLAY_TYPE.equals(key)) {
            // 切换播放类型-开始播放
            mAutoPlayHandler.switchType((Integer) value);
            mAutoPlayHandler.start();
        }
    }

    /**
     * 自动播放Hook方法
     */
    private void autoPlayHook() {

        findMethod(
                mVersionConfig.classBaseListFragment,
                mVersionConfig.methodOnResume)
                .hook(new MethodHook.AfterCallback() {
                    @Override
                    public void onAfter(XC_MethodHook.MethodHookParam param) {
                        // 保存当前对象
                        mObjectManager.setViewPager(XposedHelpers.getObjectField(
                                param.thisObject, mVersionConfig.fieldMViewPager));

                        // 开始播放
                        mAutoPlayHandler.start();
                    }
                });

        findMethod(
                mVersionConfig.classBaseListFragment,
                mVersionConfig.methodOnPause)
                .hook(new MethodHook.AfterCallback() {
                    @Override
                    public void onAfter(XC_MethodHook.MethodHookParam methodHookParam) {
                        // 重置对象
                        mObjectManager.setViewPager(null);

                        // 停止播放
                        mAutoPlayHandler.stop();
                    }
                });

        findMethod(
                mVersionConfig.classHomeChange,
                mVersionConfig.methodHomeChange,
                String.class)
                .hook(new MethodHook.AfterCallback() {
                    @Override
                    public void onAfter(XC_MethodHook.MethodHookParam param) {

                        // 获取Tab切换的名称
                        String name = (String) param.args[0];

                        if ("HOME".equals(name)) {
                            // 启动
                            mAutoPlayHandler.start();
                        } else {
                            // 停止
                            mAutoPlayHandler.stop();
                        }
                    }
                });

        findMethod(
                mVersionConfig.classBaseListFragment,
                mVersionConfig.methodPlayComplete,
                String.class)
                .hook(new MethodHook.BeforeCallback() {
                    @Override
                    public void onBefore(XC_MethodHook.MethodHookParam methodHookParam) {
                        // 播放下一个
                        mAutoPlayHandler.next();
                    }
                });
    }

    private void videoSwitchHook() {

        findMethod(
                mVersionConfig.classVerticalViewPager,
                mVersionConfig.methodVerticalViewPagerChange,
                int.class, boolean.class, boolean.class, int.class)
                .hook(new MethodHook.AfterCallback() {
                    @Override
                    public void onAfter(XC_MethodHook.MethodHookParam param) {
                        // 切换的下标
                        int position = (int) param.args[0];

                        mAutoLikeHandler.cancel();
                        mAutoLikeHandler.like(position);

                        mAutoAttentionHandler.cancel();
                        mAutoAttentionHandler.attention(position);

                        // 点击评论
                        mAutoCommentHandler.cancel();
                        mAutoCommentHandler.comment();

                        // 下载视频
                        mAutoDownloadHandler.download(position);
                    }
                });
    }

    private void settingsHook() {

        if (TextUtils.isEmpty(mVersionConfig.classMyProfileFragment)) {
            // 不需要处理,新版本不支持 2.3.0以后
            return ;
        }

        Class fragmentClass = findClass(mVersionConfig.classMyProfileFragment);
        Class adapterClass = findClass(mVersionConfig.classMenuAdapter);
        Class adapterDataClass = findClass(mVersionConfig.classMenuAdapterData);

        // 添加入口菜单项
        final Object itemData = XposedHelpers.newInstance(
                adapterDataClass, Constant.Name.TITLE, false);

        findConstructor(adapterClass, Context.class, List.class)
                .hook(new MethodHook.AfterCallback() {
                    @Override
                    public void onAfter(XC_MethodHook.MethodHookParam param) {
                        // 添加到列表中
                        List list = (List) param.args[1];
                        list.add(itemData);
                    }
                });


        findMethod(
                fragmentClass, mVersionConfig.methodMenuAction,
                fragmentClass, String.class)
                .replace(new MethodHook.ReplaceCallback() {

                    @Override
                    public Object onReplace(XC_MethodHook.MethodHookParam param) {

                        Activity activity = (Activity) XposedHelpers
                                .callMethod(param.args[0], mVersionConfig.methodGetActivity);
                        String name = (String) param.args[1];

                        if (Constant.Name.TITLE.equals(name)) {
                            // 跳转到配置界面
                            SettingsDialog dialog = new SettingsDialog();
                            dialog.show(activity.getFragmentManager(), "settings");
                            return null;
                        }
                        return invokeOriginalMethod(param);
                    }
                });
    }

    /**
     * 手动保存视频Hook方法
     */
    private void saveVideoHook() {

        findMethod(
                mVersionConfig.classShareFragment,
                mVersionConfig.methodOnCreate,
                Bundle.class)
                .hook(new MethodHook.AfterCallback() {
                    @Override
                    public void onAfter(XC_MethodHook.MethodHookParam param) {
                        // 注入View
                        injectionView((Dialog) param.thisObject);
                    }
                });
    }

    /**
     * 移除广告的Hook
     */
    private void removeAdHook() {

        // 移除推荐广告
        findMethod(mVersionConfig.classFeedApi, mVersionConfig.methodFeedList,
                mVersionConfig.classFeedItemList)
                .hook(new MethodHook.BeforeCallback() {

                    @Override
                    public void onBefore(XC_MethodHook.MethodHookParam param) {

                        if (mUserConfigManager.isRemoveAd()) {
                            // 移除广告
                            removeFeedAd(param.args[0]);
                        }
                    }
                });


        findMethod(
                mVersionConfig.classSplashActivity,
                mVersionConfig.methodSplashOnResume)
                .hook(new MethodHook.AfterCallback() {
                    @Override
                    public void onAfter(XC_MethodHook.MethodHookParam param) {

                        if (mUserConfigManager.isRemoveAd()) {
                            // 路过广告
                            XposedHelpers.callMethod(param.thisObject,
                                    mVersionConfig.methodSplashActivitySkip, new Bundle());
                        }
                    }
                });
    }

    /**
     * 更新的Hook
     */
    private void updateHook() {

        if (TextUtils.isEmpty(mVersionConfig.classAppUpdate)) {
            // 不需要处理,支持2.2.1以后
            return ;
        }

        findMethod(
                mVersionConfig.classAppUpdate,
                mVersionConfig.methodAppUpdate)
                .replace(new MethodHook.ReplaceCallback() {
                    @Override
                    public Object onReplace(XC_MethodHook.MethodHookParam param) {

                        if (mUserConfigManager.isDisableUpdate()) {
                            // 直接返回false,不需要更新
                            return false;
                        }
                        return invokeOriginalMethod(param);
                    }
                });
    }

    /**
     * 其他的Hook
     */
    private void otherHook() {

        findMethod(
                mVersionConfig.classVideoViewHolder,
                mVersionConfig.methodCopyDesc)
                .hook(new MethodHook.AfterCallback() {
                    @Override
                    public void onAfter(XC_MethodHook.MethodHookParam param) {

                        // 处理长按事件
                        View mDescView = (View) XposedHelpers
                                .getObjectField(param.thisObject, mVersionConfig.fieldDescView);
                        mDescView.setOnLongClickListener(mOnCopyListener);
                    }
                });
    }

    private void injectionView(final Dialog dialog) {

        final FragmentManager fragmentManager =
                dialog.getOwnerActivity().getFragmentManager();

        int left = DisplayUtil.dip2px(mContext, 4);
        int top = DisplayUtil.dip2px(mContext, 16);

        HorizontalScrollView scrollView = new HorizontalScrollView(mContext);
        scrollView.setPadding(left, top, 0, 0);

        LinearLayout contentLayout = new LinearLayout(mContext);
        contentLayout.setOrientation(LinearLayout.HORIZONTAL);

        LinearLayout.LayoutParams params = LayoutUtil.newLinearLayoutParams(
                DisplayUtil.dip2px(dialog.getContext(), 64),
                LinearLayout.LayoutParams.WRAP_CONTENT);

        LinearLayout configLayout = newButtonView(
                dialog, R.drawable.ic_aweme, Constant.Name.TITLE,
                new View.OnClickListener() {
                    @Override
                    public void onClick(View view) {
                        // 跳转到配置界面
                        dialog.dismiss();
                        SettingsDialog dialog = new SettingsDialog();
                        dialog.show(fragmentManager, "settings");
                    }
                });

        LinearLayout downloadLayout = newButtonView(
                dialog, R.drawable.ic_download, "无水印保存",
                new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                // 保存视频到本地
                mAutoDownloadHandler.download();
                dialog.dismiss();
            }
        });

        LinearLayout linearLayout = dialog.findViewById(
                ResourceUtil.getId(dialog.getContext(), mVersionConfig.idShareLayout));

        // 添加配置入口
        contentLayout.addView(configLayout, params);
        // 添加保存视频
        contentLayout.addView(downloadLayout, params);

        scrollView.addView(contentLayout);
        linearLayout.addView(scrollView, 5, LayoutUtil.newWrapViewGroupParams());
    }

    private LinearLayout newButtonView(final Dialog dialog,
                                       int imageRes, String desc, View.OnClickListener listener) {

        ImageView imageView = new ImageView(dialog.getContext());
        imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
        Picasso.get().load(ResourceUtil.resourceIdToUri(
                BuildConfig.APPLICATION_ID, imageRes)).into(imageView);

        TextView textView = new TextView(dialog.getContext());
        textView.setText(desc);
        textView.setGravity(Gravity.CENTER);
        textView.setTextSize(12);

        LinearLayout layout = new LinearLayout(dialog.getContext());
        layout.setGravity(Gravity.CENTER_HORIZONTAL);
        layout.setOrientation(LinearLayout.VERTICAL);

        int width = DisplayUtil.dip2px(dialog.getContext(), 48);
        int left = DisplayUtil.dip2px(dialog.getContext(), 6);
        int bottom = DisplayUtil.dip2px(dialog.getContext(), 15);
        int top = DisplayUtil.dip2px(dialog.getContext(), 6);
        LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(width, width);
        params.setMargins(0, top, 0, bottom);

        layout.setPadding(left, 0, 0, 0);
        layout.addView(imageView, params);
        layout.addView(textView, LayoutUtil.newWrapLinearLayoutParams());

        layout.setOnClickListener(listener);

        return layout;
    }

    private void removeLimitHook() {

        findMethod(
                mVersionConfig.classVideoRecordNewActivity,
                mVersionConfig.methodOnCreate,
                Bundle.class)
                .hook(new MethodHook.AfterCallback() {
                    @Override
                    public void onAfter(XC_MethodHook.MethodHookParam param) {

                        if (mUserConfigManager.isRemoveLimit()) {

                            long limitTime = mUserConfigManager.getRecordVideoTime();

                            // 重新设置限制时长
                            Object shortVideoContext = XposedHelpers.getObjectField(
                                    param.thisObject, mVersionConfig.fieldShortVideoContext);
                            XposedHelpers.setLongField(
                                    shortVideoContext, mVersionConfig.fieldMaxDuration, limitTime);
                        }
                    }
                });

        // 解除上传视频的时间限制
        findMethod(mVersionConfig.classCutVideoActivity,
                mVersionConfig.methodCutVideoTime)
                .replace(new MethodHook.ReplaceCallback() {
                    @Override
                    public Object onReplace(XC_MethodHook.MethodHookParam param) {

                        if (mUserConfigManager.isRemoveLimit()) {
                            // 返回自定义时间
                            return (int) (mUserConfigManager.getRecordVideoTime() / 1000);
                        }
                        return invokeOriginalMethod(param);
                    }
                });


        if (TextUtils.isEmpty(mVersionConfig.classCutVideoUtil)) {
            // 不需要处理
            return ;
        }

        // 兼容Vxp,所以必须延后Hook。不然会出异常
        findMethod(mVersionConfig.classCutMultiVideo,
                mVersionConfig.methodOnCreate, Bundle.class)
                .hook(new MethodHook.BeforeCallback() {
                    @Override
                    public void onBefore(XC_MethodHook.MethodHookParam methodHookParam) {

                        if (mCutVideoUnhook != null) return;

                        // 2.2.1新版本解除上传视频的时间限制
                        mCutVideoUnhook = findMethod(
                                mVersionConfig.classCutVideoUtil,
                                mVersionConfig.methodCutVideoTime2,
                                long.class)
                                .replace(new MethodHook.ReplaceCallback() {
                                    @Override
                                    public Object onReplace(XC_MethodHook.MethodHookParam param) {

                                        if (mUserConfigManager.isRemoveLimit()) {

                                            long curTime = (long) param.args[0];
                                            long setTime = mUserConfigManager.getRecordVideoTime();

                                            // 返回自定义时间
                                            return curTime > setTime ? setTime : curTime;
                                        }
                                        return invokeOriginalMethod(param);
                                    }
                                });
                    }
                });
    }

    /**
     * 移除推荐的广告
     * @param feedList
     * @return
     */
    private Object removeFeedAd(Object feedList) {

        if (feedList == null) return null;

        try {
            List<Object> newItems = new ArrayList<>();
            List<Object> items = (List<Object>) XposedHelpers
                    .getObjectField(feedList, mVersionConfig.fieldFeedListItems);

            for (Object item : items) {
                // 只添加非广告视频
                if (!isAd(item)) newItems.add(item);
            }

            // 重新设置列表数据
            XposedHelpers.setObjectField(feedList,
                    mVersionConfig.fieldFeedListItems, newItems);
        } catch (Throwable tr) {
            Alog.e("移除广告异常", tr);
        }
        return feedList;
    }

    /**
     * 判断当前视频是不是广告
     * @param aweme
     * @return
     */
    private boolean isAd(Object aweme) {
        return (boolean) XposedHelpers.callMethod(aweme, mVersionConfig.methodAwemeIsAd)
                || (boolean) XposedHelpers.callMethod(aweme, mVersionConfig.methodAwemeIsIsAppAd);
    }

    /**
     * 处理复制事件的
     */
    private final View.OnLongClickListener mOnCopyListener = new View.OnLongClickListener() {

        @Override
        public boolean onLongClick(View v) {

            if (!mUserConfigManager.isCopyVideoDesc()) {
                return false;
            }

            try {
                // 把信息复制到粘贴板上
                ClipboardManager cm = (ClipboardManager)
                        getContext().getSystemService(Context.CLIPBOARD_SERVICE);
                cm.setPrimaryClip(ClipData.newPlainText(null, ((TextView) v).getText()));
                ToastUtil.show("内容已复制到粘贴板!");
            } catch (Throwable tr) {
                Alog.e("出异常了", tr);
                ToastUtil.show("内容复制到粘贴板出错了!");
            }
            return true;
        }
    };

    private void testHook() {

        findMethod("android.app.Instrumentation", "execStartActivity",
                Context.class, IBinder.class, IBinder.class,
                Activity.class, Intent.class, int.class, Bundle.class)
                .hook(new MethodHook.BeforeCallback() {
                    @Override
                    public void onBefore(XC_MethodHook.MethodHookParam param) {

                        Intent intent = (Intent) param.args[4];
                        ToStringUtil.toString("Instrumentation#execStartActivity: " + intent.getComponent(), intent);
                    }
                });

        findMethod("android.app.Instrumentation", "execStartActivity",
                Context.class, IBinder.class, IBinder.class,
                Activity.class, Intent.class, int.class,
                Bundle.class, UserHandle.class)
                .hook(new MethodHook.BeforeCallback() {
                    @Override
                    public void onBefore(XC_MethodHook.MethodHookParam param) {

                        Intent intent = (Intent) param.args[4];
                        ToStringUtil.toString("Instrumentation#execStartActivity: " + intent.getComponent(), intent);
                    }
                });

        findMethod(
                "android.app.Dialog", "show")
                .hook(new MethodHook.BeforeCallback() {
                    @Override
                    public void onBefore(XC_MethodHook.MethodHookParam param) {

                        Alog.d(">>>>>>>>>>>>>> dialog " + param.thisObject);
                    }
                });


//        // DirectOpenType("record_plan", b.Integer, Integer.valueOf(0)),
//        Class aClass = findClass("com.ss.android.ugc.aweme.n.a.a");
//
//        findAndHookMethod("com.ss.android.ugc.aweme.n.a",
//                "b",
//                aClass,
//                new XC_MethodHook() {
//                    @Override
//                    protected void afterHookedMethod(MethodHookParam param) throws Throwable {
//                        super.afterHookedMethod(param);
//
//                        param.setResult(1);
//                    }
//                });
    }
}