package cn.tee3.avd;

import android.bluetooth.BluetoothHeadset;
import android.bluetooth.BluetoothProfile;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.hardware.usb.UsbAccessory;
import android.hardware.usb.UsbManager;
import android.media.AudioDeviceInfo;
import android.media.AudioManager;
import android.os.Build;
import android.support.annotation.IntDef;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

import javax.annotation.Nullable;

/**
 * Created by Bob.Liu on 2016/7/16.
 */
public class AudioRoute {
    private static final String TAG = "AudioRoute";

    public static AudioRoute instance() {
        return SingletonHolder.INSTANCE;
    }

    private static class SingletonHolder {
        static final AudioRoute INSTANCE = new AudioRoute();
    }

    private AudioRoute() {
        oldmode = audioManager.getMode();
    }

    public void enable(boolean enable) {
        isenable = enable;
    }

    boolean isenable = true;

    public void init() {
        if (!isenable) {
            return;
        }
        Tlog.i(TAG, "init, change to MODE_IN_COMMUNICATION, handfree, autoroute, oldmode:" + oldmode);
        // TODO: 2020/3/9 放开时背景铃声有一个短暂停顿
//        audioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
//        setHandFree(true);
        setAutoRoute(true);
    }

    public void uninit() {
        if (!isenable) {
            return;
        }
        Tlog.i(TAG, "uninit, change to oldmode:" + oldmode);
        audioManager.setMode(this.oldmode);
        if (this.autoRoute && null != this.headsetPlug) {
            try {
                AVDEngine.instance().getContext().unregisterReceiver(this.headsetPlug);
                this.headsetPlug = null;
                this.autoRoute = false;
                this.isHandFree = false;
                this.isBlueTooth = false;
            } catch (Exception e) {
                Tlog.e(TAG, "headsetPlug.unregisterReceiver,Exception e=" + e.toString());
            }
        }
    }

    AudioManager audioManager = (AudioManager)
            AVDEngine.instance().getContext().getSystemService(Context.AUDIO_SERVICE);
    // audio in communication mode
    private int oldmode = AudioManager.MODE_NORMAL;
    private boolean autoRoute = false;
    private boolean isHandFree = false;
    private boolean isBlueTooth = false;
    @Nullable
    HeadsetPlugReceiver headsetPlug = null;

    public boolean isAutoRoute() {
        return this.autoRoute;
    }

    public void setAutoRoute(boolean isOn) {
        if (!isenable) {
            return;
        }
        if (this.autoRoute == isOn) {
            return;
        }
        Tlog.i(TAG, "setAutoRoute, isOn=" + isOn);
        if (isOn) {
            if (null == this.headsetPlug) {
                this.headsetPlug = new HeadsetPlugReceiver();
            }
            AVDEngine.instance().getContext().registerReceiver(
                    this.headsetPlug, new IntentFilter(AudioManager.ACTION_HEADSET_PLUG));
            AVDEngine.instance().getContext().registerReceiver(
                    this.headsetPlug, new IntentFilter(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED));
            //AVDEngine.instance().getContext().registerReceiver(
            //       this.headsetPlug, new IntentFilter(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED));
            //AVDEngine.instance().getContext().registerReceiver(
            //        audiodeviereceiver, new IntentFilter(AudioManager.RINGER_MODE_CHANGED_ACTION));
            //AVDEngine.instance().getContext().registerReceiver(
            //        audiodeviereceiver, new IntentFilter(AudioManager.EXTRA_AUDIO_PLUG_STATE)); // HDMI
        } else {
            if (null != this.headsetPlug) {
                try {
                    AVDEngine.instance().getContext().unregisterReceiver(this.headsetPlug);
                    this.headsetPlug = null;
                } catch (Exception e) {
                    Tlog.e(TAG, "headsetPlug.unregisterReceiver,Exception e=" + e.toString());
                }
            }
        }
        this.autoRoute = isOn;
        audioAutoRoute(true);
    }

    public boolean isHandFree() {
        return this.isHandFree;
    }

    public void setHandFree(boolean isOn) {
        if (!isenable) {
            return;
        }
        this.isHandFree = audioManager.isSpeakerphoneOn();
        if (this.isHandFree == isOn) {
            return;
        }
        if (isHeadsetOn()) {
            Tlog.w(TAG, "Set hand free failure, due to head set is on!");
            return;
        }
        Tlog.i(TAG, "setHandFree, isOn=" + isOn);
        this.isHandFree = isOn;
        setSpeakerOn(isOn);
    }

    private void setSpeakerOn(boolean isOn) {
        Tlog.i(TAG, "setSpeakerOn, isOn=" + isOn);
        audioManager.setSpeakerphoneOn(isOn);
    }

    public int getSpeakerVolume() {
        return audioManager.getStreamVolume(AudioManager.STREAM_VOICE_CALL);
    }

    public void setSpeakerVolume(int value) {
        audioManager.setStreamVolume(AudioManager.STREAM_VOICE_CALL, value, 0);
    }

    public boolean isHeadsetOn() {
        Context context = AVDEngine.instance().getContext();
        if (context == null) {
            return false;
        }
        AudioManager am = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);

        if (am == null)
            return false;

        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
            return am.isWiredHeadsetOn() || am.isBluetoothScoOn() || am.isBluetoothA2dpOn();
        } else {
            AudioDeviceInfo[] devices = am.getDevices(AudioManager.GET_DEVICES_OUTPUTS);
            for (AudioDeviceInfo deviceInfo : devices) {
                if (deviceInfo.getType() == AudioDeviceInfo.TYPE_WIRED_HEADSET || deviceInfo.getType() == AudioDeviceInfo.TYPE_WIRED_HEADPHONES ||
                        deviceInfo.getType() == AudioDeviceInfo.TYPE_BLUETOOTH_A2DP || deviceInfo.getType() == AudioDeviceInfo.TYPE_BLUETOOTH_SCO) {
                    return true;
                }
            }
        }
        return false;
    }

    private void audioAutoRoute(boolean checkbt) {
        if (!isAutoRoute()) {
            return;
        }
        if (MAudio.isOpenUsb) {
            setRouteBluetooth(false);
            setSpeakerOn(false);
            return;
        }
        if (checkbt && (audioManager.isBluetoothScoOn() || audioManager.isBluetoothA2dpOn())) { // bluetooth first
            Tlog.i(TAG, "audioAutoRoute,isBluetoothScoOn:" + audioManager.isBluetoothScoOn()
                    + " isBluetoothA2dpOn:" + audioManager.isBluetoothA2dpOn());
            setSpeakerOn(false);
            sco_try = 0;
            setRouteBluetooth(true);
        } else if (audioManager.isWiredHeadsetOn()) { // headset second
            Tlog.i(TAG, "audioAutoRoute,isWiredHeadsetOn nobt=" + checkbt);
            setRouteBluetooth(false);
            setSpeakerOn(false);
        } else { // speaker last
            Tlog.i(TAG, "audioAutoRoute,phone internal mic and spk nobt=" + checkbt);
            setRouteBluetooth(false);
            setSpeakerOn(isHandFree());
        }
    }

    // for phone call in

    // for sco bluetooth
    private void startBluetoothSco(final boolean isOn) {
        try {
            audioManager.setBluetoothScoOn(false);
            audioManager.stopBluetoothSco();
            if (!isOn) {
                return;
            }
            audioManager.startBluetoothSco();
        } catch (NullPointerException e) {
            Tlog.e(TAG, "startBluetoothSco, isOn=" + isOn + " NullPointerException", e);
            return;
        }
    }

    BroadcastReceiver receiver;

    private void setRouteBluetooth(final boolean isOn) {
        if (this.isBlueTooth == isOn) {
            return;
        }
        Tlog.d(TAG, "setRouteBluetooth, isOn=" + isOn);
        this.isBlueTooth = isOn;
        startBluetoothSco(isOn);
        if (!isOn) {
            return;
        }
        receiver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                int state = intent.getIntExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, -1);
                if (AudioManager.SCO_AUDIO_STATE_CONNECTED == state) {
                    Tlog.i(TAG, "Bluetooth receiver,SCO_AUDIO_STATE_CONNECTED, do use bt. isOn=" + isOn);
                    audioManager.setBluetoothScoOn(isOn);                       //打开SCO（蓝牙输出）
                    AVDEngine.instance().getContext().unregisterReceiver(this);  //别遗漏
                } else {
                    Tlog.i(TAG, "Bluetooth receiver,state=" + state + ",sco_try=" + sco_try);
                    if (sco_try > 5) {
                        Tlog.i(TAG, "Bluetooth receiver, autoroute, too much sco_try=" + sco_try);
                        audioAutoRoute(false);
                        AVDEngine.instance().getContext().unregisterReceiver(this);  //别遗漏
                    } else {
                        try {
                            Thread.sleep(3000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        audioManager.startBluetoothSco();
                        ++sco_try;
                        AVDEngine.instance().getContext().registerReceiver(
                                receiver, new IntentFilter(AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED));
                    }
                }
            }
        };
        AVDEngine.instance().getContext().registerReceiver(
                receiver, new IntentFilter(AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED));
    }

    int sco_try = 0;

    public class HeadsetPlugReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            if (intent.getAction().equals(Intent.ACTION_HEADSET_PLUG)) {
                int state = intent.getIntExtra("state", 4);
                Tlog.i(TAG, "HeadsetPlugReceiver, ACTION_HEADSET_PLUG: state=" + state);
                if (state == 1 || state == 0) {
                    audioAutoRoute(true);
                }
            }
            // Todo: cann't receive bluetooth headset plugin or out cast.
            else if (intent.getAction().equals(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED)) {
                int state = intent.getIntExtra(BluetoothHeadset.EXTRA_STATE, BluetoothHeadset.STATE_DISCONNECTED);
                Tlog.i(TAG, "HeadsetPlugReceiver, BluetoothHeadset ACTION_CONNECTION_STATE_CHANGED: state=" + state);
                if (state == BluetoothProfile.STATE_CONNECTED) {
                    setSpeakerOn(false);  // isBluetoothScoOn is failed now
                    sco_try = 0;
                    setRouteBluetooth(true);
                } else if (state == BluetoothProfile.STATE_DISCONNECTED) {
                    audioAutoRoute(false);
                }
            } else if (intent.getAction().equals(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED)) {
                //int state = intent.getIntExtra(BluetoothHeadset.EXTRA_STATE, BluetoothHeadset.STATE_DISCONNECTED);
                Tlog.d(TAG, "ACTION_AUDIO_STATE_CHANGED, BluetoothHeadset intent=" + intent.toString());
            }
        }
    }

    private static final String ACTION_USB_PERMISSION =
            "com.android.example.USB_PERMISSION";

    private class UsbAccessoryPlugReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            if (ACTION_USB_PERMISSION.equals(action)) {
                synchronized (this) {
                    UsbAccessory accessory = (UsbAccessory) intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);
                    if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
                        if (accessory == null) {
                            return;
                        }
                        //call method to set up accessory communication
                        Tlog.i(TAG, "UsbAccessoryPlugReceiver, accessory=" + accessory.toString());

                    } else {
                        Tlog.d(TAG, "UsbAccessoryPlugReceiver, permission denied for accessory " + accessory);
                    }
                }
            }
        }
    }

    /**
     * 设置音频模式
     *
     * @param audioMode 音频模式
     */
    public void setAudioMode(@AudioMode int audioMode) {
        if (audioManager != null) {
            audioManager.setMode(audioMode);
        }
    }

    @IntDef({AudioManager.MODE_CURRENT,
            AudioManager.MODE_INVALID,
            AudioManager.MODE_NORMAL,
            AudioManager.MODE_RINGTONE,
            AudioManager.MODE_IN_CALL,
            AudioManager.MODE_IN_COMMUNICATION})
    @Retention(RetentionPolicy.SOURCE)
    public @interface AudioMode {}

//    UsbManager mUsbManager = (UsbManager) AVDEngine.instance().getContext().getSystemService(Context.USB_SERVICE);
    // android sdk 23 upper:
//    private boolean isUsedUsbAccess = false;
//    public boolean isUsedUsbAccessory() {
//        return isUsedUsbAccess;
//    }
//    public boolean setUsedUsbAccessory(boolean isOn) {
//        if (isUsedUsbAccess == isOn) {
//            return true;
//        }
//        Tlog.i(TAG, "setUsedUsbAccessory, isOn:"+isOn);
//        if (!isOn) {
//            isUsedUsbAccess = false;
//            setPreferredDevice(null);
//            return true;
//        }
//        AudioDeviceInfo[] ods = audioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS);
//        AudioDeviceInfo usbaudioDevice = null;
//        for (AudioDeviceInfo item : ods) {
//            if (item.getType() == AudioDeviceInfo.TYPE_USB_ACCESSORY
//                    || item.getType() == AudioDeviceInfo.TYPE_USB_DEVICE) {
//                usbaudioDevice = item;
//                break;
//            }
//        }
//        if (null == usbaudioDevice) {
//            Tlog.w(TAG, "setUsedUsbAccessory, can't find usb audio device.");
//            return false;
//        }
//        else {
//            Tlog.i(TAG, "setUsedUsbAccessory, usb audio device:"+item.toString());
//        }
//        boolean ret = setPreferredDevice(item);
//        if (ret) {
//            isUsedUsbAccess = true;
//        }
//        return ret;
//    }
//    private boolean setPreferredDevice(AudioDeviceInfo item) {
//        AudioTrack lastTack = WebRtcAudioTrack.lastAudioTrack();
//        if (null == lastTack) {
//            return false;
//        }
//        return lastTack.setPreferredDevice(item);
//    }

}
