package top.littlerich.virtuallocation;

import android.location.Location;
import android.util.Log;

import com.virjar.ratel.api.RatelToolKit;
import com.virjar.ratel.api.inspect.ClassLoadMonitor;
import com.virjar.ratel.api.rposed.IRposedHookLoadPackage;
import com.virjar.ratel.api.rposed.RC_MethodHook;
import com.virjar.ratel.api.rposed.RposedBridge;
import com.virjar.ratel.api.rposed.RposedHelpers;
import com.virjar.ratel.api.rposed.callbacks.RC_LoadPackage;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

import top.littlerich.virtuallocation.common.AppApplication;

/**
 * Created by xuqingfu on 2017/4/15.
 */

public class XPosedPlugin implements IRposedHookLoadPackage {

    private static final String TAG = AppApplication.tag;

    @Override
    public void handleLoadPackage(RC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable {

        Log.i(TAG, "加载Hook程序：" + loadPackageParam.packageName);

        AppApplication.queryMockGPS(RatelToolKit.sContext);
        if (AppApplication.mockGPS == null) {
            Log.i(TAG, "未配置虚拟定位");
            return;
        }
        Log.i(TAG, "虚拟定位位置:" + AppApplication.mockGPS.mLongitude + "," + AppApplication.mockGPS.mLatitude);

        handleGaoDeMap();
        handleBaiduMap();

        ClassLoadMonitor.addClassLoadMonitor(new ClassLoadMonitor.OnClassLoader() {
            @Override
            public void onClassLoad(Class<?> clazz) {
//                if (clazz.getName().contains("location")) {
//                    Log.i(AppApplication.tag, "init class: " + clazz.getName());
//                }
            }
        });
    }


    private static void handleBaiduMap() {

        //com.baidu.location.LocationClient.registerLocationListener(com.baidu.location.BDLocationListener)
        //com.baidu.location.LocationClient.registerLocationListener(com.baidu.location.BDAbstractLocationListener)
        ClassLoadMonitor.addClassLoadMonitor("com.baidu.location.LocationClient", new ClassLoadMonitor.OnClassLoader() {
            @Override
            public void onClassLoad(Class<?> clazz) {
                Log.i(AppApplication.tag, "命中百度API:" + clazz);
                RposedBridge.hookAllMethods(clazz, "registerLocationListener", new RC_MethodHook() {
                    @Override
                    protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                        //TODO param.parameterTypes is null
                        Method method = (Method) param.method;
                        param.parameterTypes = method.getParameterTypes();
                        final Object originListener = param.args[0];
                        if (originListener == null) {
                            return;
                        }
                        Class<?> BDLocationLikeListener = param.parameterTypes[0];
                        if (RatelToolKit.dexMakerProxyBuilderHelper.isProxyClass(originListener.getClass())) {
                            return;
                        }

                        InvocationHandler invocationHandler = new InvocationHandler() {
                            @Override
                            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                                if (method.getName().equals("onReceiveLocation")) {
                                    // void onReceiveLocation(AMapLocation var1);
                                    for (Object obj : args) {
                                        //com.baidu.location.BDLocation final的，所以不需要考虑被继承了
                                        if (!(obj.getClass().getName().equals("com.baidu.location.BDLocation"))) {
                                            continue;
                                        }
                                        //location.setLatitude();
                                        //location.setLongitude();
                                        RposedHelpers.callMethod(obj, "setLatitude", AppApplication.mockGPS.mLatitude);
                                        RposedHelpers.callMethod(obj, "setLongitude", AppApplication.mockGPS.mLongitude);
                                    }
                                }
                                return RposedHelpers.callMethod(originListener, method.getName(), args);
                            }
                        };

                        Object proxyListener;
                        if (BDLocationLikeListener.isInterface()) {
                            proxyListener = Proxy.newProxyInstance(BDLocationLikeListener.getClassLoader(),
                                    new Class[]{BDLocationLikeListener}, invocationHandler
                            );
                        } else {
                            proxyListener = RatelToolKit.dexMakerProxyBuilderHelper
                                    .forClass(BDLocationLikeListener)
                                    .parentClassLoader(BDLocationLikeListener.getClassLoader())
                                    .onlyMethods(BDLocationLikeListener.getDeclaredMethods())
                                    .handler(invocationHandler).build();
                        }

                        param.args[0] = proxyListener;
                    }
                });
            }
        });
    }


    private static void handleGaoDeMap() {
        //com.amap.api.location.AMapLocationClient.setLocationListener(com.amap.api.location.AMapLocationListener lis)
        ClassLoadMonitor.addClassLoadMonitor("com.amap.api.location.AMapLocationClient", new ClassLoadMonitor.OnClassLoader() {
            @Override
            public void onClassLoad(Class<?> clazz) {
                Log.i(AppApplication.tag, "命中高德API:" + clazz);

                RposedHelpers.findAndHookMethod(clazz, "setLocationListener",
                        "com.amap.api.location.AMapLocationListener",
                        new RC_MethodHook() {
                            @Override
                            protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                                //TODO param.parameterTypes is null
                                Method method = (Method) param.method;
                                param.parameterTypes = method.getParameterTypes();
                                Class<?> aMapLocationListenerInterfaceClass = param.parameterTypes[0];

                                final Object originListener = param.args[0];
                                if (originListener == null) {
                                    Log.i(AppApplication.tag, "originListener is null");
                                    return;
                                }
                                Log.i(AppApplication.tag, "原始定位回掉监听器:" + originListener.getClass().getName());

                                if (RatelToolKit.dexMakerProxyBuilderHelper.isProxyClass(originListener.getClass())) {
                                    Log.i(AppApplication.tag, "this is proxyClass");
                                    return;
                                }

                                InvocationHandler invocationHandler = new InvocationHandler() {
                                    @Override
                                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                                        if (method.getName().equals("onLocationChanged")) {
                                            Log.i(AppApplication.tag, "回掉onLocationChanged");
                                            // void onLocationChanged(AMapLocation var1);
                                            for (Object obj : args) {
                                                if (!(obj instanceof Location)) {
                                                    continue;
                                                }

                                                Location location = (Location) obj;
                                                location.setLatitude(AppApplication.mockGPS.mLatitude);
                                                location.setLongitude(AppApplication.mockGPS.mLongitude);

                                                // public void setAddress(String var1) {
                                                try {
                                                    RposedHelpers.callMethod(obj, "setAddress", AppApplication.mockGPS.address);
                                                } catch (Throwable throwable) {
                                                    throwable.printStackTrace();
                                                }

                                            }
                                        }
                                        return RposedHelpers.callMethod(originListener, method.getName(), args);
                                    }
                                };

                                Object proxyListener;
                                if (aMapLocationListenerInterfaceClass.isInterface()) {
                                    proxyListener = Proxy.newProxyInstance(aMapLocationListenerInterfaceClass.getClassLoader(),
                                            new Class[]{aMapLocationListenerInterfaceClass}, invocationHandler
                                    );
                                } else {
                                    proxyListener = RatelToolKit.dexMakerProxyBuilderHelper
                                            .forClass(aMapLocationListenerInterfaceClass)
                                            .parentClassLoader(aMapLocationListenerInterfaceClass.getClassLoader())
                                            .onlyMethods(aMapLocationListenerInterfaceClass.getDeclaredMethods())
                                            .handler(invocationHandler).build();
                                }
                                param.args[0] = proxyListener;
                            }
                        });

            }

        });
    }

}
