/*
 *
 *    soniK digital audio editor
 *    Copyright (C) 2003-2006  Robert Walker <rob@tenfoot.org.uk>
 *
 *    This program is free software; you can redistribute it and/or modify
 *    it under the terms of the GNU General Public License as published by
 *    the Free Software Foundation; either version 2 of the License, or
 *    (at your option) any later version.
 *
 *    This program 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 General Public License for more details.
 *
 *    You should have received a copy of the GNU General Public License
 *    along with this program; if not, write to the Free Software
 *    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 *
 */
#ifndef SONIK_TEST_H
#define SONIK_TEST_H

#include "sonik_autobuffer.h"

#include <qfileinfo.h>
#include <qtextstream.h>

#include <cmath>

#include <cppunit/TestAssert.h>

#define __POS QString(__FILE__) + ":" + QString::number(__LINE__)

/* inline bool _check(const char *pos, bool cond, const char *msg) */
/* { */
/*   if (!cond) */
/*   { */
/*     QTextOStream(stderr) << "FAIL(" << pos << "): " << msg << "\n"; */
/*     return false; */
/*   } */
/*   return true; */
/* } */

/* inline bool _check_r(const char *pos, bool cond, const char *msg) */
/* { */
/*   if (cond) */
/*   { */
/*     QTextOStream(stdout) << "PASS(" << pos << "): " << msg << "\n"; */
/*     return true; */
/*   } */
/*   else */
/*   { */
/*     QTextOStream(stderr) << "FAIL(" << pos << "): " << msg << "\n"; */
/*     return false; */
/*   } */
/* } */

/* template <typename T1, typename T2>  */
/* inline bool _check_op(const char *pos,  */
/*                       bool cond, T1 a, const char *op, T2 b, const char *msg) */
/* { */
/*   if (!cond) */
/*   { */
/*     QTextOStream(stderr) << "FAIL(" << pos << "): " << msg  */
/*                          << " (" << a << " " << op << " " << b << ")" */
/*                          << "\n"; */
/*     return false; */
/*   } */
/*   return true; */
/* } */

/* template <typename T1, typename T2>  */
/* inline bool _check_op_r(const char *pos,  */
/*                         bool cond, T1 a, const char *op, T2 b, const char *msg) */
/* { */
/*   if (cond) */
/*   { */
/*     QTextOStream(stdout) << "PASS(" << pos << "): " << msg */
/*                          << " (" << a << " " << op << " " << b << ")" */
/*                          << "\n"; */
/*     return true; */
/*   } */
/*   else */
/*   { */
/*     QTextOStream(stderr) << "FAIL(" << pos << "): " << msg */
/*                          << " (" << a << " " << op << " " << b << ")" */
/*                          << "\n"; */
/*     return false; */
/*   } */
/* } */

/* // */
/* // */
/* // Checking macros */
/* // */
/* // */

/* #define CHECK_MSG(_expr, _msg)    _check(__POS, (_expr), (_msg)) */
/* #define CHECK(_expr)              CHECK_MSG((_expr), (#_expr)) */
/* #define CHECK_MSG_R(_expr, _msg)  _check_r(__POS, (_expr), (_msg)) */
/* #define CHECK_R(_expr)            CHECK_MSG_R((_expr), (#_expr)) */

/* #define CHECK_OP_MSG(_a, _b, _OP, _msg)                                 \ */
/*   _check_op(__POS, ((_a) _OP (_b)), (_a), #_OP, (_b), _msg) */
/* #define CHECK_OP(_a, _b, _OP)   CHECK_OP_MSG((_a), (_b), _OP, #_a " " #_OP " " #_b) */

/* #define CHECK_EQ_MSG(a, b, m)   CHECK_OP_MSG(a, b, ==, m) */
/* #define CHECK_EQF_MSG(a, b, m)  CHECK_OP_MSG(std::fabs(a - b), 0.000001, <, m) */
/* #define CHECK_NE_MSG(a, b, m)   CHECK_OP_MSG(a, b, !=, m) */
/* #define CHECK_LT_MSG(a, b, m)   CHECK_OP_MSG(a, b, <,  m) */
/* #define CHECK_LTE_MSG(a, b, m)  CHECK_OP_MSG(a, b, <=, m) */
/* #define CHECK_GT_MSG(a, b, m)   CHECK_OP_MSG(a, b, >,  m) */
/* #define CHECK_GTE_MSG(a, b, m)  CHECK_OP_MSG(a, b, >=, m) */

/* #define CHECK_EQ(a, b)          CHECK_OP(a, b, ==) */
/* #define CHECK_EQF(a, b)         CHECK_OP(std::fabs(a - b), 0.000001, <) */
/* #define CHECK_NE(a, b)          CHECK_OP(a, b, !=) */
/* #define CHECK_LT(a, b)          CHECK_OP(a, b, <,  m) */
/* #define CHECK_LTE(a, b)         CHECK_OP(a, b, <=) */
/* #define CHECK_GT(a, b)          CHECK_OP(a, b, >,  m) */
/* #define CHECK_GTE(a, b)         CHECK_OP(a, b, >=) */

/* #define CHECK_OP_MSG_R(_a, _b, _OP, _msg)                                 \ */
/*   _check_op_r(__POS, ((_a) _OP (_b)), (_a), #_OP, (_b), _msg) */
/* #define CHECK_OP_R(_a, _b, _OP)   CHECK_OP_MSG_R((_a), (_b), _OP, #_a " " #_OP " " #_b) */

/* #define CHECK_EQ_MSG_R(a, b, m)   CHECK_OP_MSG_R(a, b, ==, m) */
/* #define CHECK_EQF_MSG_R(a, b, m)  CHECK_OP_MSG_R(std::fabs(a - b), 0.000001, <, m) */
/* #define CHECK_NE_MSG_R(a, b, m)   CHECK_OP_MSG_R(a, b, !=, m) */
/* #define CHECK_LT_MSG_R(a, b, m)   CHECK_OP_MSG_R(a, b, <,  m) */
/* #define CHECK_LTE_MSG_R(a, b, m)  CHECK_OP_MSG_R(a, b, <=, m) */
/* #define CHECK_GT_MSG_R(a, b, m)   CHECK_OP_MSG_R(a, b, >,  m) */
/* #define CHECK_GTE_MSG_R(a, b, m)  CHECK_OP_MSG_R(a, b, >=, m) */

/* #define CHECK_EQ_R(a, b)          CHECK_OP_R(a, b, ==) */
/* #define CHECK_EQF_R(a, b)         CHECK_OP_R(std::fabs(a - b), 0.000001, <) */
/* #define CHECK_NE_R(a, b)          CHECK_OP_R(a, b, !=) */
/* #define CHECK_LT_R(a, b)          CHECK_OP_R(a, b, <,  m) */
/* #define CHECK_LTE_R(a, b)         CHECK_OP_R(a, b, <=) */
/* #define CHECK_GT_R(a, b)          CHECK_OP_R(a, b, >,  m) */
/* #define CHECK_GTE_R(a, b)         CHECK_OP_R(a, b, >=) */

/* #define REPORT(ok, msg)           _check_r(__POS, ok, msg) */

//
//
// Numeric checking functions
//
//

static const double EPSILON = 0.0001;

template <typename T>
inline void _checkDataEq(const char *pos,
                  const T* buf1, size_t len1,
                  const T* buf2, size_t len2)
{
  CPPUNIT_ASSERT_EQUAL(len1, len2);

  for (size_t i = 0; i < len1; ++i)
  {
    if (buf1[i] != buf2[i])
    {
      std::ostringstream msg;
      msg << "FAIL(" << pos << "): buffer compare"
          << " (" << buf1[i] << " == " << buf2[i] << ")"
          << " at i = " << i
          << "\n";
      CPPUNIT_ASSERT_EQUAL_MESSAGE(msg.str().c_str(), buf1[i], buf2[i]);
    }
  }
}

template <>
inline void _checkDataEq(const char *pos,
                         const float* buf1, size_t len1,
                         const float* buf2, size_t len2)
{
  CPPUNIT_ASSERT_EQUAL(len1, len2);

  for (size_t i = 0; i < len1; ++i)
  {
    float a = buf1[i], b = buf2[i], e = EPSILON;
    if (b > 1.0)
      e = std::fabs(e * b); // scale EPSILON for large numbers
    if (a != b &&
        std::fabs(a - b) > e)
    {
      std::ostringstream msg;
      msg << "\n" << pos << ": buffer compare"
          << " (" << buf1[i] << " == " << buf2[i] << ")"
          << " at i = " << i
          << "\n";

      CPPUNIT_FAIL(msg.str().c_str());
    }
  }
}

template <typename T>
inline void _checkDataEq(const char *pos,
                  const Sonik::auto_buffer<T>& buf1,
                  const Sonik::auto_buffer<T>& buf2)
{
  _checkDataEq(pos, buf1.data(), buf1.size(), buf2.data(), buf2.size());
}

template <typename T>
inline void _checkDataEq(const char *pos,
                         const T* buf, size_t len, const T& c)
{
  for (size_t i = 0; i < len; ++i)
  {
    if (buf[i] != c)
    {
      std::ostringstream msg;
      msg << "FAIL(" << pos << "): buffer compare"
          << " (" << buf[i] << " == " << c << ")"
          << " at i = " << i
          << "\n";

      CPPUNIT_FAIL(msg.str().c_str());
    }
  }
}

template <typename T>
inline void _checkDataEq(const char *pos,
                  const Sonik::auto_buffer<T>& buf, const T& c)
{
  _checkDataEq(pos, buf.data(), buf.size(), c);
}

#define CHECK_BUFFER_EQ(_a, _b)   _checkDataEq(__POS, (_a), (_b))
#define CHECK_BUFFER_EQ_X(_a, _b, _l) _checkDataEq(__POS, (_a), (_l), (_b), (_l))

// //
// // dump functions
// //

// template <typename T>
// void _dump(const char *pos,
//            const T* buf, size_t len,
//            const char *msg)
// {
//   QTextOStream(stdout) << "DUMP(" << pos << "): " << msg << " ";

//   for (size_t i = 0; i < len-1; ++i)
//     QTextOStream(stdout) << buf[i] << ", ";

//   QTextOStream(stdout) << buf[len-1] << "\n";
// }

// template <typename T>
// void _dump(const char *pos,
//            const Sonik::auto_buffer<T>& buf,
//            const char *msg)
// {
//   _dump(pos, buf.data(), buf.size(), msg);
// }

// #define DUMP(_buf)           _dump(__POS, _buf, "")
// #define DUMP_MSG(_buf, _msg) _dump(__POS, _buf, _msg)

#endif // SONIK_TEST_H
