//
// Copyright (C) 2013 The Android Open Source Project
//
// 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.
//

#ifndef SHILL_WIFI_WIFI_PROVIDER_H_
#define SHILL_WIFI_WIFI_PROVIDER_H_

#include <time.h>

#include <deque>
#include <map>
#include <string>
#include <vector>

#include <gtest/gtest_prod.h>  // for FRIEND_TEST

#include "shill/accessor_interface.h"  // for ByteArrays
#include "shill/provider_interface.h"
#include "shill/refptr_types.h"

namespace shill {

class ByteString;
class ControlInterface;
class Error;
class EventDispatcher;
class KeyValueStore;
class Manager;
class Metrics;
class StoreInterface;
class Time;
class WiFiEndpoint;
class WiFiService;

// The WiFi Provider is the holder of all WiFi Services.  It holds both
// visible (created due to an Endpoint becoming visible) and invisible
// (created due to user or storage configuration) Services.
class WiFiProvider : public ProviderInterface {
 public:
  static const char kStorageFrequencies[];
  static const int kMaxStorageFrequencies;
  typedef std::map<uint16_t, int64_t> ConnectFrequencyMap;
  // The key to |ConnectFrequencyMapDated| is the number of days since the
  // Epoch.
  typedef std::map<time_t, ConnectFrequencyMap> ConnectFrequencyMapDated;
  struct FrequencyCount {
    FrequencyCount() : frequency(0), connection_count(0) {}
    FrequencyCount(uint16_t freq, size_t conn)
        : frequency(freq), connection_count(conn) {}
    uint16_t frequency;
    size_t connection_count;  // Number of successful connections at this
                              // frequency.
  };
  typedef std::deque<FrequencyCount> FrequencyCountList;

  WiFiProvider(ControlInterface* control_interface,
               EventDispatcher* dispatcher,
               Metrics* metrics,
               Manager* manager);
  ~WiFiProvider() override;

  // Called by Manager as a part of the Provider interface.  The attributes
  // used for matching services for the WiFi provider are the SSID, mode and
  // security parameters.
  void CreateServicesFromProfile(const ProfileRefPtr& profile) override;
  ServiceRefPtr FindSimilarService(
      const KeyValueStore& args, Error* error) const override;
  ServiceRefPtr GetService(const KeyValueStore& args, Error* error) override;
  ServiceRefPtr CreateTemporaryService(
      const KeyValueStore& args, Error* error) override;
  ServiceRefPtr CreateTemporaryServiceFromProfile(
      const ProfileRefPtr& profile,
      const std::string& entry_name,
      Error* error) override;
  void Start() override;
  void Stop() override;

  // Find a Service this Endpoint should be associated with.
  virtual WiFiServiceRefPtr FindServiceForEndpoint(
      const WiFiEndpointConstRefPtr& endpoint);

  // Find or create a Service for |endpoint| to be associated with.  This
  // method first calls FindServiceForEndpoint, and failing this, creates
  // a new Service.  It then associates |endpoint| with this service.
  virtual void OnEndpointAdded(const WiFiEndpointConstRefPtr& endpoint);

  // Called by a Device when it removes an Endpoint.  If the Provider
  // forgets a service as a result, it returns a reference to the
  // forgotten service, otherwise it returns a null reference.
  virtual WiFiServiceRefPtr OnEndpointRemoved(
      const WiFiEndpointConstRefPtr& endpoint);

  // Called by a Device when it receives notification that an Endpoint
  // has changed.  Ensure the updated endpoint still matches its
  // associated service.  If necessary re-assign the endpoint to a new
  // service, otherwise notify the associated service of the update to
  // the endpoint.
  virtual void OnEndpointUpdated(const WiFiEndpointConstRefPtr& endpoint);

  // Called by a WiFiService when it is unloaded and no longer visible.
  virtual bool OnServiceUnloaded(const WiFiServiceRefPtr& service);

  // Get the list of SSIDs for hidden WiFi services we are aware of.
  virtual ByteArrays GetHiddenSSIDList();

  // Calls WiFiService::FixupServiceEntries() and adds a UMA metric if
  // this causes entries to be updated.
  virtual void LoadAndFixupServiceEntries(Profile* profile);

  // Save configuration for wifi_provider to |storage|.
  virtual bool Save(StoreInterface* storage) const;

  virtual void IncrementConnectCount(uint16_t frequency_mhz);

  // Returns a list of all of the frequencies on which this device has
  // connected.  This data is accumulated across multiple shill runs.
  virtual FrequencyCountList GetScanFrequencies() const;

  // Report the number of auto connectable services available to uma
  // metrics.
  void ReportAutoConnectableServices();

  // Returns number of services available for auto-connect.
  virtual int NumAutoConnectableServices();

  // Returns a list of ByteStrings representing the SSIDs of WiFi services
  // configured for auto-connect.
  std::vector<ByteString> GetSsidsConfiguredForAutoConnect();

  bool disable_vht() { return disable_vht_; }
  void set_disable_vht(bool disable_vht) { disable_vht_ = disable_vht; }

 private:
  friend class WiFiProviderTest;
  FRIEND_TEST(WiFiProviderTest, FrequencyMapAgingIllegalDay);
  FRIEND_TEST(WiFiProviderTest, FrequencyMapBasicAging);
  FRIEND_TEST(WiFiProviderTest, FrequencyMapToStringList);
  FRIEND_TEST(WiFiProviderTest, FrequencyMapToStringListEmpty);
  FRIEND_TEST(WiFiProviderTest, IncrementConnectCount);
  FRIEND_TEST(WiFiProviderTest, IncrementConnectCountCreateNew);
  FRIEND_TEST(WiFiProviderTest, LoadAndFixupServiceEntriesDefaultProfile);
  FRIEND_TEST(WiFiProviderTest, LoadAndFixupServiceEntriesUserProfile);
  FRIEND_TEST(WiFiProviderTest, LoadAndFixupServiceEntriesNothingToDo);
  FRIEND_TEST(WiFiProviderTest, StringListToFrequencyMap);
  FRIEND_TEST(WiFiProviderTest, StringListToFrequencyMapEmpty);

  typedef std::map<const WiFiEndpoint*, WiFiServiceRefPtr> EndpointServiceMap;

  static const char kManagerErrorSSIDTooLong[];
  static const char kManagerErrorSSIDTooShort[];
  static const char kManagerErrorSSIDRequired[];
  static const char kManagerErrorUnsupportedSecurityClass[];
  static const char kManagerErrorUnsupportedSecurityMode[];
  static const char kManagerErrorUnsupportedServiceMode[];
  static const char kManagerErrorArgumentConflict[];
  static const char kFrequencyDelimiter;
  static const char kStartWeekHeader[];
  static const time_t kIllegalStartWeek;
  static const char kStorageId[];
  static const time_t kWeeksToKeepFrequencyCounts;
  static const time_t kSecondsPerWeek;

  // Add a service to the service_ vector and register it with the Manager.
  WiFiServiceRefPtr AddService(const std::vector<uint8_t>& ssid,
                               const std::string& mode,
                               const std::string& security,
                               bool is_hidden);

  // Find a service given its properties.
  WiFiServiceRefPtr FindService(const std::vector<uint8_t>& ssid,
                                const std::string& mode,
                                const std::string& security) const;

  // Returns a WiFiServiceRefPtr for unit tests and for down-casting to a
  // ServiceRefPtr in GetService().
  WiFiServiceRefPtr GetWiFiService(const KeyValueStore& args, Error* error);

  // Disassociate the service from its WiFi device and remove it from the
  // services_ vector.
  void ForgetService(const WiFiServiceRefPtr& service);

  void ReportRememberedNetworkCount();
  void ReportServiceSourceMetrics();

  // Retrieve a WiFi service's identifying properties from passed-in |args|.
  // Returns true if |args| are valid and populates |ssid|, |mode|,
  // |security| and |hidden_ssid|, if successful.  Otherwise, this function
  // returns false and populates |error| with the reason for failure.  It
  // is a fatal error if the "Type" parameter passed in |args| is not kWiFi.
  static bool GetServiceParametersFromArgs(const KeyValueStore& args,
                                           std::vector<uint8_t>* ssid_bytes,
                                           std::string* mode,
                                           std::string* security_method,
                                           bool* hidden_ssid,
                                           Error* error);
  // Retrieve a WiFi service's identifying properties from passed-in |storage|.
  // Return true if storage contain valid parameter values and populates |ssid|,
  // |mode|, |security| and |hidden_ssid|. Otherwise, this function returns
  // false and populates |error| with the reason for failure.
  static bool GetServiceParametersFromStorage(const StoreInterface* storage,
                                              const std::string& entry_name,
                                              std::vector<uint8_t>* ssid_bytes,
                                              std::string* mode,
                                              std::string* security_method,
                                              bool* hidden_ssid,
                                              Error* error);

  // Converts frequency profile information from a list of strings of the form
  // "frequency:connection_count" to a form consistent with
  // |connect_count_by_frequency_|.  The first string must be of the form
  // [nnn] where |nnn| is a positive integer that represents the creation time
  // (number of days since the Epoch) of the data.
  static time_t StringListToFrequencyMap(
      const std::vector<std::string>& strings,
      ConnectFrequencyMap* numbers);

  // Extracts the start week from the first string in the StringList for
  // |StringListToFrequencyMap|.
  static time_t GetStringListStartWeek(const std::string& week_string);

  // Extracts frequency and connection count from a string from the StringList
  // for |StringListToFrequencyMap|.  Places those values in |numbers|.
  static void ParseStringListFreqCount(const std::string& freq_count_string,
                                       ConnectFrequencyMap* numbers);

  // Converts frequency profile information from a form consistent with
  // |connect_count_by_frequency_| to a list of strings of the form
  // "frequency:connection_count".  The |creation_day| is the day that the
  // data was first createed (represented as the number of days since the
  // Epoch).
  static void FrequencyMapToStringList(time_t creation_day,
                                       const ConnectFrequencyMap& numbers,
                                       std::vector<std::string>* strings);

  ControlInterface* control_interface_;
  EventDispatcher* dispatcher_;
  Metrics* metrics_;
  Manager* manager_;

  std::vector<WiFiServiceRefPtr> services_;
  EndpointServiceMap service_by_endpoint_;

  bool running_;

  // Map of frequencies at which we've connected and the number of times a
  // successful connection has been made at that frequency.  Absent frequencies
  // have not had a successful connection.
  ConnectFrequencyMap connect_count_by_frequency_;
  // A number of entries of |ConnectFrequencyMap| stored by date of creation.
  ConnectFrequencyMapDated connect_count_by_frequency_dated_;

  // Count of successful wifi connections we've made.
  int64_t total_frequency_connections_;

  Time* time_;

  // Disable 802.11ac Very High Throughput (VHT) connections.
  bool disable_vht_;

  DISALLOW_COPY_AND_ASSIGN(WiFiProvider);
};

}  // namespace shill

#endif  // SHILL_WIFI_WIFI_PROVIDER_H_