/*
 * Copyright (C) 2016 Simon Fels <morphis@gravedo.de>
 *
 * This program is free software: you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 3, as published
 * by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranties of
 * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
 * PURPOSE.  See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

#include "anbox/application/launcher_storage.h"
#include "anbox/utils.h"
#include "anbox/logger.h"

#include <algorithm>
#include <fstream>
#include <iostream>

namespace fs = boost::filesystem;

namespace {
// This will always point us to the right executable when we're running within
// a snap environment.
constexpr const char *snap_exe_path{"/snap/bin/anbox"};
}

namespace anbox::application {
LauncherStorage::LauncherStorage(const fs::path &path) :
  path_(path) {}

LauncherStorage::~LauncherStorage() {}

void LauncherStorage::reset() {
  if (fs::exists(path_)) {
    for(auto & p : boost::filesystem::directory_iterator(path_)) {
        if (fs::is_regular_file(p)){
          auto str = p.path().filename().string();
          if (boost::starts_with(str, "anbox-"))
            fs::remove(p);
        }
     }
   }
}

std::string LauncherStorage::clean_package_name(const std::string &package_name) {
  auto cleaned_package_name = package_name;
  std::replace(cleaned_package_name.begin(), cleaned_package_name.end(), '.', '-');
  return cleaned_package_name;
}

fs::path LauncherStorage::path_for_item(const std::string &package_name) {
  return path_ / utils::string_format("anbox-%s.desktop", package_name);
}

fs::path LauncherStorage::path_for_item_icon(const std::string &package_name) {
  return path_ / utils::string_format("anbox-%s.png", package_name);
}

void LauncherStorage::add_or_update(const Database::Item &item) {
  if (!fs::exists(path_)) fs::create_directories(path_);

  auto package_name = item.package;
  std::replace(package_name.begin(), package_name.end(), '.', '-');

  auto exe_path = utils::process_get_exe_path(getpid());
  if (utils::get_env_value("SNAP").length() > 0)
    exe_path = snap_exe_path;

  std::string exec = utils::string_format("%s launch ", exe_path);

  if (!item.launch_intent.action.empty())
    exec += utils::string_format("--action=%s ", item.launch_intent.action);

  if (!item.launch_intent.type.empty())
    exec += utils::string_format("--type=%s ", item.launch_intent.type);

  if (!item.launch_intent.uri.empty())
    exec += utils::string_format("--uri=%s ", item.launch_intent.uri);

  if (!item.launch_intent.package.empty())
    exec += utils::string_format("--package=%s ", item.launch_intent.package);

  if (!item.launch_intent.component.empty())
    exec += utils::string_format("--component=%s ", item.launch_intent.component);

  const auto item_icon_path = path_for_item_icon(package_name);
  if (auto desktop_item = std::ofstream(path_for_item(package_name).string())) {
    desktop_item << "[Desktop Entry]" << std::endl
                 << "Type=Application" << std::endl
                 << "Name=" << item.name << std::endl
                 << "Exec=" << exec << std::endl
                 << "Terminal=false" << std::endl
                 << "Categories=Anbox;" << std::endl
                 << "Icon=" << item_icon_path.string() << std::endl;
  } else {
    BOOST_THROW_EXCEPTION(std::runtime_error("Failed to create desktop item"));
  }

  if (auto icon = std::ofstream(item_icon_path.string()))
    icon.write(item.icon.data(), item.icon.size());
  else
    BOOST_THROW_EXCEPTION(std::runtime_error("Failed to write icon"));
}

void LauncherStorage::remove(const Database::Item &item) {
  auto package_name = clean_package_name(item.package);

  const auto item_path = path_for_item(package_name);
  if (fs::exists(item_path))
    fs::remove(item_path);

  const auto item_icon_path = path_for_item_icon(package_name);
  if (fs::exists(item_icon_path))
    fs::remove(item_icon_path);
}

}