/*
 * Copyright 2019 Fitbit, Inc. All rights reserved.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/.
 */

package com.fitbit.bluetooth.fbgatt.tx;

import com.fitbit.bluetooth.fbgatt.FitbitGatt;
import com.fitbit.bluetooth.fbgatt.GattServerConnection;
import com.fitbit.bluetooth.fbgatt.GattServerTransaction;
import com.fitbit.bluetooth.fbgatt.GattState;
import com.fitbit.bluetooth.fbgatt.GattTransactionCallback;
import com.fitbit.bluetooth.fbgatt.TransactionResult;
import com.fitbit.bluetooth.fbgatt.util.GattUtils;

import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothGattServer;

import timber.log.Timber;

/**
 * Will close out the existing gatt server reference.  Typically this shouldn't be necessary
 * but on some phones where BT off does not clear out services, this may help.  When BT is
 * turned back on, openGattServer will be called and the callbacks will be relinked.
 *
 * Only use this transaction if you know that you need to do so, and if you do, only use it
 * once BT is off, and probably after clearing services.  Be aware that removing services may
 * trigger a services changed notification on the remote peripheral.
 *
 * Created by iowens on 6/12/19.
 */

public class CloseGattServerTransaction extends GattServerTransaction {
    private static final String NAME = "CloseGattServerTransaction";
    public CloseGattServerTransaction(GattServerConnection server, GattState successEndState) {
        super(server, successEndState);
    }

    public CloseGattServerTransaction(GattServerConnection server, GattState successEndState, long timeoutMillis) {
        super(server, successEndState, timeoutMillis);
    }

    @Override
    protected void transaction(GattTransactionCallback callback) {
        super.transaction(callback);
        getGattServer().setState(GattState.CLOSING_GATT_SERVER);
        TransactionResult.Builder builder = new TransactionResult.Builder().transactionName(getName());
        BluetoothGattServer server = getGattServer().getServer();
        if(server == null) {
            Timber.w("The gatt server was null");
            getGattServer().setState(GattState.CLOSE_GATT_SERVER_FAILURE);
            builder.gattState(getGattServer().getGattState())
                .resultStatus(TransactionResult.TransactionResultStatus.FAILURE);
            mainThreadHandler.post(() -> {
                callCallbackWithTransactionResultAndRelease(callback, builder.build());
                getGattServer().setState(GattState.IDLE);
            });
        } else {
            BluetoothAdapter adapter = new GattUtils().getBluetoothAdapter(FitbitGatt.getInstance().getAppContext());
            if(adapter != null && adapter.isEnabled()) {
                Timber.e("I hope you know what you are doing, you will not be able to operate on the gatt server again until BT is toggled.");
            }
            try {
                server.close();
                getGattServer().setState(GattState.CLOSE_GATT_SERVER_SUCCESS);
                builder.gattState(getGattServer().getGattState())
                    .resultStatus(TransactionResult.TransactionResultStatus.SUCCESS);
                mainThreadHandler.post(() -> {
                    callCallbackWithTransactionResultAndRelease(callback, builder.build());
                    getGattServer().setState(GattState.IDLE);
                });
            } catch (NullPointerException ex) {
                Timber.w("As the client close can sometimes NPE internally, it is reasonable to assume that a similar thing occurred for the server");
                getGattServer().setState(GattState.CLOSE_GATT_SERVER_FAILURE);
                builder.gattState(getGattServer().getGattState())
                    .resultStatus(TransactionResult.TransactionResultStatus.FAILURE);
                mainThreadHandler.post(() -> {
                    callCallbackWithTransactionResultAndRelease(callback, builder.build());
                    getGattServer().setState(GattState.IDLE);
                });
            }
        }
    }

    @Override
    public String getName() {
        return NAME;
    }
}