/*
 * Copyright (C) 2015 Peter Gregus for GravityBox Project (C3C076@xda)
 *
 * 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.wrbug.gravitybox.nougat;

import java.lang.reflect.Method;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.media.AudioManager;
import android.media.Ringtone;
import android.net.Uri;
import android.os.Handler;

import com.wrbug.gravitybox.nougat.ledcontrol.QuietHours;
import com.wrbug.gravitybox.nougat.preference.IncreasingRingPreference;
import com.wrbug.gravitybox.nougat.preference.IncreasingRingPreference.ConfigStore;

import de.robv.android.xposed.XC_MethodHook;
import de.robv.android.xposed.XSharedPreferences;
import de.robv.android.xposed.XposedBridge;
import de.robv.android.xposed.XposedHelpers;

public class ModRinger {
    public static final String PACKAGE_NAME = "com.android.server.telecom";
    private static final String TAG = "GB:ModRinger";
    private static final boolean DEBUG = BuildConfig.DEBUG;

    private static ConfigStore mRingerConfig;
    private static float mIncrementAmount;
    private static float mCurrentIncrementVolume;
    private static Ringtone mRingtone;
    private static Handler mHandler;
    private static Object mAsyncRinger;

    private static void log(String message) {
        XposedBridge.log(TAG + ": " + message);
    }

    private static BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
           if (intent.getAction().equals(IncreasingRingPreference.ACTION_INCREASING_RING_CHANGED) &&
                   intent.getIntExtra(IncreasingRingPreference.EXTRA_STREAM_TYPE, -1) ==
                       AudioManager.STREAM_RING) {
               mRingerConfig.enabled = intent.getBooleanExtra(
                       IncreasingRingPreference.EXTRA_ENABLED, false);
               mRingerConfig.minVolume = intent.getFloatExtra(
                       IncreasingRingPreference.EXTRA_MIN_VOLUME, 0.1f);
               mRingerConfig.rampUpDuration = intent.getIntExtra(
                       IncreasingRingPreference.EXTRA_RAMP_UP_DURATION, 10);
               if (DEBUG) log(mRingerConfig.toString());
           }
        }
    };

    private static Runnable mRunnable = new Runnable() {
        @Override
        public void run() {
            if (mRingtone == null) return;
            mCurrentIncrementVolume += mIncrementAmount;
            if (mCurrentIncrementVolume > 1f) mCurrentIncrementVolume = 1f;
            if (DEBUG) log("Increasing ringtone volume to " +
                    Math.round(mCurrentIncrementVolume * 100f) + "%");
            setVolume(mCurrentIncrementVolume);
            if (mCurrentIncrementVolume < 1f) {
                mHandler.postDelayed(this, 1000);
            }
        }
    };

    public static void init(final XSharedPreferences prefs, final ClassLoader classLoader) {
        try {
            final String CLASS_RINGTONE_PLAYER = Utils.isSamsungRom() ? 
                    "com.android.server.telecom.secutils.SecAsyncRingtonePlayer" :
                    "com.android.server.telecom.AsyncRingtonePlayer";

            final XSharedPreferences qhPrefs = new XSharedPreferences(GravityBox.PACKAGE_NAME, "quiet_hours");
            qhPrefs.makeWorldReadable();
            final Class<?> clsRingtonePlayer = XposedHelpers.findClass(CLASS_RINGTONE_PLAYER, classLoader);

            Method mtdHandlePlay = null;
            try {
                mtdHandlePlay = clsRingtonePlayer.getDeclaredMethod("handlePlay", Uri.class);
                if (DEBUG) log("handlePlay found");
            } catch (NoSuchMethodException nme) {
                try {
                    mtdHandlePlay = clsRingtonePlayer.getDeclaredMethod("access$000",
                            clsRingtonePlayer, Uri.class);
                    if (DEBUG) log("handlePlay found as access$000");
                } catch (NoSuchMethodException nme2) { }
            }

            if (mtdHandlePlay == null) {
                log("Cannot find handlePlay method in " + CLASS_RINGTONE_PLAYER + ". Increasing ringtone disabled");
                return;
            }

            mRingerConfig = new ConfigStore(prefs.getString(
                    GravityBoxSettings.PREF_KEY_INCREASING_RING, null));
            if (DEBUG) log(mRingerConfig.toString());

            XposedBridge.hookAllConstructors(clsRingtonePlayer, new XC_MethodHook() {
                @Override
                protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                    mAsyncRinger = param.thisObject;
                    Context context = (Context) XposedHelpers.getObjectField(param.thisObject, "mContext");
                    IntentFilter intentFilter = new IntentFilter();
                    intentFilter.addAction(IncreasingRingPreference.ACTION_INCREASING_RING_CHANGED);
                    context.registerReceiver(mBroadcastReceiver, intentFilter);
                    if (DEBUG) log("Ringtone player created; broadcast receiver registered");
                }
            });

            XposedBridge.hookMethod(mtdHandlePlay, new XC_MethodHook() {
                @Override
                protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                    qhPrefs.reload();
                    QuietHours qh = new QuietHours(qhPrefs);
                    if (qh.isSystemSoundMuted(QuietHours.SystemSound.RINGER)) {
                        param.setResult(null);
                    }
                }
                @Override
                protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                    if (!mRingerConfig.enabled) return;

                    mRingtone = (Ringtone) XposedHelpers.getObjectField(mAsyncRinger, "mRingtone");
                    if (mRingtone == null) {
                        if (DEBUG) log("handlePlay called but ringtone is null");
                        return;
                    }

                    setVolume(mRingerConfig.minVolume);
                    mIncrementAmount = (1f - mRingerConfig.minVolume) / (float) mRingerConfig.rampUpDuration;
                    mCurrentIncrementVolume = mRingerConfig.minVolume;
                    mHandler = (Handler) XposedHelpers.getObjectField(mAsyncRinger, "mHandler");
                    mHandler.postDelayed(mRunnable, 1000);
                    if (DEBUG) log("Starting increasing ring");
                }
            });

            XposedHelpers.findAndHookMethod(clsRingtonePlayer, "handleStop", new XC_MethodHook() {
                @Override
                protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                    if (mHandler != null) {
                        if (DEBUG) log("Removing increasing ring callback");
                        mHandler.removeCallbacks(mRunnable);
                    }
                }
            });

        } catch (Throwable t) {
            XposedBridge.log(t);
        }
    }

    private static void setVolume(float volume) {
        Object player = XposedHelpers.getObjectField(mRingtone, "mLocalPlayer");
        if (player != null) {
            XposedHelpers.callMethod(player, "setVolume", volume);
        } else if (XposedHelpers.getBooleanField(mRingtone, "mAllowRemote")) {
            player = XposedHelpers.getObjectField(mRingtone, "mRemotePlayer");
            if (player != null) {
                try {
                    XposedHelpers.callMethod(player, "setVolume",
                            XposedHelpers.getObjectField(mRingtone, "mRemoteToken"),
                            volume);
                } catch (Throwable t) {
                    
                }
            }
        }
    }
}