// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef CHROME_BROWSER_EXTENSIONS_API_MDNS_DNS_SD_REGISTRY_H_
#define CHROME_BROWSER_EXTENSIONS_API_MDNS_DNS_SD_REGISTRY_H_

#include <map>
#include <memory>
#include <string>
#include <utility>
#include <vector>

#include "base/macros.h"
#include "base/memory/linked_ptr.h"
#include "base/observer_list.h"
#include "chrome/browser/extensions/api/mdns/dns_sd_delegate.h"

namespace local_discovery {
class ServiceDiscoverySharedClient;
class ServiceDiscoveryClient;
}

namespace extensions {

class DnsSdDeviceLister;
class ServiceTypeData;

// Registry class for keeping track of discovered network services over DNS-SD.
class DnsSdRegistry : public DnsSdDelegate {
 public:
  typedef std::vector<DnsSdService> DnsSdServiceList;

  class DnsSdObserver {
   public:
    virtual void OnDnsSdEvent(const std::string& service_type,
                              const DnsSdServiceList& services) = 0;

   protected:
    virtual ~DnsSdObserver() {}
  };

  DnsSdRegistry();
  explicit DnsSdRegistry(local_discovery::ServiceDiscoverySharedClient* client);
  virtual ~DnsSdRegistry();

  // Publishes the current device list for |service_type| to event listeners
  // whose event filter matches the service type.
  virtual void Publish(const std::string& service_type);

  // Immediately issues a multicast DNS query for all service types of the
  // calling extension.
  virtual void ForceDiscovery();

  // Observer registration for parties interested in discovery events.
  virtual void AddObserver(DnsSdObserver* observer);
  virtual void RemoveObserver(DnsSdObserver* observer);

  // DNS-SD-related discovery functionality.
  virtual void RegisterDnsSdListener(const std::string& service_type);
  virtual void UnregisterDnsSdListener(const std::string& service_type);

 protected:
  // Data class for managing all the resources and information related to a
  // particular service type.
  class ServiceTypeData {
   public:
    explicit ServiceTypeData(std::unique_ptr<DnsSdDeviceLister> lister);
    virtual ~ServiceTypeData();

    // Notify the data class of listeners so that it can be reference counted.
    void ListenerAdded();
    // Returns true if the last listener was removed.
    bool ListenerRemoved();
    int GetListenerCount();

    // Immediately issues a multicast DNS query for the service type owned by
    // |this|.
    void ForceDiscovery();

    // Methods for adding, updating or removing services for this service type.
    bool UpdateService(bool added, const DnsSdService& service);
    bool RemoveService(const std::string& service_name);
    // Called when the discovery service was restarted.
    // Clear the local cache and initiate rediscovery.
    bool ClearServices();

    const DnsSdRegistry::DnsSdServiceList& GetServiceList();

   private:
    int ref_count;
    std::unique_ptr<DnsSdDeviceLister> lister_;
    DnsSdRegistry::DnsSdServiceList service_list_;
    DISALLOW_COPY_AND_ASSIGN(ServiceTypeData);
  };

  // Maps service types to associated data such as listers and service lists.
  typedef std::map<std::string, linked_ptr<ServiceTypeData> >
      DnsSdServiceTypeDataMap;

  virtual DnsSdDeviceLister* CreateDnsSdDeviceLister(
      DnsSdDelegate* delegate,
      const std::string& service_type,
      local_discovery::ServiceDiscoverySharedClient* discovery_client);

  // DnsSdDelegate implementation:
  void ServiceChanged(const std::string& service_type,
                      bool added,
                      const DnsSdService& service) override;
  void ServiceRemoved(const std::string& service_type,
                      const std::string& service_name) override;
  void ServicesFlushed(const std::string& service_type) override;

  DnsSdServiceTypeDataMap service_data_map_;

 private:
  void DispatchApiEvent(const std::string& service_type);
  bool IsRegistered(const std::string& service_type);

  scoped_refptr<local_discovery::ServiceDiscoverySharedClient>
      service_discovery_client_;
  base::ObserverList<DnsSdObserver> observers_;

  DISALLOW_COPY_AND_ASSIGN(DnsSdRegistry);
};

}  // namespace extensions

#endif  // CHROME_BROWSER_EXTENSIONS_API_MDNS_DNS_SD_REGISTRY_H_
