// Copyright 2010-2021, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//     * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#include "client/client_mock.h"

#include <string>

#include "base/logging.h"
#include "protocol/config.pb.h"
#include "absl/synchronization/mutex.h"

namespace mozc {
namespace client {

// Most of methods in ClientMock looks almost same.  Here defines the
// boilerplates for those methods.
#define MockConstBoolImplementation(method_name, argument) \
  bool ClientMock::method_name(argument) const {           \
    absl::MutexLock l(&mutex_);                            \
    function_counter_[#method_name]++;                     \
    std::map<std::string, bool>::const_iterator it =       \
        return_bool_values_.find(#method_name);            \
    if (it != return_bool_values_.end()) {                 \
      return it->second;                                   \
    }                                                      \
    return false;                                          \
  }
#define MockBoolImplementation(method_name, argument) \
  bool ClientMock::method_name(argument) {            \
    absl::MutexLock l(&mutex_);                       \
    function_counter_[#method_name]++;                \
    std::map<std::string, bool>::const_iterator it =  \
        return_bool_values_.find(#method_name);       \
    if (it != return_bool_values_.end()) {            \
      return it->second;                              \
    }                                                 \
    return false;                                     \
  }
#define MockVoidImplementation(method_name, argument) \
  void ClientMock::method_name(argument) {            \
    absl::MutexLock l(&mutex_);                       \
    function_counter_[#method_name]++;                \
    return;                                           \
  }

MockVoidImplementation(SetIPCClientFactory,
                       IPCClientFactoryInterface *client_factory);
MockVoidImplementation(SetServerLauncher,
                       ServerLauncherInterface *server_launcher);
MockConstBoolImplementation(IsValidRunLevel, void);
MockBoolImplementation(EnsureConnection, void);
MockBoolImplementation(EnsureSession, void);
MockBoolImplementation(CheckVersionOrRestartServer, void);
MockBoolImplementation(ClearUserHistory, void);
MockBoolImplementation(ClearUserPrediction, void);
MockBoolImplementation(ClearUnusedUserPrediction, void);
MockBoolImplementation(Shutdown, void);
MockBoolImplementation(SyncData, void);
MockBoolImplementation(Reload, void);
MockBoolImplementation(Cleanup, void);
MockVoidImplementation(Reset, void);
MockConstBoolImplementation(PingServer, void);
MockBoolImplementation(NoOperation, void);
MockVoidImplementation(EnableCascadingWindow, bool enable);
MockVoidImplementation(set_timeout, int timeout);
MockVoidImplementation(set_restricted, bool restricted);
MockVoidImplementation(set_server_program, const std::string &program_path);
MockVoidImplementation(set_suppress_error_dialog, bool suppress_error_dialog);
MockVoidImplementation(set_client_capability,
                       const commands::Capability &capability);
MockBoolImplementation(LaunchToolWithProtoBuf, const commands::Output &output);
MockBoolImplementation(OpenBrowser, const std::string &url);

#undef MockConstImplementation
#undef MockBoolImplementation
#undef MockVoidImplementation

// Another boilerplate for the method with an "output" as its second
// argument.
#define MockImplementationWithContextAndOutput(method_name, argtype) \
  bool ClientMock::method_name(argtype argument,                     \
                               const commands::Context &context,     \
                               commands::Output *output) {           \
    absl::MutexLock l(&mutex_);                                      \
    function_counter_[#method_name]++;                               \
    called_##method_name##_ = argument;                              \
    std::map<std::string, commands::Output>::const_iterator it =     \
        outputs_.find(#method_name);                                 \
    if (it != outputs_.end()) {                                      \
      *output = it->second;                                          \
    }                                                                \
    std::map<std::string, bool>::const_iterator retval =             \
        return_bool_values_.find(#method_name);                      \
    if (retval != return_bool_values_.end()) {                       \
      return retval->second;                                         \
    }                                                                \
    return false;                                                    \
  }

MockImplementationWithContextAndOutput(SendKeyWithContext,
                                       const commands::KeyEvent &);
MockImplementationWithContextAndOutput(TestSendKeyWithContext,
                                       const commands::KeyEvent &);
MockImplementationWithContextAndOutput(SendCommandWithContext,
                                       const commands::SessionCommand &);

#undef MockImplementationWithContextAndOutput

// Exceptional methods.
// GetConfig needs to obtain the "called_config_".
bool ClientMock::GetConfig(config::Config *config) {
  absl::MutexLock l(&mutex_);
  function_counter_["GetConfig"]++;
  *config = called_config_;
  std::map<std::string, bool>::const_iterator it =
      return_bool_values_.find("GetConfig");
  if (it != return_bool_values_.end()) {
    return it->second;
  }
  return false;
}

// SetConfig needs to set the "called_config_".
bool ClientMock::SetConfig(const config::Config &config) {
  absl::MutexLock l(&mutex_);
  function_counter_["SetConfig"]++;
  called_config_ = config;
  std::map<std::string, bool>::const_iterator it =
      return_bool_values_.find("SetConfig");
  if (it != return_bool_values_.end()) {
    return it->second;
  }
  return false;
}

// LaunchTool arguments are quite different from other methods.
bool ClientMock::LaunchTool(const std::string &mode,
                            const std::string &extra_arg) {
  absl::MutexLock l(&mutex_);
  function_counter_["LaunchTool"]++;
  return return_bool_values_["LaunchTool"];
}

// Other methods to deal with internal data such like operations over
// function counters or setting the expected return values.
void ClientMock::ClearFunctionCounter() {
  absl::MutexLock l(&mutex_);
  for (std::map<std::string, int>::iterator it = function_counter_.begin();
       it != function_counter_.end(); it++) {
    it->second = 0;
  }
}

void ClientMock::SetBoolFunctionReturn(std::string func_name, bool value) {
  absl::MutexLock l(&mutex_);
  return_bool_values_[func_name] = value;
}

int ClientMock::GetFunctionCallCount(std::string key) {
  absl::MutexLock l(&mutex_);
  return function_counter_[key];
}

}  // namespace client
}  // namespace mozc
