// Copyright (c) 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <stddef.h>

#include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h"
#include "content/browser/accessibility/accessibility_tree_formatter_blink.h"

namespace content {

AccessibilityTreeFormatterBlink::AccessibilityTreeFormatterBlink()
    : AccessibilityTreeFormatter() {
}

AccessibilityTreeFormatterBlink::~AccessibilityTreeFormatterBlink() {
}

uint32_t AccessibilityTreeFormatterBlink::ChildCount(
    const BrowserAccessibility& node) const {
  return node.InternalChildCount();
}

BrowserAccessibility* AccessibilityTreeFormatterBlink::GetChild(
    const BrowserAccessibility& node,
    uint32_t i) const {
  return node.InternalGetChild(i);
}

void AccessibilityTreeFormatterBlink::AddProperties(
    const BrowserAccessibility& node,
    base::DictionaryValue* dict) {
  dict->SetInteger("id", node.GetId());

  dict->SetString("role", ui::ToString(node.GetData().role));

  dict->SetInteger("boundsX", node.GetData().location.x());
  dict->SetInteger("boundsY", node.GetData().location.y());
  dict->SetInteger("boundsWidth", node.GetData().location.width());
  dict->SetInteger("boundsHeight", node.GetData().location.height());

  for (int state_index = ui::AX_STATE_NONE;
       state_index <= ui::AX_STATE_LAST;
       ++state_index) {
    auto state = static_cast<ui::AXState>(state_index);
    if (node.HasState(state))
      dict->SetBoolean(ui::ToString(state), true);
  }

  for (int attr_index = ui::AX_STRING_ATTRIBUTE_NONE;
       attr_index <= ui::AX_STRING_ATTRIBUTE_LAST;
       ++attr_index) {
    auto attr = static_cast<ui::AXStringAttribute>(attr_index);
    if (node.HasStringAttribute(attr))
      dict->SetString(ui::ToString(attr), node.GetStringAttribute(attr));
  }

  for (int attr_index = ui::AX_INT_ATTRIBUTE_NONE;
       attr_index <= ui::AX_INT_ATTRIBUTE_LAST;
       ++attr_index) {
    auto attr = static_cast<ui::AXIntAttribute>(attr_index);
    if (node.HasIntAttribute(attr))
      dict->SetInteger(ui::ToString(attr), node.GetIntAttribute(attr));
  }

  for (int attr_index = ui::AX_FLOAT_ATTRIBUTE_NONE;
       attr_index <= ui::AX_FLOAT_ATTRIBUTE_LAST;
       ++attr_index) {
    auto attr = static_cast<ui::AXFloatAttribute>(attr_index);
    if (node.HasFloatAttribute(attr))
      dict->SetDouble(ui::ToString(attr), node.GetFloatAttribute(attr));
  }

  for (int attr_index = ui::AX_BOOL_ATTRIBUTE_NONE;
       attr_index <= ui::AX_BOOL_ATTRIBUTE_LAST;
       ++attr_index) {
    auto attr = static_cast<ui::AXBoolAttribute>(attr_index);
    if (node.HasBoolAttribute(attr))
      dict->SetBoolean(ui::ToString(attr), node.GetBoolAttribute(attr));
  }

  for (int attr_index = ui::AX_INT_LIST_ATTRIBUTE_NONE;
       attr_index <= ui::AX_INT_LIST_ATTRIBUTE_LAST;
       ++attr_index) {
    auto attr = static_cast<ui::AXIntListAttribute>(attr_index);
    if (node.HasIntListAttribute(attr)) {
      std::vector<int32_t> values;
      node.GetIntListAttribute(attr, &values);
      base::ListValue* value_list = new base::ListValue;
      for (size_t i = 0; i < values.size(); ++i)
        value_list->AppendInteger(values[i]);
      dict->Set(ui::ToString(attr), value_list);
    }
  }
}

base::string16 AccessibilityTreeFormatterBlink::ToString(
    const base::DictionaryValue& dict) {
  base::string16 line;

  if (show_ids()) {
    int id_value;
    dict.GetInteger("id", &id_value);
    WriteAttribute(true, base::IntToString16(id_value), &line);
  }

  base::string16 role_value;
  dict.GetString("role", &role_value);
  WriteAttribute(true, base::UTF16ToUTF8(role_value), &line);

  for (int state_index = ui::AX_STATE_NONE;
       state_index <= ui::AX_STATE_LAST;
       ++state_index) {
    auto state = static_cast<ui::AXState>(state_index);
    const base::Value* value;
    if (!dict.Get(ui::ToString(state), &value))
      continue;

    WriteAttribute(false, ui::ToString(state), &line);
  }

  WriteAttribute(false,
                 FormatCoordinates("location", "boundsX", "boundsY", dict),
                 &line);
  WriteAttribute(false,
                 FormatCoordinates("size", "boundsWidth", "boundsHeight", dict),
                 &line);

  for (int attr_index = ui::AX_STRING_ATTRIBUTE_NONE;
       attr_index <= ui::AX_STRING_ATTRIBUTE_LAST;
       ++attr_index) {
    auto attr = static_cast<ui::AXStringAttribute>(attr_index);
    std::string string_value;
    if (!dict.GetString(ui::ToString(attr), &string_value))
      continue;
    WriteAttribute(false,
                   base::StringPrintf(
                       "%s='%s'",
                       ui::ToString(attr).c_str(),
                       string_value.c_str()),
                   &line);
  }

  for (int attr_index = ui::AX_INT_ATTRIBUTE_NONE;
       attr_index <= ui::AX_INT_ATTRIBUTE_LAST;
       ++attr_index) {
    auto attr = static_cast<ui::AXIntAttribute>(attr_index);
    int int_value;
    if (!dict.GetInteger(ui::ToString(attr), &int_value))
      continue;
    WriteAttribute(false,
                   base::StringPrintf(
                       "%s=%d",
                       ui::ToString(attr).c_str(),
                       int_value),
                   &line);
  }

  for (int attr_index = ui::AX_BOOL_ATTRIBUTE_NONE;
       attr_index <= ui::AX_BOOL_ATTRIBUTE_LAST;
       ++attr_index) {
    auto attr = static_cast<ui::AXBoolAttribute>(attr_index);
    bool bool_value;
    if (!dict.GetBoolean(ui::ToString(attr), &bool_value))
      continue;
    WriteAttribute(false,
                   base::StringPrintf(
                       "%s=%s",
                       ui::ToString(attr).c_str(),
                       bool_value ? "true" : "false"),
                   &line);
  }

  for (int attr_index = ui::AX_INT_LIST_ATTRIBUTE_NONE;
       attr_index <= ui::AX_INT_LIST_ATTRIBUTE_LAST;
       ++attr_index) {
    auto attr = static_cast<ui::AXIntListAttribute>(attr_index);
    const base::ListValue* value;
    if (!dict.GetList(ui::ToString(attr), &value))
      continue;
    std::string attr_string = ui::ToString(attr) + "=";
    for (size_t i = 0; i < value->GetSize(); ++i) {
      int int_value;
      value->GetInteger(i, &int_value);
      if (i > 0)
        attr_string += ",";
      attr_string += base::IntToString(int_value);
    }
    WriteAttribute(false, attr_string, &line);
  }

  return line;
}

const base::FilePath::StringType
AccessibilityTreeFormatterBlink::GetExpectedFileSuffix() {
  return FILE_PATH_LITERAL("-expected-blink.txt");
}

const std::string AccessibilityTreeFormatterBlink::GetAllowEmptyString() {
  return "@BLINK-ALLOW-EMPTY:";
}

const std::string AccessibilityTreeFormatterBlink::GetAllowString() {
  return "@BLINK-ALLOW:";
}

const std::string AccessibilityTreeFormatterBlink::GetDenyString() {
  return "@BLINK-DENY:";
}

}  // namespace content
