// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/ash/arc/input_overlay/ui/edit_labels.h"

#include "base/check_op.h"
#include "base/strings/string_util.h"
#include "chrome/browser/ash/arc/input_overlay/actions/action.h"
#include "chrome/browser/ash/arc/input_overlay/display_overlay_controller.h"
#include "chrome/browser/ash/arc/input_overlay/ui/button_options_menu.h"
#include "chrome/browser/ash/arc/input_overlay/ui/edit_label.h"
#include "chrome/browser/ash/arc/input_overlay/ui/editing_list.h"
#include "chrome/browser/ash/arc/input_overlay/ui/name_tag.h"
#include "chrome/browser/ash/arc/input_overlay/ui/ui_utils.h"
#include "chrome/grit/generated_resources.h"
#include "chromeos/strings/grit/chromeos_strings.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/metadata/metadata_impl_macros.h"
#include "ui/views/layout/table_layout.h"
#include "ui/views/view_utils.h"

namespace arc::input_overlay {

// static
std::unique_ptr<EditLabels> EditLabels::CreateEditLabels(
    DisplayOverlayController* controller,
    Action* action,
    NameTag* name_tag,
    bool for_editing_list) {
  auto labels = std::make_unique<EditLabels>(controller, action, name_tag,
                                             for_editing_list);
  labels->Init();
  return labels;
}

EditLabels::EditLabels(DisplayOverlayController* controller,
                       Action* action,
                       NameTag* name_tag,
                       bool for_editing_list)
    : controller_(controller),
      action_(action),
      name_tag_(name_tag),
      for_editing_list_(for_editing_list) {}

EditLabels::~EditLabels() = default;

void EditLabels::Init() {
  switch (action_->GetType()) {
    case ActionType::TAP:
      InitForActionTapKeyboard();
      break;
    case ActionType::MOVE:
      InitForActionMoveKeyboard();
      break;
    default:
      NOTREACHED();
  }

  UpdateNameTag();
}

void EditLabels::OnActionInputBindingUpdated() {
  for (arc::input_overlay::EditLabel* label : labels_) {
    label->OnActionInputBindingUpdated();
  }

  UpdateNameTag();
}

void EditLabels::SetNameTagState(bool is_error,
                                 const std::u16string& error_tooltip) {
  // If an individual label doesn't need to show error, but other sibling label
  // still has label unassigned, `name_tag_` still needs to show error.
  if (!is_error && missing_assign_) {
    name_tag_->SetState(
        /*is_error=*/!action_->is_new(),
        l10n_util::GetStringUTF16(IDS_INPUT_OVERLAY_EDIT_MISSING_BINDING));
  } else {
    name_tag_->SetState(is_error, error_tooltip);
  }
}

void EditLabels::FocusLabel() {
  // Clicking the edit labels with an already focused edit label causes the next
  // label to gain focus.
  for (size_t i = 0; i < labels_.size(); i++) {
    if (auto* label = labels_[i].get(); label->HasFocus()) {
      labels_[(i + 1) % labels_.size()]->RequestFocus();
      return;
    }
  }
  labels_[0]->RequestFocus();
}

std::u16string EditLabels::CalculateActionName() {
  std::u16string key_string = u"";
  // Check if all labels are unassigned. The prefix for the sub-title is
  // different if all labels are unassigned.
  bool all_unassigned = true;
  // If at least one label is unassigned, it needs to show error state.
  missing_assign_ = false;
  DCHECK_GE(labels_.size(), 1u);
  for (arc::input_overlay::EditLabel* label : labels_) {
    if (label->IsInputUnbound()) {
      missing_assign_ = true;
    } else {
      key_string.append(label->GetText());
      all_unassigned = false;
    }
  }

  int control_type_id = 0;
  switch (action_->GetType()) {
    case ActionType::TAP:
      control_type_id = IDS_INPUT_OVERLAY_BUTTON_TYPE_SINGLE_BUTTON_LABEL;
      break;
    case ActionType::MOVE:
      control_type_id = IDS_INPUT_OVERLAY_BUTTON_TYPE_JOYSTICK_BUTTON_LABEL;
      break;
    default:
      NOTREACHED();
  }

  if (all_unassigned) {
    return l10n_util::GetStringFUTF16(
        IDS_INPUT_OVERLAY_CONTROL_NAME_LABEL_UNASSIGNED_TEMPLATE,
        l10n_util::GetStringUTF16(control_type_id));
  }

  return l10n_util::GetStringFUTF16(
      IDS_INPUT_OVERLAY_CONTROL_NAME_LABEL_TEMPLATE,
      l10n_util::GetStringUTF16(control_type_id), key_string);
}

std::u16string EditLabels::CalculateKeyListForA11yLabel() const {
  std::vector<std::u16string> keys;
  for (EditLabel* label : labels_) {
    keys.push_back(
        GetDisplayTextAccessibleName(std::u16string(label->GetText())));
  }

  return base::JoinString(keys, u", ");
}

bool EditLabels::IsFirstLabelUnassigned() const {
  DCHECK_GE(labels_.size(), 1u);
  return labels_[0]->IsInputUnbound();
}

void EditLabels::PerformPulseAnimationOnFirstLabel() {
  DCHECK_GE(labels_.size(), 1u);
  labels_[0]->PerformPulseAnimation(/*pulse_count=*/0);
}

void EditLabels::InitForActionTapKeyboard() {
  SetUseDefaultFillLayout(true);
  labels_.emplace_back(AddChildView(
      std::make_unique<EditLabel>(controller_, action_, for_editing_list_)));
}

void EditLabels::InitForActionMoveKeyboard() {
  SetLayoutManager(std::make_unique<views::TableLayout>())
      ->AddColumn(/*h_align=*/views::LayoutAlignment::kCenter,
                  /*v_align=*/views::LayoutAlignment::kCenter,
                  /*horizontal_resize=*/1.0f,
                  /*size_type=*/views::TableLayout::ColumnSize::kUsePreferred,
                  /*fixed_width=*/0, /*min_width=*/0)
      .AddPaddingColumn(/*horizontal_resize=*/views::TableLayout::kFixedSize,
                        /*width=*/4)
      .AddColumn(/*h_align=*/views::LayoutAlignment::kCenter,
                 /*v_align=*/views::LayoutAlignment::kCenter,
                 /*horizontal_resize=*/1.0f,
                 /*size_type=*/views::TableLayout::ColumnSize::kUsePreferred,
                 /*fixed_width=*/0, /*min_width=*/0)
      .AddPaddingColumn(/*horizontal_resize=*/views::TableLayout::kFixedSize,
                        /*width=*/4)
      .AddColumn(/*h_align=*/views::LayoutAlignment::kCenter,
                 /*v_align=*/views::LayoutAlignment::kCenter,
                 /*horizontal_resize=*/1.0f,
                 /*size_type=*/views::TableLayout::ColumnSize::kUsePreferred,
                 /*fixed_width=*/0, /*min_width=*/0)
      .AddRows(1, /*vertical_resize=*/views::TableLayout::kFixedSize)
      .AddPaddingRow(/*vertical_resize=*/views::TableLayout::kFixedSize,
                     /*height=*/4)
      .AddRows(1, /*vertical_resize=*/views::TableLayout::kFixedSize);

  for (int i = 0; i < 6; i++) {
    // Column 1 row 1 and Column 3 row 1 are empty.
    if (i == 0 || i == 2) {
      AddChildView(std::make_unique<views::View>());
    } else {
      DCHECK_LT(labels_.size(), size_t(Direction::kMaxValue) + 1);
      labels_.emplace_back(AddChildView(std::make_unique<EditLabel>(
          controller_, action_, for_editing_list_, labels_.size())));
    }
  }
}

void EditLabels::UpdateNameTag() {
  // If at least one label is unassigned, it needs to show error state.
  missing_assign_ = false;
  DCHECK_GE(labels_.size(), 1u);
  for (arc::input_overlay::EditLabel* label : labels_) {
    if (label->IsInputUnbound()) {
      missing_assign_ = true;
      break;
    }
  }

  name_tag_->SetState(
      // The name tag is not set to be in an error state if it was newly
      // created.
      /*is_error=*/missing_assign_ && !action_->is_new(),
      missing_assign_
          ? l10n_util::GetStringUTF16(IDS_INPUT_OVERLAY_EDIT_MISSING_BINDING)
          : u"");

  if (for_editing_list_) {
    name_tag_->SetTitle(CalculateActionName());
  }
}

void EditLabels::RemoveNewState() {
  for (arc::input_overlay::EditLabel* label : labels_) {
    label->RemoveNewState();
  }

  UpdateNameTag();
}

BEGIN_METADATA(EditLabels)
END_METADATA

}  // namespace arc::input_overlay
