// Aseprite
// Copyright (C) 2018  Igara Studio S.A.
// Copyright (C) 2001-2018  David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "app/recent_files.h"

#include "app/ini_file.h"
#include "base/convert_to.h"
#include "base/fs.h"

#include <cstdio>
#include <cstring>
#include <set>

namespace {

const char* kSectionName[] = { "PinnedFiles",
                               "RecentFiles",
                               "PinnedPaths",
                               "RecentPaths" };

struct compare_path {
  std::string a;
  compare_path(const std::string& a) : a(a) { }
  bool operator()(const std::string& b) const {
    return base::compare_filenames(a, b) == 0;
  }
};

}

namespace app {

RecentFiles::RecentFiles(const int limit)
  : m_limit(limit)
{
  load();
}

RecentFiles::~RecentFiles()
{
  save();
}

void RecentFiles::addRecentFile(const std::string& filename)
{
  std::string fn = normalizePath(filename);

  // If the filename is already pinned, we don't add it in the
  // collection of recent files collection.
  auto it = std::find(m_paths[kPinnedFiles].begin(),
                      m_paths[kPinnedFiles].end(), fn);
  if (it != m_paths[kPinnedFiles].end())
    return;
  addItem(m_paths[kRecentFiles], fn);

  // Add recent folder
  std::string path = base::get_file_path(fn);
  it = std::find(m_paths[kPinnedFolders].begin(),
                 m_paths[kPinnedFolders].end(), path);
  if (it == m_paths[kPinnedFolders].end()) {
    addItem(m_paths[kRecentFolders], path);
  }

  Changed();
}

void RecentFiles::removeRecentFile(const std::string& filename)
{
  std::string fn = normalizePath(filename);
  removeItem(m_paths[kRecentFiles], fn);

  std::string dir = base::get_file_path(fn);
  if (!base::is_directory(dir))
    removeRecentFolder(dir);

  Changed();
}

void RecentFiles::removeRecentFolder(const std::string& dir)
{
  std::string fn = normalizePath(dir);
  removeItem(m_paths[kRecentFolders], fn);

  Changed();
}

void RecentFiles::setLimit(const int newLimit)
{
  ASSERT(newLimit >= 0);

  for (auto& list : m_paths) {
    if (newLimit < list.size()) {
      auto it = list.begin();
      std::advance(it, newLimit);
      list.erase(it, list.end());
    }
  }

  m_limit = newLimit;
  Changed();
}

void RecentFiles::clear()
{
  // Clear only recent items (not pinned items)
  m_paths[kRecentFiles].clear();
  m_paths[kRecentFolders].clear();

  Changed();
}

void RecentFiles::setFiles(const base::paths& pinnedFiles,
                           const base::paths& recentFiles)
{
  m_paths[kPinnedFiles] = pinnedFiles;
  m_paths[kRecentFiles] = recentFiles;
}

void RecentFiles::setFolders(const base::paths& pinnedFolders,
                             const base::paths& recentFolders)
{
  m_paths[kPinnedFolders] = pinnedFolders;
  m_paths[kRecentFolders] = recentFolders;
}

std::string RecentFiles::normalizePath(const std::string& filename)
{
  return base::normalize_path(filename);
}

void RecentFiles::addItem(base::paths& list, const std::string& fn)
{
  auto it = std::find_if(list.begin(), list.end(), compare_path(fn));

  // If the item already exist in the list...
  if (it != list.end()) {
    // Move it to the first position
    list.erase(it);
    list.insert(list.begin(), fn);
    return;
  }

  if (m_limit > 0)
    list.insert(list.begin(), fn);

  while (list.size() > m_limit)
    list.erase(--list.end());
}

void RecentFiles::removeItem(base::paths& list, const std::string& fn)
{
  auto it = std::find_if(list.begin(), list.end(), compare_path(fn));
  if (it != list.end())
    list.erase(it);
}

void RecentFiles::load()
{
  for (int i=0; i<kCollections; ++i) {
    for (const auto& key : enum_config_keys(kSectionName[i])) {
      const char* fn = get_config_string(kSectionName[i], key.c_str(), nullptr);
      if (fn && *fn &&
          ((i < 2 && base::is_file(fn)) ||
           (i >= 2 && base::is_directory(fn)))) {
        std::string normalFn = normalizePath(fn);
        m_paths[i].push_back(normalFn);
      }
    }
  }
}

void RecentFiles::save()
{
  for (int i=0; i<kCollections; ++i) {
    for (const auto& key : enum_config_keys(kSectionName[i]))
      del_config_value(kSectionName[i],
                       key.c_str());

    for (int j=0; j<m_paths[i].size(); ++j) {
      set_config_string(kSectionName[i],
                        base::convert_to<std::string>(j).c_str(),
                        m_paths[i][j].c_str());
    }
  }
}

} // namespace app
