/**
 *  Copyright (c) 2014-present, Facebook, Inc.
 *  All rights reserved.
 *
 *  This source code is licensed under both the Apache 2.0 license (found in the
 *  LICENSE file in the root directory of this source tree) and the GPLv2 (found
 *  in the COPYING file in the root directory of this source tree).
 *  You may select, at your option, one of the above-listed licenses.
 */

#include <CoreWLAN/CoreWLAN.h>
#include <Foundation/Foundation.h>
#include <objc/runtime.h>

#include <osquery/system.h>
#include <osquery/logger.h>
#include <osquery/tables.h>

#include "osquery/tables/networking/darwin/wifi_utils.h"

#pragma mark - osquery NSBundle

@implementation NSBundle(osquery)

- (NSString *)__bundleIdentifier
{
    if (self == [NSBundle mainBundle]) {
        return @"com.apple.osqueryd";
    } else {
        return [self __bundleIdentifier];
    }
}

@end

BOOL installNSBundleHook()
{
    Class c = objc_getClass("NSBundle");
    if (c) {
        method_exchangeImplementations(class_getInstanceMethod(c, @selector(bundleIdentifier)),
                                       class_getInstanceMethod(c, @selector(__bundleIdentifier)));
        return YES;
    }
	return NO;
}


namespace osquery {
namespace tables {

QueryData genWifiScan(QueryContext& context) {
  QueryData results;
  @autoreleasepool {
    NSArray<CWInterface*>* interfaces =
        [[CWWiFiClient sharedWiFiClient] interfaces];
    if (interfaces == nil || [interfaces count] == 0) {
      LOG(WARNING) << "Did not return any interfaces, might lack permissions";
      return results;
    }
    for (CWInterface* interface in interfaces) {
      NSError *error = nil;
      installNSBundleHook();
      NSSet<CWNetwork*>* networks =
          [interface scanForNetworksWithName:nil error:&error];
      if (error != nil) {
        LOG(WARNING) << std::string([[error localizedDescription] UTF8String]);
        return results;
      }
      for (CWNetwork* network in networks) {
        Row r;
        r["interface"] = [[interface interfaceName] UTF8String];
        r["ssid"] = extractSsid((__bridge CFDataRef)[network ssidData]);
        auto bssid = [network bssid];
        if (bssid != nullptr) {
          r["bssid"] = [bssid UTF8String];
        }
        r["network_name"] = [[network ssid] UTF8String];
        NSString* country_code = [network countryCode];
        if (country_code != nil) {
          r["country_code"] = [country_code UTF8String];
        }
        r["rssi"] = INTEGER([network rssiValue]);
        r["noise"] = INTEGER([network noiseMeasurement]);
        CWChannel* cwc = [network wlanChannel];
        if (cwc != nil) {
          r["channel"] = INTEGER(getChannelNumber(cwc));
          r["channel_width"] = INTEGER(getChannelWidth(cwc));
          r["channel_band"] = INTEGER(getChannelBand(cwc));
        }
        results.push_back(r);
      }
    }
  }
  return results;
}
}
}
