// Copyright (c) 2011 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.

#include "chrome/browser/policy/configuration_policy_reader.h"

#include <map>
#include <vector>

#include "base/stl_util.h"
#include "base/utf_string_conversions.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/policy/browser_policy_connector.h"
#include "chrome/browser/policy/configuration_policy_pref_store.h"
#include "chrome/browser/policy/configuration_policy_store_interface.h"

namespace policy {

// This class functions as a container for policy status information used by the
// ConfigurationPolicyReader class. It obtains policy values from a
// ConfigurationPolicyProvider and maps them to their status information.
class ConfigurationPolicyStatusKeeper
    : public ConfigurationPolicyStoreInterface {
 public:
  ConfigurationPolicyStatusKeeper(ConfigurationPolicyProvider* provider,
                                  PolicyStatusInfo::PolicyLevel policy_level);
  virtual ~ConfigurationPolicyStatusKeeper();

  // ConfigurationPolicyStoreInterface methods.
  virtual void Apply(ConfigurationPolicyType policy, base::Value* value);

  // Returns a pointer to a DictionaryValue containing the status information
  // of the policy |policy|. The caller acquires ownership of the returned
  // value. Returns NULL if no such policy is stored in this keeper.
  DictionaryValue* GetPolicyStatus(ConfigurationPolicyType policy) const;

 private:
  typedef std::map<ConfigurationPolicyType, PolicyStatusInfo*> PolicyStatusMap;
  typedef std::map<ConfigurationPolicyType, string16> PolicyNameMap;
  typedef ConfigurationPolicyProvider::PolicyDefinitionList
      PolicyDefinitionList;

  // Mapping from ConfigurationPolicyType to PolicyStatusInfo.
  PolicyStatusMap policy_map_;

  // The level of the policies stored in this keeper (mandatory or
  // recommended).
  PolicyStatusInfo::PolicyLevel policy_level_;

  DISALLOW_COPY_AND_ASSIGN(ConfigurationPolicyStatusKeeper);
};

// ConfigurationPolicyStatusKeeper
ConfigurationPolicyStatusKeeper::ConfigurationPolicyStatusKeeper(
    ConfigurationPolicyProvider* provider,
    PolicyStatusInfo::PolicyLevel policy_level) : policy_level_(policy_level) {

  if (!provider->Provide(this))
    LOG(WARNING) << "Failed to get policy from provider.";
}

ConfigurationPolicyStatusKeeper::~ConfigurationPolicyStatusKeeper() {
  STLDeleteContainerPairSecondPointers(policy_map_.begin(), policy_map_.end());
  policy_map_.clear();
}

void ConfigurationPolicyStatusKeeper::Apply(
    ConfigurationPolicyType policy, base::Value* value) {
  string16 name = PolicyStatus::GetPolicyName(policy);

  if (name == string16()) {
    NOTREACHED();
  }

  // TODO(simo) actually determine whether the policy is a user or a device one
  // and whether the policy could be enforced or not once this information
  // is available.
  PolicyStatusInfo* info = new PolicyStatusInfo(name,
                                                PolicyStatusInfo::USER,
                                                policy_level_,
                                                value,
                                                PolicyStatusInfo::ENFORCED,
                                                string16());
  policy_map_[policy] = info;
}

DictionaryValue* ConfigurationPolicyStatusKeeper::GetPolicyStatus(
    ConfigurationPolicyType policy) const {
  PolicyStatusMap::const_iterator entry = policy_map_.find(policy);
  return entry != policy_map_.end() ?
      (entry->second)->GetDictionaryValue() : NULL;
}

// ConfigurationPolicyReader
ConfigurationPolicyReader::~ConfigurationPolicyReader() {
}

void ConfigurationPolicyReader::OnUpdatePolicy() {
  Refresh();
}

void ConfigurationPolicyReader::OnProviderGoingAway() {
  provider_ = NULL;
}

// static
ConfigurationPolicyReader*
    ConfigurationPolicyReader::CreateManagedPlatformPolicyReader() {
  BrowserPolicyConnector* connector =
      g_browser_process->browser_policy_connector();
  return new ConfigurationPolicyReader(
    connector->GetManagedPlatformProvider(), PolicyStatusInfo::MANDATORY);
}

// static
ConfigurationPolicyReader*
    ConfigurationPolicyReader::CreateManagedCloudPolicyReader() {
  BrowserPolicyConnector* connector =
      g_browser_process->browser_policy_connector();
  return new ConfigurationPolicyReader(
      connector->GetManagedCloudProvider(), PolicyStatusInfo::MANDATORY);
}

// static
ConfigurationPolicyReader*
    ConfigurationPolicyReader::CreateRecommendedPlatformPolicyReader() {
  BrowserPolicyConnector* connector =
      g_browser_process->browser_policy_connector();
  return new ConfigurationPolicyReader(
      connector->GetRecommendedPlatformProvider(),
          PolicyStatusInfo::RECOMMENDED);
}

// static
ConfigurationPolicyReader*
    ConfigurationPolicyReader::CreateRecommendedCloudPolicyReader() {
  BrowserPolicyConnector* connector =
      g_browser_process->browser_policy_connector();
  return new ConfigurationPolicyReader(
      connector->GetRecommendedCloudProvider(), PolicyStatusInfo::RECOMMENDED);
}

DictionaryValue* ConfigurationPolicyReader::GetPolicyStatus(
    ConfigurationPolicyType policy) const {
  return policy_keeper_->GetPolicyStatus(policy);
}

ConfigurationPolicyReader::ConfigurationPolicyReader(
    ConfigurationPolicyProvider* provider,
    PolicyStatusInfo::PolicyLevel policy_level)
    : provider_(provider),
      policy_level_(policy_level) {
  if (provider_) {
    // Read initial policy.
    policy_keeper_.reset(
        new ConfigurationPolicyStatusKeeper(provider, policy_level));
    registrar_.Init(provider_, this);
  }
}

ConfigurationPolicyReader::ConfigurationPolicyReader()
    : provider_(NULL),
      policy_level_(PolicyStatusInfo::LEVEL_UNDEFINED),
      policy_keeper_(NULL) {
}

void ConfigurationPolicyReader::Refresh() {
  if (!provider_)
    return;

  // Read policy state into a new keeper and swap out old keeper.
  scoped_ptr<ConfigurationPolicyStatusKeeper> new_keeper(
      new ConfigurationPolicyStatusKeeper(provider_, policy_level_));
  policy_keeper_.reset(new_keeper.release());
}

// PolicyStatus
PolicyStatus::PolicyStatus(ConfigurationPolicyReader* managed_platform,
                           ConfigurationPolicyReader* managed_cloud,
                           ConfigurationPolicyReader* recommended_platform,
                           ConfigurationPolicyReader* recommended_cloud)
    : managed_platform_(managed_platform),
      managed_cloud_(managed_cloud),
      recommended_platform_(recommended_platform),
      recommended_cloud_(recommended_cloud) {
}

PolicyStatus::~PolicyStatus() {
}

ListValue* PolicyStatus::GetPolicyStatusList(bool* any_policies_sent) const {
  ListValue* result = new ListValue();
  std::vector<DictionaryValue*> unsent_policies;

  *any_policies_sent = false;
  const PolicyDefinitionList* supported_policies =
      ConfigurationPolicyPrefStore::GetChromePolicyDefinitionList();
  const PolicyDefinitionList::Entry* policy = supported_policies->begin;
  for ( ; policy != supported_policies->end; ++policy) {
    if (!AddPolicyFromReaders(policy->policy_type, result)) {
      PolicyStatusInfo info(ASCIIToUTF16(policy->name),
                            PolicyStatusInfo::SOURCE_TYPE_UNDEFINED,
                            PolicyStatusInfo::LEVEL_UNDEFINED,
                            Value::CreateNullValue(),
                            PolicyStatusInfo::STATUS_UNDEFINED,
                            string16());
      unsent_policies.push_back(info.GetDictionaryValue());
    } else {
      *any_policies_sent = true;
    }
  }

  // Add policies that weren't actually sent from providers to list.
  std::vector<DictionaryValue*>::const_iterator info = unsent_policies.begin();
  for ( ; info != unsent_policies.end(); ++info)
    result->Append(*info);

  return result;
}

// static
string16 PolicyStatus::GetPolicyName(ConfigurationPolicyType policy_type) {
  static std::map<ConfigurationPolicyType, string16> name_map;
  static const ConfigurationPolicyProvider::PolicyDefinitionList*
      supported_policies;

  if (!supported_policies) {
    supported_policies =
        ConfigurationPolicyPrefStore::GetChromePolicyDefinitionList();

    // Create mapping from ConfigurationPolicyTypes to actual policy names.
    const ConfigurationPolicyProvider::PolicyDefinitionList::Entry* entry =
        supported_policies->begin;
    for ( ; entry != supported_policies->end; ++entry)
      name_map[entry->policy_type] = ASCIIToUTF16(entry->name);
  }

  std::map<ConfigurationPolicyType, string16>::const_iterator entry =
      name_map.find(policy_type);

  if (entry == name_map.end())
    return string16();

  return entry->second;
}

bool PolicyStatus::AddPolicyFromReaders(
    ConfigurationPolicyType policy, ListValue* list) const {
  DictionaryValue* mp_policy =
      managed_platform_->GetPolicyStatus(policy);
  DictionaryValue* mc_policy =
      managed_cloud_->GetPolicyStatus(policy);
  DictionaryValue* rp_policy =
      recommended_platform_->GetPolicyStatus(policy);
  DictionaryValue* rc_policy =
      recommended_cloud_->GetPolicyStatus(policy);

  bool added_policy = mp_policy || mc_policy || rp_policy || rc_policy;

  if (mp_policy)
    list->Append(mp_policy);
  if (mc_policy)
    list->Append(mc_policy);
  if (rp_policy)
    list->Append(rp_policy);
  if (rc_policy)
    list->Append(rc_policy);

  return added_policy;
}

} // namespace policy