// 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 "gui/config_dialog/roman_table_editor.h"

#include <QMenu>
#include <QMessageBox>
#include <QtGui>
#include <istream>
#include <memory>
#include <string>
#include <vector>

#include "absl/log/check.h"
#include "absl/strings/str_split.h"
#include "base/config_file_stream.h"
#include "base/util.h"
#include "base/vlog.h"
#include "gui/base/table_util.h"
#include "gui/base/util.h"
#include "gui/config_dialog/generic_table_editor.h"

namespace mozc {
namespace gui {
namespace {
enum {
  NEW_INDEX = 0,
  REMOVE_INDEX = 1,
  IMPORT_FROM_FILE_INDEX = 2,
  EXPORT_TO_FILE_INDEX = 3,
  RESET_INDEX = 4,
  MENU_SIZE = 5
};
constexpr char kRomanTableFile[] = "system://romanji-hiragana.tsv";
}  // namespace

RomanTableEditorDialog::RomanTableEditorDialog(QWidget *parent)
    : GenericTableEditorDialog(parent, 3), actions_(MENU_SIZE) {
  actions_[NEW_INDEX] = mutable_edit_menu()->addAction(tr("New entry"));
  actions_[REMOVE_INDEX] =
      mutable_edit_menu()->addAction(tr("Remove selected entries"));
  mutable_edit_menu()->addSeparator();
  actions_[IMPORT_FROM_FILE_INDEX] =
      mutable_edit_menu()->addAction(tr("Import from file..."));
  actions_[EXPORT_TO_FILE_INDEX] =
      mutable_edit_menu()->addAction(tr("Export to file..."));
  mutable_edit_menu()->addSeparator();
  actions_[RESET_INDEX] =
      mutable_edit_menu()->addAction(tr("Reset to defaults"));

  setWindowTitle(tr("[ProductName] Romaji table editor"));
  GuiUtil::ReplaceWidgetLabels(this);
  dialog_title_ = GuiUtil::ReplaceString(tr("[ProductName] settings"));
  CHECK(mutable_table_widget());
  CHECK_EQ(mutable_table_widget()->columnCount(), 3);
  QStringList headers;
  headers << tr("Input") << tr("Output") << tr("Next input");
  mutable_table_widget()->setHorizontalHeaderLabels(headers);

  resize(330, 350);

  UpdateMenuStatus();
}

std::string RomanTableEditorDialog::GetDefaultRomanTable() {
  std::unique_ptr<std::istream> ifs(
      ConfigFileStream::LegacyOpen(kRomanTableFile));
  CHECK(ifs.get() != nullptr);  // should never happen
  std::string line, result;
  while (std::getline(*ifs, line)) {
    if (line.empty()) {
      continue;
    }
    Util::ChopReturns(&line);
    std::vector<std::string> fields =
        absl::StrSplit(line, '\t', absl::AllowEmpty());
    if (fields.size() < 2) {
      MOZC_VLOG(3) << "field size < 2";
      continue;
    }
    result += fields[0];
    result += '\t';
    result += fields[1];
    if (fields.size() >= 3) {
      result += '\t';
      result += fields[2];
    }
    result += '\n';
  }
  return result;
}

bool RomanTableEditorDialog::LoadFromStream(std::istream *is) {
  CHECK(is);
  std::string line;
  mutable_table_widget()->setRowCount(0);
  mutable_table_widget()->verticalHeader()->hide();

  int row = 0;
  while (std::getline(*is, line)) {
    if (line.empty()) {
      continue;
    }
    Util::ChopReturns(&line);

    std::vector<std::string> fields =
        absl::StrSplit(line, '\t', absl::AllowEmpty());
    if (fields.size() < 2) {
      MOZC_VLOG(3) << "field size < 2";
      continue;
    }

    if (fields.size() == 2) {
      fields.push_back("");
    }

    QTableWidgetItem *input =
        new QTableWidgetItem(QString::fromStdString(fields[0]));
    QTableWidgetItem *output =
        new QTableWidgetItem(QString::fromStdString(fields[1]));
    QTableWidgetItem *pending =
        new QTableWidgetItem(QString::fromStdString(fields[2]));

    mutable_table_widget()->insertRow(row);
    mutable_table_widget()->setItem(row, 0, input);
    mutable_table_widget()->setItem(row, 1, output);
    mutable_table_widget()->setItem(row, 2, pending);
    ++row;

    if (row >= max_entry_size()) {
      QMessageBox::warning(
          this, dialog_title_,
          tr("You can't have more than %1 entries").arg(max_entry_size()));
      break;
    }
  }

  UpdateMenuStatus();

  return true;
}

bool RomanTableEditorDialog::LoadDefaultRomanTable() {
  std::unique_ptr<std::istream> ifs(
      ConfigFileStream::LegacyOpen(kRomanTableFile));
  CHECK(ifs);  // should never happen
  CHECK(LoadFromStream(ifs.get()));
  return true;
}

bool RomanTableEditorDialog::Update() {
  if (mutable_table_widget()->rowCount() == 0) {
    QMessageBox::warning(this, dialog_title_,
                         tr("Romaji to Kana table is empty."));
    return false;
  }

  bool contains_capital = false;
  std::string *table = mutable_table();
  table->clear();
  for (int i = 0; i < mutable_table_widget()->rowCount(); ++i) {
    const std::string &input =
        TableUtil::SafeGetItemText(mutable_table_widget(), i, 0).toStdString();
    const std::string &output =
        TableUtil::SafeGetItemText(mutable_table_widget(), i, 1).toStdString();
    const std::string &pending =
        TableUtil::SafeGetItemText(mutable_table_widget(), i, 2).toStdString();
    if (input.empty() || (output.empty() && pending.empty())) {
      continue;
    }
    *table += input;
    *table += '\t';
    *table += output;
    if (!pending.empty()) {
      *table += '\t';
      *table += pending;
    }
    *table += '\n';

    if (!contains_capital) {
      std::string lower = input;
      Util::LowerString(&lower);
      contains_capital = (lower != input);
    }
  }

  if (contains_capital) {
    // TODO(taku):
    // Want to see the current setting and suppress this
    // dialog if the shift-mode-switch is already off.
    QMessageBox::information(this, dialog_title_,
                             tr("Input fields contain capital characters. "
                                "\"Shift-mode-switch\" function is disabled "
                                "with this new mapping."));
  }

  return true;
}

void RomanTableEditorDialog::UpdateMenuStatus() {
  const bool status = (mutable_table_widget()->rowCount() > 0);
  actions_[RESET_INDEX]->setEnabled(status);
  actions_[REMOVE_INDEX]->setEnabled(status);
  UpdateOKButton(status);
}

void RomanTableEditorDialog::OnEditMenuAction(QAction *action) {
  if (action == actions_[NEW_INDEX]) {
    AddNewItem();
  } else if (action == actions_[REMOVE_INDEX]) {
    DeleteSelectedItems();
  } else if (action == actions_[IMPORT_FROM_FILE_INDEX] ||
             action == actions_[RESET_INDEX]) {  // import or reset
    if (mutable_table_widget()->rowCount() > 0 &&
        QMessageBox::Ok !=
            QMessageBox::question(
                this, dialog_title_,
                tr("Do you want to overwrite the current roman table?"),
                QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Cancel)) {
      return;
    }

    if (action == actions_[IMPORT_FROM_FILE_INDEX]) {
      Import();
    } else if (action == actions_[RESET_INDEX]) {
      LoadDefaultRomanTable();
    }
  } else if (action == actions_[EXPORT_TO_FILE_INDEX]) {
    Export();
  }
}

// static
bool RomanTableEditorDialog::Show(QWidget *parent,
                                  const std::string &current_roman_table,
                                  std::string *new_roman_table) {
  RomanTableEditorDialog window(parent);

  if (current_roman_table.empty()) {
    window.LoadDefaultRomanTable();
  } else {
    window.LoadFromString(current_roman_table);
  }

  // open modal mode
  const bool result = (QDialog::Accepted == window.exec());
  new_roman_table->clear();

  if (result && window.table() != window.GetDefaultRomanTable()) {
    *new_roman_table = window.table();
  }

  return result;
}
}  // namespace gui
}  // namespace mozc
