/*
 * Copyright 2016 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 android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.wifi.p2p.WifiP2pDevice;
import android.net.wifi.p2p.WifiP2pDeviceList;
import android.net.wifi.p2p.WifiP2pManager;
import android.net.wifi.p2p.WifiP2pManager.Channel;
import android.net.wifi.p2p.WifiP2pManager.PeerListListener;

/**
 * This class is for using WifiDirect to discover Physical Web
 * Devices through peer discovery.
 */
class WifiUrlDeviceDiscoverer extends UrlDeviceDiscoverer {
  private static final String TAG = WifiUrlDeviceDiscoverer.class.getSimpleName();
  private Context mContext;
  private WifiP2pManager mManager;
  private Channel mChannel;
  private IntentFilter mIntentFilter = new IntentFilter();
  private boolean mIsRunning = false;
  private BroadcastReceiver mReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
      String action = intent.getAction();
      if (WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION.equals(action)
          && mManager != null) {
        mManager.requestPeers(mChannel, mPeerListener);
      }
    }
  };
  private final PeerListListener mPeerListener = new PeerListListener() {
    @Override
    public void onPeersAvailable(WifiP2pDeviceList list) {
      Log.d(TAG, list.toString());
      for (WifiP2pDevice device : list.getDeviceList()) {
        Utils.WifiDirectInfo info = Utils.parseWifiDirectName(device.deviceName);
        if (info != null) {
          String name = info.title;
          int port = info.port;
          reportUrlDevice(createUrlDeviceBuilder("WifiDirect" + name,
              device.deviceAddress + ":" + port)
            .setWifiAddress(device.deviceAddress)
            .setWifiPort(port)
            .setTitle(name)
            .setDescription("")
            .setDeviceType(Utils.WIFI_DIRECT_DEVICE_TYPE)
            .build());
        }
      }
    }
  };

  public WifiUrlDeviceDiscoverer(Context context) {
    mContext = context;
  }

  @Override
  public synchronized void startScanImpl() {
    mManager = (WifiP2pManager) mContext.getSystemService(Context.WIFI_P2P_SERVICE);
    mChannel = mManager.initialize(mContext, mContext.getMainLooper(), null);
    IntentFilter intentFilter = new IntentFilter();
    intentFilter.addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION);
    mContext.registerReceiver(mReceiver, intentFilter);
    mIsRunning = true;
    discoverHelper(true);
  }

  private void discoverHelper(boolean retry) {
    mManager.discoverPeers(mChannel, new WifiP2pManager.ActionListener() {
      @Override
      public void onSuccess() {
      }

      @Override
      public void onFailure(int reasonCode) {
        if (retry && reasonCode == WifiP2pManager.BUSY) {
          mManager.cancelConnect(mChannel, new WifiP2pManager.ActionListener() {
            @Override
            public void onSuccess() {
              Log.d(TAG, "cancel connect call success");
              if (mIsRunning) {
                discoverHelper(false);
              }
            }
            @Override
            public void onFailure(int reason) {
              Log.d(TAG, "cancel connect call fail " + reason);
            }
          });
        }
      }
    });
  }

  @Override
  public synchronized void stopScanImpl() {
    mIsRunning = false;
    mContext.unregisterReceiver(mReceiver);
    mManager.stopPeerDiscovery(mChannel, new WifiP2pManager.ActionListener() {
      @Override
      public void onSuccess() {
        Log.d(TAG, "stop discovering");
      }

      @Override
      public void onFailure(int reasonCode) {
        Log.d(TAG, "stop discovery failed " + reasonCode);
      }
    });
  }
}