/*
 *  Copyright (c) 2008 Cyrille Berger <cberger@cberger.net>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation;
 * either version 2, or (at your option) any later version of the License.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this library; see the file COPYING.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 */

#ifndef _CAUCHYTEST_TEST_CASE_H_
#define _CAUCHYTEST_TEST_CASE_H_

#include <sstream>
#include <string>
#include <math.h>

/**
 * @internal
 * @ingroup CauchyTest
 * Check that a is true and display a message containing the value
 */
#define CAUCHYTEST_CHECK(a) \
  CAUCHYTEST_CHECK_MESSAGE( (a), #a);

/**
 * @internal
 * @ingroup CauchyTest
 * Check that a is true and display a message containing the value.
 * Stop the test if failed;
 */
#define CAUCHYTEST_CHECK_REQUIRED(a) \
  CAUCHYTEST_CHECK_MESSAGE_REQUIRED( (a), #a);

/**
 * @internal
 * @ingroup CauchyTest
 * This macro will test that a and b are strictly equal, and display a message containing the values
 * in case of failure.
 */
#define CAUCHYTEST_CHECK_EQUAL(a, b) \
  CAUCHYTEST_CHECK_MESSAGE( (a) == (b), (#a) << " == " << (#b) << " (" << (a) << ") == (" << (b) << ")" );
 
/**
 * @internal
 * @ingroup CauchyTest
 * This macro will test that a and b are not equal, and display a message containing the values
 * in case of failure.
 */
#define CAUCHYTEST_CHECK_NOT_EQUAL(a, b) \
  CAUCHYTEST_CHECK( (a) != (b) );

/**
 * @internal
 * @ingroup CauchyTest
 * This macro will test that a and b are near equal, and display a message containing the values
 * in case of failure.
 */
#define CAUCHYTEST_CHECK_NEAR_EQUAL(a, b) \
  CAUCHYTEST_CHECK_MESSAGE( fabs(a - b) < 10e-6, (#a) << " == " << (#b) << " " << (a) << " == " << (b) );

/**
 * @internal
 * @ingroup CauchyTest
 * This macro will display the message including the file and line number, in case of failure.
 */
#define CAUCHYTEST_CHECK_MESSAGE(a, msg) \
  { \
    std::stringstream ss; \
    std::string str; \
    ss << __FILE__ << " at " << __LINE__ << ": " << msg; \
    check( (a), ss.str()); \
  }

/**
 * @internal
 * @ingroup CauchyTest
 * This macro will display the message including the file and line number, in case of failure.
 */
#define CAUCHYTEST_CHECK_MESSAGE_REQUIRED(a, msg) \
  { \
    std::stringstream ss; \
    std::string str; \
    ss << __FILE__ << " at " << __LINE__ << ": " << msg; \
    bool t = (a); \
    check( t, ss.str()); \
    if(not t) \
      return; \
  }

namespace CauchyTest {
  class Suite;
  class Result;
  /**
   * @internal
   * @ingroup CauchyTest
   * A Case is a test of a functionality.
   * 
   */
  class Case {
    public:
      /**
       * Construct a test with the given name.
       */
      Case(const std::string& name);
      virtual ~Case();
      /**
       * run the test and fill the result object.
       */
      void run(Result*);
      /**
       * @return the name of the test
       */
      const std::string& name() const;
    protected:
      /**
       * Reimplement this in a subclass with the content of test.
       */
      virtual void runTest() = 0;
    protected:
      /**
       * You will want to use the macro (\ref CAUCHYTEST_CHECK , \ref CAUCHYTEST_CHECK_EQUAL ,
       * \ref CAUCHYTEST_CHECK_NOT_EQUAL , \ref CAUCHYTEST_CHECK_NEAR_EQUAL ,
       * \ref CAUCHYTEST_CHECK_MESSAGE ) instead of using directly this function.
       * 
       * @param _test if true increment the number of successfull test,
       *              if false increment the number of failed test and show the message
       * @param _message text message to display on the standard error
       */
      void check(bool _test, const std::string& _message);
      /**
       * @return result object associated with this test case
       */
      Result* result();
    private:
      struct Private;
      Private* const d;
  };
}

#endif
