// Copyright (c) Glyn Matthews 2016.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
//  http://www.boost.org/LICENSE_1_0.txt)

#include <gtest/gtest.h>
#include <string>
#include <network/optional.hpp>
#include <network/string_view.hpp>

TEST(optional_test, empty_optional) {
  network::optional<int> opt;
  ASSERT_FALSE(opt);
}

TEST(optional_test, empty_optional_constructed_with_nullopt) {
  network::optional<int> opt{network::nullopt};
  ASSERT_FALSE(opt);
}

TEST(optional_test, empty_optional_string) {
  network::optional<std::string> opt{};
  ASSERT_FALSE(opt);
}

TEST(optional_test, empty_optional_string_with_nullopt) {
  network::optional<std::string> opt{network::nullopt};
  ASSERT_FALSE(opt);
}

TEST(optional_test, value_constructor) {
  network::optional<int> opt{42};
  ASSERT_TRUE(opt);
  ASSERT_EQ(*opt, 42);
}

TEST(optional_test, value_constructor_string) {
  network::optional<std::string> opt{"banana"};
  ASSERT_TRUE(opt);
  ASSERT_EQ(*opt, "banana");
}

TEST(optional_test, rvalue_ref_constructor) {
  int value = 42;
  network::optional<int> opt{std::move(value)};
  ASSERT_TRUE(opt);
  ASSERT_EQ(*opt, 42);
}

TEST(optional_test, rvalue_ref_constructor_string) {
  std::string value = "banana";
  network::optional<std::string> opt{std::move(value)};
  ASSERT_TRUE(opt);
  ASSERT_EQ(*opt, "banana");
}

TEST(optional_test, nullopt_copy_constructor) {
  network::optional<int> other{network::nullopt};
  network::optional<int> opt{other};
  ASSERT_FALSE(opt);
}

TEST(optional_test, nullopt_move_constructor) {
  network::optional<int> other{network::nullopt};
  network::optional<int> opt{std::move(other)};
  ASSERT_FALSE(opt);
}

TEST(optional_test, value_copy_constructor) {
  network::optional<int> other{42};
  network::optional<int> opt{other};
  ASSERT_TRUE(opt);
  ASSERT_EQ(*opt, 42);
}

TEST(optional_test, value_move_constructor) {
  network::optional<int> other{42};
  network::optional<int> opt{std::move(other)};
  ASSERT_TRUE(opt);
  ASSERT_EQ(*opt, 42);
}

TEST(optional_test, value_copy_constructor_string) {
  network::optional<std::string> other{"banana"};
  network::optional<std::string> opt{other};
  ASSERT_TRUE(opt);
  ASSERT_EQ(*opt, "banana");
}

TEST(optional_test, value_move_constructor_string) {
  network::optional<std::string> other{"banana"};
  network::optional<std::string> opt{std::move(other)};
  ASSERT_TRUE(opt);
  ASSERT_EQ(*opt, "banana");
}

TEST(optional_test, nullopt_assignment) {
  network::optional<int> opt(42);
  opt = network::nullopt;
  ASSERT_FALSE(opt);
}

TEST(optional_test, nullopt_assignment_string) {
  network::optional<std::string> opt("banana");
  opt = network::nullopt;
  ASSERT_FALSE(opt);
}

TEST(optional_test, value_copy_assigment) {
  network::optional<int> opt{};
  network::optional<int> other{42};
  opt = other;
  ASSERT_TRUE(opt);
  ASSERT_EQ(*opt, 42);
}

TEST(optional_test, value_move_assignment) {
  network::optional<int> opt{};
  network::optional<int> other{42};
  opt = std::move(other);
  ASSERT_TRUE(opt);
  ASSERT_EQ(*opt, 42);
}

TEST(optional_test, value_copy_assignment_string) {
  network::optional<std::string> opt{};
  network::optional<std::string> other{"banana"};
  opt = other;
  ASSERT_TRUE(opt);
  ASSERT_EQ(*opt, "banana");
}

TEST(optional_test, value_move_assignment_string) {
  network::optional<std::string> opt{};
  network::optional<std::string> other{"banana"};
  opt = std::move(other);
  ASSERT_TRUE(opt);
  ASSERT_EQ(*opt, "banana");
}

TEST(optional_test, value_or_reference) {
  network::optional<std::string> opt;
  auto result = opt.value_or("other");
  ASSERT_EQ("other", result);
}

TEST(optional_test, value_or_reference_with_value) {
  network::optional<std::string> opt("this");
  auto result = opt.value_or("other");
  ASSERT_EQ("this", result);
}

TEST(optional_test, value_or_rvalue_reference) {
  std::string other("other");
  auto result = network::optional<std::string>().value_or(other);
  ASSERT_EQ("other", result);
}

TEST(optional_test, value_or_rvalue_reference_with_value) {
  std::string other("other");
  auto result = network::optional<std::string>("this").value_or(other);
  ASSERT_EQ("this", result);
}

TEST(optional_test, assign_nullopt_to_nullopt) {
        network::optional<std::string> opt;
        opt = network::nullopt;
}