// Copyright (c) 2012 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.
//
// Objects that handle file operations for saving files, on the file thread.
//
// The SaveFileManager owns a set of SaveFile objects, each of which connects
// with a SaveItem object which belongs to one SavePackage and runs on the file
// thread for saving data in order to avoid disk activity on either network IO
// thread or the UI thread. It coordinates the notifications from the network
// and UI.
//
// The SaveFileManager itself is a singleton object created and owned by the
// BrowserMainLoop.
//
// The data sent to SaveFileManager have 2 sources:
// - SimpleURLLoaders which are used to download sub-resources and
//   save-only-HTML pages
// - render processese, for HTML pages which are serialized from the DOM in
//   their original encoding. The data is received on the UI thread and
//   dispatched directly to the SaveFileManager on the file thread.
//
// A typical saving job operation involves multiple threads and sequences:
//
// Updating an in progress save file:
//      |----> data from    ---->|  |
//      |      render process    |  |
//      |      SimpleURLLoaders  |  |
// ui_thread                     |  |
//                   download_task_runner (writes to disk)
//                               |----> stats ---->|
//                                              ui_thread (feedback for user)
//
//
// Cancel operations perform the inverse order when triggered by a user action:
// ui_thread (user click)
//    |----> cancel command ---->|
//    |           |      download_task_runner (close file)
//    |           |---------------------> cancel command ---->|
//    |                                               io_thread (stops net IO
// ui_thread (user close contents)                               for saving)
//    |----> cancel command ---->|
//                            Render process(stop serializing DOM and sending
//                                           data)
//
//
// The SaveFileManager tracks saving requests, mapping from a save item id to
// the SavePackage for the contents where the saving job was initiated. In the
// event of a contents closure during saving, the SavePackage will notify the
// SaveFileManage to cancel all SaveFile jobs.

#ifndef CONTENT_BROWSER_DOWNLOAD_SAVE_FILE_MANAGER_H_
#define CONTENT_BROWSER_DOWNLOAD_SAVE_FILE_MANAGER_H_

#include <stdint.h>

#include <memory>
#include <string>
#include <unordered_map>
#include <vector>

#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "content/browser/download/save_types.h"
#include "content/common/content_export.h"

class GURL;

namespace base {
class FilePath;
}

namespace content {
class ResourceContext;
class SaveFile;
class SavePackage;
class StoragePartition;
struct Referrer;

class CONTENT_EXPORT SaveFileManager
    : public base::RefCountedThreadSafe<SaveFileManager> {
 public:
   // Returns the singleton instance of the SaveFileManager.
  static SaveFileManager* Get();

  SaveFileManager();

  // Lifetime management.
  void Shutdown();

  // Saves the specified URL |url|. |save_package| must not be deleted before
  // the call to RemoveSaveFile. Should be called on the UI thread,
  void SaveURL(SaveItemId save_item_id,
               const GURL& url,
               const Referrer& referrer,
               int render_process_host_id,
               int render_view_routing_id,
               int render_frame_routing_id,
               SaveFileCreateInfo::SaveFileSource save_source,
               const base::FilePath& file_full_path,
               ResourceContext* context,
               StoragePartition* storage_partition,
               SavePackage* save_package);

  // Notifications sent from the IO thread and run on the file thread:
  void StartSave(std::unique_ptr<SaveFileCreateInfo> info);
  void UpdateSaveProgress(SaveItemId save_item_id, const std::string& data);
  void SaveFinished(SaveItemId save_item_id,
                    SavePackageId save_package_id,
                    bool is_success);

  // Notifications sent from the UI thread and run on the file thread.
  // Cancel a SaveFile instance which has specified save item id.
  void CancelSave(SaveItemId save_item_id);

  // Called on the UI thread to remove a save package from SaveFileManager's
  // tracking map.
  void RemoveSaveFile(SaveItemId save_item_id, SavePackage* package);

  // Helper function for deleting specified file.
  void DeleteDirectoryOrFile(const base::FilePath& full_path, bool is_dir);

  // Runs on file thread to save a file by copying from file system when
  // original url is using file scheme.
  void SaveLocalFile(const GURL& original_file_url,
                     SaveItemId save_item_id,
                     SavePackageId save_package_id);

  // Renames all the successfully saved files.
  void RenameAllFiles(const FinalNamesMap& final_names,
                      const base::FilePath& resource_dir,
                      int render_process_id,
                      int render_frame_routing_id,
                      SavePackageId save_package_id);

  // When the user cancels the saving, we need to remove all remaining saved
  // files of this page saving job from save_file_map_.
  void RemoveSavedFileFromFileMap(const std::vector<SaveItemId>& save_item_ids);

 private:
  friend class base::RefCountedThreadSafe<SaveFileManager>;

  class SimpleURLLoaderHelper;

  ~SaveFileManager();

  // A cleanup helper that runs on the file thread.
  void OnShutdown();

  // Called only on UI thread to get the SavePackage for a contents's browser
  // context.
  static SavePackage* GetSavePackageFromRenderIds(int render_process_id,
                                                  int render_frame_routing_id);

  // Look up the SavePackage according to save item id.
  SavePackage* LookupPackage(SaveItemId save_item_id);

  // Called only on the file thread.
  // Look up one in-progress saving item according to save item id.
  SaveFile* LookupSaveFile(SaveItemId save_item_id);

  // Help function for sending notification of canceling specific request.
  void SendCancelRequest(SaveItemId save_item_id);

  // Notifications sent from the file thread and run on the UI thread.

  // Lookup the SaveManager for this WebContents' saving browser context and
  // inform it the saving job has been started.
  void OnStartSave(const SaveFileCreateInfo& info);
  // Update the SavePackage with the current state of a started saving job.
  // If the SavePackage for this saving job is gone, cancel the request.
  void OnUpdateSaveProgress(SaveItemId save_item_id,
                            int64_t bytes_so_far,
                            bool write_success);
  // Update the SavePackage with the finish state, and remove the request
  // tracking entries.
  void OnSaveFinished(SaveItemId save_item_id,
                      int64_t bytes_so_far,
                      bool is_success);
  // Notifies SavePackage that the whole page saving job is finished.
  void OnFinishSavePageJob(int render_process_id,
                           int render_frame_routing_id,
                           SavePackageId save_package_id);

  // Notifications sent from the UI thread and run on the file thread.

  // Deletes a specified file on the file thread.
  void OnDeleteDirectoryOrFile(const base::FilePath& full_path, bool is_dir);

  // Notifications sent from the UI thread and run on the IO thread

  // Initiates a request for URL to be saved.
  void OnSaveURL(const GURL& url,
                 const Referrer& referrer,
                 SaveItemId save_item_id,
                 SavePackageId save_package_id,
                 int render_process_host_id,
                 int render_view_routing_id,
                 int render_frame_routing_id,
                 StoragePartition* storage_partition);

  // Called on the UI thread to remove the SimpleURLLoader in
  // |url_loader_helpers_| associated with |save_item_id|. This stops the load
  // if it is not complete.
  void ClearURLLoader(SaveItemId save_item_id);

  // A map from save_item_id into SaveFiles.
  std::unordered_map<SaveItemId, std::unique_ptr<SaveFile>, SaveItemId::Hasher>
      save_file_map_;

  // Tracks which SavePackage to send data to, called only on UI thread.
  // SavePackageMap maps save item ids to their SavePackage.
  std::unordered_map<SaveItemId, SavePackage*, SaveItemId::Hasher> packages_;

  // The helper object doing the actual download. Should be accessed on the UI
  // thread.
  std::unordered_map<SaveItemId,
                     std::unique_ptr<SimpleURLLoaderHelper>,
                     SaveItemId::Hasher>
      url_loader_helpers_;

  DISALLOW_COPY_AND_ASSIGN(SaveFileManager);
};

}  // namespace content

#endif  // CONTENT_BROWSER_DOWNLOAD_SAVE_FILE_MANAGER_H_
