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

#include "chrome/browser/printing/print_job_manager.h"

#include "base/functional/bind.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "chrome/browser/printing/print_job.h"
#include "chrome/browser/printing/printer_query.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/global_routing_id.h"
#include "printing/printed_document.h"

#include "base/win/windows_h_disallowed.h"

namespace printing {

PrintQueriesQueue::PrintQueriesQueue() = default;

PrintQueriesQueue::~PrintQueriesQueue() {
  base::AutoLock lock(lock_);
  queued_queries_.clear();
}

void PrintQueriesQueue::QueuePrinterQuery(std::unique_ptr<PrinterQuery> query) {
  base::AutoLock lock(lock_);
  DCHECK(query);
  DCHECK(query->is_valid());
  queued_queries_.push_back(std::move(query));
}

std::unique_ptr<PrinterQuery> PrintQueriesQueue::PopPrinterQuery(
    int document_cookie) {
  base::AutoLock lock(lock_);
  for (auto it = queued_queries_.begin(); it != queued_queries_.end(); ++it) {
    std::unique_ptr<PrinterQuery>& query = *it;
    if (query->cookie() != document_cookie)
      continue;

    std::unique_ptr<PrinterQuery> current_query = std::move(query);
    queued_queries_.erase(it);
    DCHECK(current_query->is_valid());
    return current_query;
  }
  return nullptr;
}

std::unique_ptr<PrinterQuery> PrintQueriesQueue::CreatePrinterQuery(
    content::GlobalRenderFrameHostId rfh_id) {
  return PrinterQuery::Create(rfh_id);
}

void PrintQueriesQueue::Shutdown() {
  {
    base::AutoLock lock(lock_);
    queued_queries_.clear();
  }
}

PrintJobManager::PrintJobManager() = default;

PrintJobManager::~PrintJobManager() = default;

base::CallbackListSubscription PrintJobManager::AddDocDoneCallback(
    DocDoneCallback callback) {
  return callback_list_.Add(callback);
}

scoped_refptr<PrintQueriesQueue> PrintJobManager::queue() {
  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
  if (!queue_)
    queue_ = base::MakeRefCounted<PrintQueriesQueue>();
  return queue_;
}

void PrintJobManager::SetQueueForTest(scoped_refptr<PrintQueriesQueue> queue) {
  if (queue_)
    queue_->Shutdown();
  queue_ = queue;
}

void PrintJobManager::Shutdown() {
  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
  DCHECK(!is_shutdown_);
  is_shutdown_ = true;
  StopJobs(true);
  if (queue_) {
    queue_->Shutdown();
    queue_ = nullptr;
  }
}

void PrintJobManager::StopJobs(bool wait_for_finish) {
  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
  // Copy the array since it can be modified in transit.
  PrintJobs to_stop;
  to_stop.swap(current_jobs_);

  for (auto job = to_stop.begin(); job != to_stop.end(); ++job) {
    // Wait for two minutes for the print job to be spooled.
    if (wait_for_finish)
      (*job)->FlushJob(base::Minutes(2));
    (*job)->Stop();
  }
}

void PrintJobManager::OnStarted(PrintJob* print_job) {
  if (is_shutdown_)
    return;

  // Causes a AddRef().
  bool inserted = current_jobs_.insert(print_job).second;
  DCHECK(inserted);
}

void PrintJobManager::OnDocDone(PrintJob* print_job,
                                PrintedDocument* document,
                                int job_id) {
  if (is_shutdown_)
    return;

  callback_list_.Notify(print_job, document, job_id);
}

void PrintJobManager::OnJobDone(PrintJob* print_job) {
  if (is_shutdown_)
    return;

  size_t erased = current_jobs_.erase(print_job);
  DCHECK_EQ(1U, erased);
}

void PrintJobManager::OnFailed(PrintJob* print_job) {
  if (is_shutdown_)
    return;

  current_jobs_.erase(print_job);
}

}  // namespace printing
