using content::NavigationController;
using content::WebContents;

namespace browser_sync {

NotificationServiceSessionsRouter::NotificationServiceSessionsRouter(
    Profile* profile, const syncer::SyncableService::StartSyncFlare& flare)
        : handler_(NULL),
          profile_(profile),
          flare_(flare),
          weak_ptr_factory_(this) {
  registrar_.Add(this, chrome::NOTIFICATION_TAB_PARENTED,
      content::NotificationService::AllSources());
  registrar_.Add(this, content::NOTIFICATION_WEB_CONTENTS_DESTROYED,
      content::NotificationService::AllSources());
  registrar_.Add(this, content::NOTIFICATION_NAV_LIST_PRUNED,
      content::NotificationService::AllSources());
  registrar_.Add(this, content::NOTIFICATION_NAV_ENTRY_CHANGED,
      content::NotificationService::AllSources());
  registrar_.Add(this, content::NOTIFICATION_NAV_ENTRY_COMMITTED,
      content::NotificationService::AllSources());
#if defined(ENABLE_EXTENSIONS)
  registrar_.Add(this,
      chrome::NOTIFICATION_TAB_CONTENTS_APPLICATION_EXTENSION_CHANGED,
      content::NotificationService::AllSources());
#endif
  registrar_.Add(this,
      content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME,
      content::NotificationService::AllBrowserContextsAndSources());
  history::HistoryService* history_service =
      HistoryServiceFactory::GetForProfile(profile,
                                           ServiceAccessType::EXPLICIT_ACCESS);
  if (history_service) {
    favicon_changed_subscription_ = history_service->AddFaviconsChangedCallback(
        base::Bind(&NotificationServiceSessionsRouter::OnFaviconsChanged,
                   base::Unretained(this)));
  }
#if defined(ENABLE_SUPERVISED_USERS)
  if (profile_->IsSupervised()) {
    SupervisedUserService* supervised_user_service =
        SupervisedUserServiceFactory::GetForProfile(profile_);
    supervised_user_service->AddNavigationBlockedCallback(
        base::Bind(&NotificationServiceSessionsRouter::OnNavigationBlocked,
                   weak_ptr_factory_.GetWeakPtr()));
  }
#endif
}

NotificationServiceSessionsRouter::~NotificationServiceSessionsRouter() {}

void NotificationServiceSessionsRouter::Observe(
    int type,
    const content::NotificationSource& source,
    const content::NotificationDetails& details) {
  switch (type) {
    // Source<WebContents>.
    case chrome::NOTIFICATION_TAB_PARENTED:
    case content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME:
    case content::NOTIFICATION_WEB_CONTENTS_DESTROYED: {
      WebContents* web_contents = content::Source<WebContents>(source).ptr();
      SyncedTabDelegate* tab =
          SyncedTabDelegate::ImplFromWebContents(web_contents);
      if (!tab || tab->profile() != profile_)
        return;
      if (handler_)
        handler_->OnLocalTabModified(tab);
      if (!tab->ShouldSync())
        return;
      break;
    }
    // Source<NavigationController>.
    case content::NOTIFICATION_NAV_LIST_PRUNED:
    case content::NOTIFICATION_NAV_ENTRY_CHANGED:
    case content::NOTIFICATION_NAV_ENTRY_COMMITTED: {
      SyncedTabDelegate* tab = SyncedTabDelegate::ImplFromWebContents(
          content::Source<NavigationController>(source).ptr()->
              GetWebContents());
      if (!tab || tab->profile() != profile_)
        return;
      if (handler_)
        handler_->OnLocalTabModified(tab);
      if (!tab->ShouldSync())
        return;
      break;
    }
#if defined(ENABLE_EXTENSIONS)
    case chrome::NOTIFICATION_TAB_CONTENTS_APPLICATION_EXTENSION_CHANGED: {
      extensions::TabHelper* extension_tab_helper =
          content::Source<extensions::TabHelper>(source).ptr();
      if (extension_tab_helper->web_contents()->GetBrowserContext() !=
              profile_) {
        return;
      }
      if (extension_tab_helper->extension_app()) {
        SyncedTabDelegate* tab = SyncedTabDelegate::ImplFromWebContents(
            extension_tab_helper->web_contents());
        if (!tab || tab->profile() != profile_)
          return;
        if (handler_)
          handler_->OnLocalTabModified(tab);
        break;
      }
      return;
    }
#endif
    default:
      LOG(ERROR) << "Received unexpected notification of type " << type;
      return;
  }

  if (!flare_.is_null()) {
    flare_.Run(syncer::SESSIONS);
    flare_.Reset();
  }
}

void NotificationServiceSessionsRouter::OnNavigationBlocked(
    content::WebContents* web_contents) {
  SyncedTabDelegate* tab =
      SyncedTabDelegate::ImplFromWebContents(web_contents);
  if (!tab || !handler_)
    return;

  DCHECK(tab->profile() == profile_);
  handler_->OnLocalTabModified(tab);
}

void NotificationServiceSessionsRouter::OnFaviconsChanged(
    const std::set<GURL>& page_urls,
    const GURL& icon_url) {
  if (handler_)
    handler_->OnFaviconsChanged(page_urls, icon_url);
}

void NotificationServiceSessionsRouter::StartRoutingTo(
    LocalSessionEventHandler* handler) {
  DCHECK(!handler_);
  handler_ = handler;
}

void NotificationServiceSessionsRouter::Stop() {
  weak_ptr_factory_.InvalidateWeakPtrs();
  handler_ = NULL;
}

}  // namespace browser_sync
