/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <folly/File.h>

#include <string>
#include <vector>

#include <folly/Range.h>
#include <folly/container/Array.h>
#include <folly/gen/Base.h>
#include <folly/gen/File.h>
#include <folly/portability/GTest.h>
#include <folly/testing/TestUtil.h>

using namespace folly::gen;
using namespace folly;
using std::vector;

TEST(FileGen, ByLine) {
  auto collect = eachTo<std::string>() | as<vector>();
  const std::string cases[] = {
      "Hello world\n"
      "This is the second line\n"
      "\n"
      "\n"
      "a few empty lines above\n"
      "incomplete last line",

      "complete last line\n",

      "\n",

      "",
  };

  for (auto& lines : cases) {
    test::TemporaryFile file("ByLine");
    EXPECT_EQ(lines.size(), write(file.fd(), lines.data(), lines.size()));

    auto expected = from({lines}) | resplit('\n') | collect;
    auto found = byLine(file.path().string().c_str()) | collect;

    EXPECT_EQ(expected, found) << "For Input: '" << lines << "'";
  }
}

TEST(FileGen, ByLineFull) {
  auto cases = std::vector<std::string>{
      stripLeftMargin(R"(
         Hello world
         This is the second line


         a few empty lines above
         incomplete last line)"),

      "complete last line\n",

      "\n",

      "",
  };

  for (auto& lines : cases) {
    test::TemporaryFile file("ByLineFull");
    EXPECT_EQ(lines.size(), write(file.fd(), lines.data(), lines.size()));

    auto found =
        byLineFull(file.path().string().c_str()) | unsplit<std::string>("");

    EXPECT_EQ(lines, found);
  }
}

class FileGenBufferedTest : public ::testing::TestWithParam<int> {};

TEST_P(FileGenBufferedTest, FileWriter) {
  size_t bufferSize = GetParam();
  test::TemporaryFile file("FileWriter");

  static const std::string lines(
      "Hello world\n"
      "This is the second line\n"
      "\n"
      "\n"
      "a few empty lines above\n");

  auto src = from({lines, lines, lines, lines, lines, lines, lines, lines});
  auto collect = eachTo<std::string>() | as<vector>();
  auto expected = src | resplit('\n') | collect;

  src | eachAs<StringPiece>() | toFile(File(file.fd()), bufferSize);
  auto found = byLine(file.path().string().c_str()) | collect;

  EXPECT_TRUE(expected == found);
}

TEST(FileGenBufferedTest, FileWriterSimple) {
  test::TemporaryFile file("FileWriter");
  auto toLine = [](int v) { return to<std::string>(v, '\n'); };

  auto squares = seq(1, 100) | map([](int x) { return x * x; });
  squares | map(toLine) | eachAs<StringPiece>() | toFile(File(file.fd()));
  EXPECT_EQ(
      squares | sum,
      byLine(File(file.path().string().c_str())) | eachTo<int>() | sum);
}

INSTANTIATE_TEST_SUITE_P(
    DifferentBufferSizes,
    FileGenBufferedTest,
    ::testing::Values(0, 1, 2, 4, 8, 64, 4096));
