/*
 * Copyright 2015 Google Inc. All rights reserved.
 *
 * 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 org.physical_web.physicalweb;

import org.physical_web.collection.EddystoneBeacon;
import org.physical_web.collection.UrlDevice;
import org.physical_web.physicalweb.ble.ScanRecord;

import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothManager;
import android.content.Context;
import android.os.ParcelUuid;
import android.os.Parcelable;
import android.webkit.URLUtil;

import java.util.List;

class BleUrlDeviceDiscoverer extends UrlDeviceDiscoverer
                             implements BluetoothAdapter.LeScanCallback {
  private static final String TAG = BleUrlDeviceDiscoverer.class.getSimpleName();
  private static final ParcelUuid URIBEACON_SERVICE_UUID =
      ParcelUuid.fromString("0000FED8-0000-1000-8000-00805F9B34FB");
  private static final ParcelUuid EDDYSTONE_URL_SERVICE_UUID =
      ParcelUuid.fromString("0000FEAA-0000-1000-8000-00805F9B34FB");
  private BluetoothAdapter mBluetoothAdapter;
  private Parcelable[] mScanFilterUuids;
  private Context mContext;

  public BleUrlDeviceDiscoverer(Context context) {
    mContext = context;
    final BluetoothManager bluetoothManager = (BluetoothManager) mContext.getSystemService(
        Context.BLUETOOTH_SERVICE);
    mBluetoothAdapter = bluetoothManager.getAdapter();
    mScanFilterUuids = new ParcelUuid[]{URIBEACON_SERVICE_UUID, EDDYSTONE_URL_SERVICE_UUID};
  }

  private boolean leScanMatches(ScanRecord scanRecord) {
    if (mScanFilterUuids == null) {
      return true;
    }
    List services = scanRecord.getServiceUuids();
    if (services != null) {
      for (Parcelable uuid : mScanFilterUuids) {
        if (services.contains(uuid)) {
          return true;
        }
      }
    }
    return false;
  }

  @Override
  public void onLeScan(final BluetoothDevice device, final int rssi, final byte[] scanBytes) {
    ScanRecord scanRecord = ScanRecord.parseFromBytes(scanBytes);
    if (!leScanMatches(scanRecord)) {
      return;
    }

    byte[] urlServiceData = scanRecord.getServiceData(EDDYSTONE_URL_SERVICE_UUID);
    byte[] uriServiceData = scanRecord.getServiceData(URIBEACON_SERVICE_UUID);
    if (Utils.isFatBeaconEnabled(mContext) && EddystoneBeacon.isFatBeacon(urlServiceData)) {
      String title = EddystoneBeacon.getFatBeaconTitle(urlServiceData);
      if (title.isEmpty()) {
        return;
      }
      UrlDevice urlDevice = createUrlDeviceBuilder(TAG + device.getAddress(), device.getAddress())
          .setTitle(title)
          .setDescription(mContext.getString(R.string.fatbeacon_description))
          .setDeviceType(Utils.FAT_BEACON_DEVICE_TYPE)
          .build();
      reportUrlDevice(urlDevice);
      return;
    }

    EddystoneBeacon beacon = EddystoneBeacon.parseFromServiceData(urlServiceData, uriServiceData);
    if (beacon == null || !URLUtil.isNetworkUrl(beacon.getUrl())) {
      return;
    }
    UrlDevice urlDevice = createUrlDeviceBuilder(TAG + device.getAddress() + beacon.getUrl(),
                                                 beacon.getUrl())
        .setRssi(rssi)
        .setTxPower(beacon.getTxPowerLevel())
        .setDeviceType(Utils.BLE_DEVICE_TYPE)
        .build();
    Utils.updateRegion(urlDevice);
    reportUrlDevice(urlDevice);
  }

  @Override
  @SuppressWarnings("deprecation")
  public synchronized void startScanImpl() {
    mBluetoothAdapter.startLeScan(this);
  }

  @Override
  @SuppressWarnings("deprecation")
  public synchronized void stopScanImpl() {
    mBluetoothAdapter.stopLeScan(this);
  }
}