/*
 * Author:          Patrick-Christopher Mattulat
 * Co-Author:       Claude Sonnet 4.6 (LLM)
 * Company:         Lynar Studios
 * E-Mail:          webmaster@lynarstudios.com
 * Created:         2020-08-09
 * Changed:         2026-06-23
 *
 * */

#include <gtest/gtest.h>
#include <ls-std/ls-std-boxing.hpp>
#include <ls-std/ls-std-core.hpp>

using ls::standard::boxing::Boolean;
using ls::standard::core::IllegalArgumentException;
using std::ostringstream;
using testing::Test;

namespace
{
  class BooleanTest : public Test
  {
    public:

      BooleanTest() = default;
      ~BooleanTest() override = default;
  };

  // assignment operators

  TEST_F(BooleanTest, operator_assignment_boolean)
  {
    Boolean expression{};
    expression = true;

    ASSERT_TRUE(expression.getValue());
  }

  TEST_F(BooleanTest, operator_assignment_integer)
  {
    Boolean expression{};
    expression = 1;

    ASSERT_TRUE(expression.getValue());
  }

  // stream operators

  TEST_F(BooleanTest, operator_output_stream)
  {
    Boolean expression{true};
    ostringstream _stream{};
    _stream << expression;

    ASSERT_STREQ("true", _stream.str().c_str());
  }

  // logical operators

  TEST_F(BooleanTest, operator_negation_negative_value)
  {
    const Boolean expression{};
    ASSERT_TRUE(!expression);
  }

  TEST_F(BooleanTest, operator_negation_positive_value)
  {
    const Boolean expression{true};
    ASSERT_FALSE(!expression);
  }

  TEST_F(BooleanTest, operator_and)
  {
    Boolean expressionA{true};
    Boolean expressionB{true};

    ASSERT_TRUE(expressionA && expressionB);
    ASSERT_TRUE(expressionB && expressionA);
    ASSERT_TRUE(expressionA && true);
    ASSERT_TRUE(expressionA && 1);
    ASSERT_TRUE(1 && expressionB.getValue());
  }

  TEST_F(BooleanTest, operator_and_with_false_result)
  {
    Boolean expressionA{true};
    Boolean expressionB{false};

    ASSERT_FALSE(expressionA && expressionB);
    ASSERT_FALSE(expressionB && expressionA);
  }

  TEST_F(BooleanTest, operator_or)
  {
    Boolean expressionA{false};
    Boolean expressionB{true};

    ASSERT_TRUE(expressionA || expressionB);
    ASSERT_TRUE(expressionB || expressionA);
    ASSERT_TRUE(1 || expressionA.getValue());
  }

  TEST_F(BooleanTest, operator_or_with_false_result)
  {
    Boolean expressionA{false};
    Boolean expressionB{false};

    ASSERT_FALSE(expressionA || expressionB);
    ASSERT_FALSE(expressionB || expressionA);
    ASSERT_FALSE(expressionA || 0);
    ASSERT_FALSE(expressionA || false);
    ASSERT_FALSE(0 || expressionA.getValue());
  }

  // implementation

  TEST_F(BooleanTest, parse_true_value)
  {
    Boolean expression{};

    expression.parse("true");
    ASSERT_TRUE(expression.getValue());

    expression.parse("tRue");
    ASSERT_TRUE(expression.getValue());

    expression.parse("TRUE");
    ASSERT_TRUE(expression.getValue());
  }

  TEST_F(BooleanTest, parse_false_value)
  {
    Boolean expression{};

    expression.parse("false");
    ASSERT_FALSE(expression);

    expression.parse("fAlSe");
    ASSERT_FALSE(expression);

    expression.parse("FALSE");
    ASSERT_FALSE(expression);
  }

  TEST_F(BooleanTest, parse_with_invalid_parameter)
  {
    EXPECT_THROW(
        {
          try
          {
            Boolean expression{};
            expression.parse("hello");
          }
          catch (const IllegalArgumentException &_exception)
          {
            throw;
          }
        },
        IllegalArgumentException);
  }

  TEST_F(BooleanTest, toString_true)
  {
    Boolean expression{2 < 3};
    ASSERT_STREQ("true", expression.toString().c_str());
  }

  TEST_F(BooleanTest, toString_false)
  {
    Boolean expression{4 < 3};
    ASSERT_STREQ("false", expression.toString().c_str());
  }

  // additional functionality

  TEST_F(BooleanTest, getValue)
  {
    const Boolean x{2 < 3};
    ASSERT_TRUE(x.getValue());
  }

  TEST_F(BooleanTest, XOR_with_positive_result)
  {
    Boolean x{2 < 3};
    Boolean y{4 < 3};

    ASSERT_TRUE(Boolean::XOR(x, y));
    ASSERT_TRUE(Boolean::XOR(y, x));
    ASSERT_TRUE(Boolean::XOR(y, true));
    ASSERT_TRUE(Boolean::XOR(true, y));
    ASSERT_TRUE(Boolean::XOR(true, false));
    ASSERT_TRUE(Boolean::XOR(false, true));
  }

  TEST_F(BooleanTest, XOR_with_negative_result)
  {
    Boolean w{};
    Boolean x{true};
    Boolean y{};
    Boolean z{true};

    ASSERT_FALSE(Boolean::XOR(x, z));
    ASSERT_FALSE(Boolean::XOR(w, y));
    ASSERT_FALSE(Boolean::XOR(true, true));
    ASSERT_FALSE(Boolean::XOR(false, false));
    ASSERT_FALSE(Boolean::XOR(w, false));
    ASSERT_FALSE(Boolean::XOR(false, w));
  }
}
