// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Autogenerated from Pigeon (v10.1.6), do not edit directly.
// See also: https://pub.dev/packages/pigeon

#undef _HAS_EXCEPTIONS

#include "messages.h"

#include <flutter/basic_message_channel.h>
#include <flutter/binary_messenger.h>
#include <flutter/encodable_value.h>
#include <flutter/method_call.h>
#include <flutter/method_channel.h>
#include <flutter/standard_method_codec.h>

#include <filesystem>
#include <sstream>
#include <string>

#include "plugins/common/common.h"

namespace plugin_file_selector {

static constexpr char kMethodOpenFile[] = "openFile";
static constexpr char kGetSavePath[] = "getSavePath";
static constexpr char kGetDirectoryPath[] = "getDirectoryPath";

static constexpr char kArgInitialDirectory[] = "initialDirectory";
static constexpr char kArgConfirmButtonText[] = "confirmButtonText";
static constexpr char kArgAcceptedTypeGroups[] = "acceptedTypeGroups";

static constexpr char kArgTypeGroupLabel[] = "label";
static constexpr char kArgTypeGroupExtensions[] = "extensions";
// TODO static constexpr char kArgTypeGroupMime[] = "mimeTypes";
static constexpr char kArgMultiple[] = "multiple";
static constexpr char kArgSuggestedName[] = "suggestedName";

using flutter::BasicMessageChannel;
using flutter::CustomEncodableValue;
using flutter::EncodableList;
using flutter::EncodableMap;
using flutter::EncodableValue;

// Sets up an instance of `UrlLauncherApi` to handle messages through the
// `binary_messenger`.
void FileSelectorApi::SetUp(flutter::BinaryMessenger* binary_messenger,
                            const FileSelectorApi* api) {
  {
    const auto channel = std::make_unique<flutter::MethodChannel<>>(
        binary_messenger, "plugins.flutter.dev/file_selector_linux",
        &flutter::StandardMethodCodec::GetInstance());
    if (api != nullptr) {
      channel->SetMethodCallHandler(
          [](const flutter::MethodCall<>& call,
             const std::unique_ptr<flutter::MethodResult<>>& result) {
            SPDLOG_DEBUG("[file_selector] {}", call.method_name());
            if (call.method_name() == kGetDirectoryPath) {
              SPDLOG_DEBUG("[file_selector] getDirectoryPath:");
              if (call.arguments()->IsNull()) {
                result->Error("invalid_arguments", "");
                return;
              }

              std::string initialDirectory;
              std::string confirmButtonText;
              bool multiple{};
              std::ostringstream oss;

              auto args = std::get_if<EncodableMap>(call.arguments());
              for (const auto& [fst, snd] : *args) {
                if (auto key = std::get<std::string>(fst);
                    key == kArgInitialDirectory && !snd.IsNull() &&
                    std::holds_alternative<std::string>(snd)) {
                  initialDirectory = std::get<std::string>(snd);
                } else if (key == kArgConfirmButtonText && !snd.IsNull()) {
                  confirmButtonText.assign(std::get<std::string>(snd));
                } else if (key == kArgMultiple && !snd.IsNull() &&
                           std::holds_alternative<bool>(snd)) {
                  multiple = std::get<bool>(snd);
                }
              }

              SPDLOG_DEBUG("initialDirectory: [{}]", initialDirectory);
              if (!initialDirectory.empty()) {
                if (std::filesystem::exists(initialDirectory)) {
                  oss << "cd " << initialDirectory << " && ";
                }
              }

              oss << "zenity --file-selection --directory";

              SPDLOG_DEBUG("multiple: [{}]", multiple);
              if (multiple) {
                oss << " --multiple";
              }

              SPDLOG_DEBUG("confirmButtonText: [{}]", confirmButtonText);
              if (!confirmButtonText.empty()) {
                oss << " --title=" << confirmButtonText;
              }

              SPDLOG_DEBUG("cmd: [{}]", oss.str());

              std::string path;
              if (!plugin_common::Command::Execute(oss.str().c_str(), path)) {
                result->Error("failed", "failed to execute command");
                return;
              }
              EncodableList results;
              auto paths = plugin_common::StringTools::split(path, "|");
              for (auto p : paths) {
                results.emplace_back(EncodableValue(
                    std::move(plugin_common::StringTools::trim(p, "\n"))));
              }
              result->Success(flutter::EncodableValue(results));
              return;
            }
            if (call.method_name() == kGetSavePath) {
              SPDLOG_DEBUG("[file_selector] getSavePath:");
              if (call.arguments()->IsNull()) {
                result->Error("invalid_arguments", "");
                return;
              }

              std::filesystem::path filepath;
              std::string initialDirectory;
              std::string suggestedName;
              std::string confirmButtonText;
              std::ostringstream oss;

              auto args = std::get_if<EncodableMap>(call.arguments());
              for (const auto& [fst, snd] : *args) {
                if (auto key = std::get<std::string>(fst);
                    key == kArgInitialDirectory && !snd.IsNull() &&
                    std::holds_alternative<std::string>(snd)) {
                  initialDirectory = std::get<std::string>(snd);
                } else if (key == kArgSuggestedName && !snd.IsNull() &&
                           std::holds_alternative<std::string>(snd)) {
                  suggestedName.assign(std::get<std::string>(snd));
                } else if (key == kArgConfirmButtonText && !snd.IsNull()) {
                  confirmButtonText.assign(std::get<std::string>(snd));
                }
              }

              SPDLOG_DEBUG("initialDirectory: [{}]", initialDirectory);
              filepath.assign(initialDirectory);
              if (!exists(filepath)) {
                if (!create_directories(filepath)) {
                  result->Error("error", "Failed to create directories");
                  return;
                }
              }
              SPDLOG_DEBUG("suggestedName: [{}]", suggestedName);
              filepath.append(suggestedName);
              SPDLOG_DEBUG("confirmButtonText: [{}]", confirmButtonText);
              SPDLOG_DEBUG("save filepath: [{}]", filepath.c_str());
              EncodableValue val(filepath.c_str());
              result->Success(EncodableValue(filepath.c_str()));
              return;
            }
            if (call.method_name() == kMethodOpenFile) {
              SPDLOG_DEBUG("[file_selector] openFile");

              if (call.arguments()->IsNull()) {
                result->Error("invalid_arguments", "");
                return;
              }

              std::string initialDirectory;
              std::string confirmButtonText;
              std::string label;
              std::ostringstream oss;
              std::stringstream extensions;
              bool multiple{};

              auto args = std::get_if<EncodableMap>(call.arguments());
              for (const auto& [fst, snd] : *args) {
                if (auto key = std::get<std::string>(fst);
                    key == kArgAcceptedTypeGroups && !snd.IsNull() &&
                    std::holds_alternative<EncodableList>(snd)) {
                  auto acceptedTypeGroups = std::get<EncodableList>(snd);
                  for (auto const& group : acceptedTypeGroups) {
                    if (!group.IsNull() &&
                        std::holds_alternative<EncodableMap>(group)) {
                      auto map = std::get<EncodableMap>(group);
                      for (const auto& [fst, snd] : map) {
                        if (std::holds_alternative<std::string>(fst) &&
                            std::holds_alternative<std::string>(snd)) {
                          if (const auto k = std::get<std::string>(fst);
                              k == kArgTypeGroupLabel) {
                            label = std::get<std::string>(snd);
                            SPDLOG_DEBUG("{}: {}", k, label);
                          }
                        }
                        if (std::holds_alternative<std::string>(fst) &&
                            std::holds_alternative<EncodableList>(snd)) {
                          auto k = std::get<std::string>(fst);
                          auto list = std::get<EncodableList>(snd);
                          for (const auto& item : list) {
                            if (std::holds_alternative<std::string>(item)) {
                              auto value = std::get<std::string>(item);
                              if (k == kArgTypeGroupExtensions) {
                                extensions << " " << value;
                                SPDLOG_DEBUG("{}: {}", k, value);
                              }
                            }
                          }
                        }
                      }
                    }
                  }
                } else if (key == kArgInitialDirectory && !snd.IsNull() &&
                           std::holds_alternative<std::string>(snd)) {
                  initialDirectory.assign(std::get<std::string>(snd));
                } else if (key == kArgConfirmButtonText && !snd.IsNull()) {
                  confirmButtonText.assign(std::get<std::string>(snd));
                } else if (key == kArgMultiple && !snd.IsNull() &&
                           std::holds_alternative<bool>(snd)) {
                  multiple = std::get<bool>(snd);
                }
              }

              SPDLOG_DEBUG("initialDirectory: [{}]", initialDirectory);
              if (!initialDirectory.empty()) {
                if (std::filesystem::exists(initialDirectory)) {
                  oss << "cd " << initialDirectory << " && ";
                }
              }

              oss << "zenity --file-selection --file-filter=\"" << label
                  << " | " << extensions.str() << "\"";

              SPDLOG_DEBUG("multiple: {}", multiple);
              if (multiple) {
                oss << " --multiple";
              }

              SPDLOG_DEBUG("confirmButtonText: [{}]", confirmButtonText);
              if (!confirmButtonText.empty()) {
                oss << " --title=" << confirmButtonText;
              }

              SPDLOG_DEBUG("cmd: [{}]", oss.str());

              std::string path;
              if (!plugin_common::Command::Execute(oss.str().c_str(), path)) {
                result->Error("failed", "failed to execute command");
                return;
              }

              flutter::EncodableList results;
              auto paths = plugin_common::StringTools::split(path, "|");
              for (auto p : paths) {
                results.emplace_back(
                    std::move(plugin_common::StringTools::trim(p, "\n")));
              }
              result->Success(flutter::EncodableValue(results));
            } else {
              result->NotImplemented();
            }
          });
    } else {
      channel->SetMethodCallHandler(nullptr);
    }
  }
}

EncodableValue FileSelectorApi::WrapError(
    const std::string_view error_message) {
  return EncodableValue(
      EncodableList{EncodableValue(std::string(error_message)),
                    EncodableValue("Error"), EncodableValue()});
}

EncodableValue FileSelectorApi::WrapError(const FlutterError& error) {
  return EncodableValue(EncodableList{EncodableValue(error.code()),
                                      EncodableValue(error.message()),
                                      error.details()});
}

}  // namespace plugin_file_selector
