// 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/plugin_exceptions_table_model.h"

#include "base/auto_reset.h"
#include "base/sys_string_conversions.h"
#include "base/utf_string_conversions.h"
#include "content/common/notification_service.h"
#include "grit/generated_resources.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/models/table_model_observer.h"

PluginExceptionsTableModel::PluginExceptionsTableModel(
    HostContentSettingsMap* content_settings_map,
    HostContentSettingsMap* otr_content_settings_map)
    : map_(content_settings_map),
      otr_map_(otr_content_settings_map),
      updates_disabled_(false),
      observer_(NULL) {
  registrar_.Add(this, NotificationType::CONTENT_SETTINGS_CHANGED,
                 NotificationService::AllSources());
}

PluginExceptionsTableModel::~PluginExceptionsTableModel() {
}

bool PluginExceptionsTableModel::CanRemoveRows(const Rows& rows) const {
  return !rows.empty();
}

void PluginExceptionsTableModel::RemoveRows(const Rows& rows) {
  AutoReset<bool> tmp(&updates_disabled_, true);
  bool reload_all = false;
  // Iterate over the rows starting with the highest ones so we can delete
  // entries from |settings_| without the other indices shifting.
  for (Rows::const_reverse_iterator it = rows.rbegin();
       it != rows.rend(); ++it) {
    DCHECK_LT(*it, settings_.size());
    SettingsEntry entry = settings_[*it];
    HostContentSettingsMap* map = entry.is_otr ? otr_map_ : map_;
    map->SetContentSetting(entry.pattern,
                           CONTENT_SETTINGS_TYPE_PLUGINS,
                           resources_[entry.plugin_id],
                           CONTENT_SETTING_DEFAULT);
    settings_.erase(settings_.begin() + *it);
    row_counts_[entry.plugin_id]--;
    if (!reload_all) {
      // If we remove the last exception for a plugin, recreate all groups
      // to get correct IDs.
      if (row_counts_[entry.plugin_id] == 0)  {
        reload_all = true;
      } else {
        if (observer_)
          observer_->OnItemsRemoved(*it, 1);
      }
    }
  }
  if (reload_all) {
    // This also notifies the observer.
    ReloadSettings();
  }
}

void PluginExceptionsTableModel::RemoveAll() {
  AutoReset<bool> tmp(&updates_disabled_, true);
  map_->ClearSettingsForOneType(CONTENT_SETTINGS_TYPE_PLUGINS);
  if (otr_map_)
    otr_map_->ClearSettingsForOneType(CONTENT_SETTINGS_TYPE_PLUGINS);

  ClearSettings();
  if (observer_)
    observer_->OnModelChanged();
}

int PluginExceptionsTableModel::RowCount() {
  return settings_.size();
}

string16 PluginExceptionsTableModel::GetText(int row, int column_id) {
  DCHECK_GE(row, 0);
  DCHECK_LT(row, static_cast<int>(settings_.size()));
  SettingsEntry& entry = settings_[row];
  if (column_id == IDS_EXCEPTIONS_PATTERN_HEADER ||
      column_id == IDS_EXCEPTIONS_HOSTNAME_HEADER) {
    return UTF8ToUTF16(entry.pattern.AsString());
  } else if (column_id == IDS_EXCEPTIONS_ACTION_HEADER) {
    switch (entry.setting) {
      case CONTENT_SETTING_ALLOW:
        return l10n_util::GetStringUTF16(IDS_EXCEPTIONS_ALLOW_BUTTON);
      case CONTENT_SETTING_BLOCK:
        return l10n_util::GetStringUTF16(IDS_EXCEPTIONS_BLOCK_BUTTON);
      default:
        NOTREACHED();
    }
  } else {
    NOTREACHED();
  }
  return string16();
}

bool PluginExceptionsTableModel::HasGroups() {
  return true;
}

void PluginExceptionsTableModel::SetObserver(ui::TableModelObserver* observer) {
  observer_ = observer;
}

ui::TableModel::Groups PluginExceptionsTableModel::GetGroups() {
  return groups_;
}

int PluginExceptionsTableModel::GetGroupID(int row) {
  DCHECK_LT(row, static_cast<int>(settings_.size()));
  return settings_[row].plugin_id;
}

void PluginExceptionsTableModel::Observe(NotificationType type,
                                         const NotificationSource& source,
                                         const NotificationDetails& details) {
  if (!updates_disabled_)
    ReloadSettings();
}

void PluginExceptionsTableModel::ClearSettings() {
  settings_.clear();
  groups_.clear();
  row_counts_.clear();
  resources_.clear();
}

void PluginExceptionsTableModel::GetPlugins(
    std::vector<webkit::npapi::PluginGroup>* plugin_groups) {
  webkit::npapi::PluginList::Singleton()->GetPluginGroups(false, plugin_groups);
}

void PluginExceptionsTableModel::LoadSettings() {
  int group_id = 0;
  std::vector<webkit::npapi::PluginGroup> plugins;
  GetPlugins(&plugins);
  for (size_t i = 0; i < plugins.size(); ++i) {
    std::string plugin = plugins[i].identifier();
    HostContentSettingsMap::SettingsForOneType settings;
    map_->GetSettingsForOneType(CONTENT_SETTINGS_TYPE_PLUGINS,
                                plugin,
                                &settings);
    HostContentSettingsMap::SettingsForOneType otr_settings;
    if (otr_map_) {
      otr_map_->GetSettingsForOneType(CONTENT_SETTINGS_TYPE_PLUGINS,
                                      plugin,
                                      &otr_settings);
    }
    string16 title = plugins[i].GetGroupName();
    for (HostContentSettingsMap::SettingsForOneType::iterator setting_it =
             settings.begin();
         setting_it != settings.end(); ++setting_it) {
      SettingsEntry entry = {
        setting_it->first,
        group_id,
        setting_it->second,
        false
      };
      settings_.push_back(entry);
    }
    for (HostContentSettingsMap::SettingsForOneType::iterator setting_it =
             otr_settings.begin();
         setting_it != otr_settings.end(); ++setting_it) {
      SettingsEntry entry = {
        setting_it->first,
        group_id,
        setting_it->second,
        true
      };
      settings_.push_back(entry);
    }
    int num_plugins = settings.size() + otr_settings.size();
    if (num_plugins > 0) {
      Group group = { title, group_id++ };
      groups_.push_back(group);
      resources_.push_back(plugin);
      row_counts_.push_back(num_plugins);
    }
  }
}

void PluginExceptionsTableModel::ReloadSettings() {
  ClearSettings();
  LoadSettings();

  if (observer_)
    observer_->OnModelChanged();
}