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

#include "core/fxcrt/xml/cfx_xmlinstruction.h"

#include <memory>

#include "core/fxcrt/cfx_read_only_span_stream.h"
#include "core/fxcrt/xml/cfx_xmldocument.h"
#include "core/fxcrt/xml/cfx_xmlelement.h"
#include "core/fxcrt/xml/cfx_xmlparser.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/string_write_stream.h"

TEST(CFX_XMLInstructionTest, GetType) {
  CFX_XMLInstruction node(L"acrobat");
  EXPECT_EQ(CFX_XMLNode::Type::kInstruction, node.GetType());
}

TEST(CFX_XMLInstructionTest, AcrobatInstruction) {
  CFX_XMLInstruction node(L"acrobat");
  EXPECT_TRUE(node.IsAcrobat());
  EXPECT_FALSE(node.IsOriginalXFAVersion());
}

TEST(CFX_XMLInstructionTest, OriginalXFAInstruction) {
  CFX_XMLInstruction node(L"originalXFAVersion");
  EXPECT_TRUE(node.IsOriginalXFAVersion());
  EXPECT_FALSE(node.IsAcrobat());
}

TEST(CFX_XMLInstructionTest, TargetData) {
  CFX_XMLInstruction node(L"acrobat");
  EXPECT_EQ(0U, node.GetTargetData().size());

  node.AppendData(L"firstString");
  node.AppendData(L"secondString");

  auto& data = node.GetTargetData();
  ASSERT_EQ(2U, data.size());
  EXPECT_EQ(L"firstString", data[0]);
  EXPECT_EQ(L"secondString", data[1]);
}

TEST(CFX_XMLInstructionTest, Clone) {
  CFX_XMLDocument doc;

  CFX_XMLInstruction node(L"acrobat");
  node.AppendData(L"firstString");
  node.AppendData(L"secondString");

  CFX_XMLNode* clone = node.Clone(&doc);
  EXPECT_TRUE(clone != nullptr);

  ASSERT_EQ(CFX_XMLNode::Type::kInstruction, clone->GetType());
  CFX_XMLInstruction* inst = ToXMLInstruction(clone);
  EXPECT_TRUE(inst->IsAcrobat());

  auto& data = inst->GetTargetData();
  ASSERT_EQ(2U, data.size());
  EXPECT_EQ(L"firstString", data[0]);
  EXPECT_EQ(L"secondString", data[1]);
}

TEST(CFX_XMLInstructionTest, SaveXML) {
  auto stream = pdfium::MakeRetain<StringWriteStream>();
  CFX_XMLInstruction node(L"xml");
  node.Save(stream);
  EXPECT_EQ("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n", stream->ToString());
}

TEST(CFX_XMLInstructionTest, SaveAcrobat) {
  auto stream = pdfium::MakeRetain<StringWriteStream>();
  CFX_XMLInstruction node(L"acrobat");
  node.AppendData(L"http://www.xfa.org/schema/xfa-template/3.3/");
  node.AppendData(L"Display:1");

  node.Save(stream);
  EXPECT_EQ(
      "<?acrobat http://www.xfa.org/schema/xfa-template/3.3/ Display:1 ?>\n",
      stream->ToString());
}

TEST(CFX_XMLInstructionTest, ParseAndReSave) {
  static const char input[] =
      "<?acrobat http://www.xfa.org/schema/xfa-template/3.3/ Display:1 ?>\n"
      "<node></node>";

  auto in_stream =
      pdfium::MakeRetain<CFX_ReadOnlySpanStream>(pdfium::as_byte_span(input));

  CFX_XMLParser parser(in_stream);
  std::unique_ptr<CFX_XMLDocument> doc = parser.Parse();
  ASSERT_TRUE(doc != nullptr);

  CFX_XMLElement* root = doc->GetRoot();
  ASSERT_TRUE(root->GetFirstChild() != nullptr);
  ASSERT_EQ(CFX_XMLNode::Type::kInstruction, root->GetFirstChild()->GetType());

  CFX_XMLInstruction* node = ToXMLInstruction(root->GetFirstChild());
  ASSERT_TRUE(node != nullptr);
  EXPECT_TRUE(node->IsAcrobat());

  auto& data = node->GetTargetData();
  ASSERT_EQ(2U, data.size());
  EXPECT_EQ(L"http://www.xfa.org/schema/xfa-template/3.3/", data[0]);
  EXPECT_EQ(L"Display:1", data[1]);

  auto out_stream = pdfium::MakeRetain<StringWriteStream>();
  node->Save(out_stream);
  EXPECT_EQ(
      "<?acrobat http://www.xfa.org/schema/xfa-template/3.3/ Display:1 ?>\n",
      out_stream->ToString());
}

TEST(CFX_XMLInstructionTest, ParseAndReSaveInnerInstruction) {
  static const char input[] =
      "<node>\n"
      "<?acrobat http://www.xfa.org/schema/xfa-template/3.3/ Display:1 ?>\n"
      "</node>";

  auto in_stream =
      pdfium::MakeRetain<CFX_ReadOnlySpanStream>(pdfium::as_byte_span(input));

  CFX_XMLParser parser(in_stream);
  std::unique_ptr<CFX_XMLDocument> doc = parser.Parse();
  ASSERT_TRUE(doc != nullptr);

  CFX_XMLElement* root = doc->GetRoot();
  ASSERT_TRUE(root->GetFirstChild() != nullptr);
  ASSERT_TRUE(root->GetFirstChild()->GetType() == CFX_XMLNode::Type::kElement);

  CFX_XMLElement* node = ToXMLElement(root->GetFirstChild());
  EXPECT_EQ(L"node", node->GetName());

  CFX_XMLInstruction* instruction = nullptr;
  for (auto* elem = node->GetFirstChild(); elem && !instruction;
       elem = elem->GetNextSibling()) {
    instruction = ToXMLInstruction(elem);
  }
  ASSERT_TRUE(instruction != nullptr);
  EXPECT_TRUE(instruction->IsAcrobat());

  auto& data = instruction->GetTargetData();
  ASSERT_EQ(2U, data.size());
  EXPECT_EQ(L"http://www.xfa.org/schema/xfa-template/3.3/", data[0]);
  EXPECT_EQ(L"Display:1", data[1]);

  auto out_stream = pdfium::MakeRetain<StringWriteStream>();
  node->Save(out_stream);
  EXPECT_EQ(
      "<node>\n\n"
      "<?acrobat http://www.xfa.org/schema/xfa-template/3.3/ Display:1 ?>\n\n"
      "</node>\n",
      out_stream->ToString());
}
