/*
 *
 *    soniK digital audio editor
 *    Copyright (C) 2004-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
 *
 */

#include "sonik_autobuffer.h"

#include <cppunit/extensions/HelperMacros.h>

#include "test.h"

typedef Sonik::auto_buffer<int> IntBuffer;
typedef Sonik::auto_buffer_2d<int> IntBuffer2D;


namespace Sonik
{
  class AutobufferTest : public CppUnit::TestFixture
  {
    CPPUNIT_TEST_SUITE( AutobufferTest );
    CPPUNIT_TEST( testAutobuffer );
    CPPUNIT_TEST( testAutobuffer2d );
    CPPUNIT_TEST_SUITE_END();

  public:

    void testAutobuffer();
    void testAutobuffer2d();
  };
}

CPPUNIT_TEST_SUITE_REGISTRATION(Sonik::AutobufferTest);

size_t f_ref(IntBuffer& a)
{
  a[0] = 1234;
  return a.size();
}

size_t f_const_ref(const IntBuffer& a)
{
  return a.size();
}

size_t f_val(IntBuffer a)
{
  a[0] = 1234;
  return a.size();
}

size_t f_const_val(const IntBuffer a)
{
  return a.size();
}

size_t f_ref_2d(IntBuffer2D& a)
{
  a[0][0] = 1234;
  a[1][0] = 5678;
  return a.size();
}

size_t f_const_ref_2d(const IntBuffer2D& a)
{
  return a.size();
}

size_t f_val_2d(IntBuffer2D a)
{
  a[0][0] = 1234;
  return a.size();
}

size_t f_const_val_2d(const IntBuffer2D a)
{
  return a.size();
}

IntBuffer makeBuffer(size_t s)
{
  IntBuffer r(s);
  return r;
}

void Sonik::AutobufferTest::testAutobuffer()
{
  {
    IntBuffer a;
    CPPUNIT_ASSERT_EQUAL(a.size(), 0u);
    CPPUNIT_ASSERT_EQUAL(a.capacity(), 0u);
    CPPUNIT_ASSERT_EQUAL(a.data(), (int *)0);
  }

  {
    IntBuffer a(10);
    CPPUNIT_ASSERT_EQUAL(a.size(), 10u);
    CPPUNIT_ASSERT_EQUAL(a.capacity(), 10u);
    CPPUNIT_ASSERT(a.data() != 0);
  }

  {
    IntBuffer a(new int[10], 10);
    CPPUNIT_ASSERT_EQUAL(a.size(), 10u);
    CPPUNIT_ASSERT_EQUAL(a.capacity(), 10u);
    CPPUNIT_ASSERT(a.data() != 0);

    IntBuffer b(new int[20], 10, 20);
    CPPUNIT_ASSERT_EQUAL(b.size(), 10u);
    CPPUNIT_ASSERT_EQUAL(b.capacity(), 20u);
    CPPUNIT_ASSERT(b.data() != 0);
  }

  {
    int* buf = new int[10];
    IntBuffer a(buf, 10);
    CPPUNIT_ASSERT_EQUAL(a.size(), 10u);
    CPPUNIT_ASSERT_EQUAL(a.capacity(), 10u);
    CPPUNIT_ASSERT_EQUAL(a.data(), buf);
  }

  {
    IntBuffer a(10);
    CPPUNIT_ASSERT_EQUAL(a.size(), 10u);
    CPPUNIT_ASSERT_EQUAL(a.capacity(), 10u);
    CPPUNIT_ASSERT(a.data() != 0);
    int* p = a.data();
    IntBuffer b(a);
    CPPUNIT_ASSERT_EQUAL(a.size(), 0u);
    CPPUNIT_ASSERT_EQUAL(a.capacity(), 0u);
    CPPUNIT_ASSERT_EQUAL(a.data(), (int *)0);
    CPPUNIT_ASSERT_EQUAL(b.size(), 10u);
    CPPUNIT_ASSERT_EQUAL(b.capacity(), 10u);
    CPPUNIT_ASSERT_EQUAL(b.data(), p);
  }

  {
    IntBuffer a(10);
    CPPUNIT_ASSERT_EQUAL(a.size(), 10u);
    CPPUNIT_ASSERT_EQUAL(a.capacity(), 10u);
    CPPUNIT_ASSERT(a.data() != 0);
    int* p = a.data();
    IntBuffer b = a;
    CPPUNIT_ASSERT_EQUAL(a.size(), 0u);
    CPPUNIT_ASSERT_EQUAL(a.capacity(), 0u);
    CPPUNIT_ASSERT_EQUAL(a.data(), (int *)0);
    CPPUNIT_ASSERT_EQUAL(b.size(), 10u);
    CPPUNIT_ASSERT_EQUAL(b.capacity(), 10u);
    CPPUNIT_ASSERT_EQUAL(b.data(), p);
  }

  {
    int buf[10];
    IntBuffer a(buf, 10);
    int* p = a.release();
    CPPUNIT_ASSERT_EQUAL(a.size(), 0u);
    CPPUNIT_ASSERT_EQUAL(a.capacity(), 0u);
    CPPUNIT_ASSERT_EQUAL(a.data(), (int *)0);
    CPPUNIT_ASSERT_EQUAL(p, (int *)buf);
  }

  {
    IntBuffer a(10);
    CPPUNIT_ASSERT_EQUAL(a.size(), 10u);
    CPPUNIT_ASSERT_EQUAL(a.capacity(), 10u);
    CPPUNIT_ASSERT(a.data() != 0);
    int *p = a.data();
    a.resize(5);
    CPPUNIT_ASSERT_EQUAL(a.size(), 5u);
    CPPUNIT_ASSERT_EQUAL(a.capacity(), 10u);
    CPPUNIT_ASSERT_EQUAL(a.data(), p);
    a.resize(8);
    CPPUNIT_ASSERT_EQUAL(a.size(), 8u);
    CPPUNIT_ASSERT_EQUAL(a.capacity(), 10u);
    CPPUNIT_ASSERT_EQUAL(a.data(), p);
  }

  {
    IntBuffer a(10);
    CPPUNIT_ASSERT_EQUAL(a.size(), 10u);
    CPPUNIT_ASSERT_EQUAL(a.capacity(), 10u);
    CPPUNIT_ASSERT(a.data() != 0);
    int* p = a.data();
    a.reset(15);
    CPPUNIT_ASSERT_EQUAL(a.size(), 15u);
    CPPUNIT_ASSERT_EQUAL(a.capacity(), 15u);
    CPPUNIT_ASSERT(a.data() != p);
  }

  {
    IntBuffer a(10);
    CPPUNIT_ASSERT_EQUAL(a.size(), 10u);
    CPPUNIT_ASSERT_EQUAL(a.capacity(), 10u);
    CPPUNIT_ASSERT(a.data() != 0);
    int* p = a.data();
    a.reset(new int[15], 15);
    CPPUNIT_ASSERT_EQUAL(a.size(), 15u);
    CPPUNIT_ASSERT_EQUAL(a.capacity(), 15u);
    CPPUNIT_ASSERT(a.data() != p);
    a.reset(new int[30], 15, 30);
    CPPUNIT_ASSERT_EQUAL(a.size(), 15u);
    CPPUNIT_ASSERT_EQUAL(a.capacity(), 30u);
    CPPUNIT_ASSERT(a.data() != p);
  }

  {
    IntBuffer a(10);
    CPPUNIT_ASSERT_EQUAL(a.size(), 10u);
    CPPUNIT_ASSERT_EQUAL(a.capacity(), 10u);
    CPPUNIT_ASSERT(a.data() != 0);
    int* p = a.data();
    IntBuffer& b(a);
    CPPUNIT_ASSERT_EQUAL(a.size(), 10u);
    CPPUNIT_ASSERT_EQUAL(a.capacity(), 10u);
    CPPUNIT_ASSERT_EQUAL(a.data(), p);
    CPPUNIT_ASSERT_EQUAL(b.size(), 10u);
    CPPUNIT_ASSERT_EQUAL(b.capacity(), 10u);
    CPPUNIT_ASSERT_EQUAL(b.data(), p);
    IntBuffer& c = a;
    CPPUNIT_ASSERT_EQUAL(a.size(), 10u);
    CPPUNIT_ASSERT_EQUAL(a.capacity(), 10u);
    CPPUNIT_ASSERT_EQUAL(a.data(), p);
    CPPUNIT_ASSERT_EQUAL(c.size(), 10u);
    CPPUNIT_ASSERT_EQUAL(c.capacity(), 10u);
    CPPUNIT_ASSERT_EQUAL(c.data(), p);
    c.resize(5);
    CPPUNIT_ASSERT_EQUAL(a.size(), 5u);
    CPPUNIT_ASSERT_EQUAL(c.size(), 5u);
  }

  {
    int buf[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
    IntBuffer a(buf, 10);
    int i;

    for (i = 0; i < 10; ++i)
      CPPUNIT_ASSERT_EQUAL(a[i], i);

    for (i = 0; i < 10; ++i)
      a[i] = 100 + i;
    for (i = 0; i < 10; ++i)
      CPPUNIT_ASSERT_EQUAL(a[i], 100+i);

    a.release();
  }

  {
    IntBuffer a(10);
    int *p = a.data();

    size_t s = f_ref(a);
    CPPUNIT_ASSERT_EQUAL(s, 10u);
    CPPUNIT_ASSERT_EQUAL(a.size(), 10u);
    CPPUNIT_ASSERT_EQUAL(a.data(), p);
    CPPUNIT_ASSERT_EQUAL(a[0], 1234);

    s = f_const_ref(a);
    CPPUNIT_ASSERT_EQUAL(s, 10u);
    CPPUNIT_ASSERT_EQUAL(a.size(), 10u);
    CPPUNIT_ASSERT_EQUAL(a.data(), p);

    s = f_val(a);
    CPPUNIT_ASSERT_EQUAL(s, 10u);
    CPPUNIT_ASSERT_EQUAL(a.size(), 0u);
    CPPUNIT_ASSERT_EQUAL(a.data(), (int *)0);
    a.reset(10);

    s = f_const_val(a);
    CPPUNIT_ASSERT_EQUAL(s, 10u);
    CPPUNIT_ASSERT_EQUAL(a.size(), 0u);
    CPPUNIT_ASSERT_EQUAL(a.data(), (int *)0);
  }
}

void Sonik::AutobufferTest::testAutobuffer2d()
{
  {
    IntBuffer2D a;
    CPPUNIT_ASSERT_EQUAL(a.rows(), 0u);
    CPPUNIT_ASSERT_EQUAL(a.size(), 0u);
    CPPUNIT_ASSERT_EQUAL(a.capacity(), 0u);
    CPPUNIT_ASSERT_EQUAL(a.data(0), (int *)0);
  }

  {
    IntBuffer2D a(2, 10);
    CPPUNIT_ASSERT_EQUAL(a.rows(), 2u);
    CPPUNIT_ASSERT_EQUAL(a.size(), 10u);
    CPPUNIT_ASSERT_EQUAL(a.capacity(), 10u);
    CPPUNIT_ASSERT(a.data(0) != 0);
  }

  {
    IntBuffer2D a(new int[2*10], 2, 10);
    CPPUNIT_ASSERT_EQUAL(a.rows(), 2u);
    CPPUNIT_ASSERT_EQUAL(a.size(), 10u);
    CPPUNIT_ASSERT_EQUAL(a.capacity(), 10u);
    CPPUNIT_ASSERT(a.data(0) != 0);

    IntBuffer2D b(new int[4*20], 4, 10, 20);
    CPPUNIT_ASSERT_EQUAL(b.rows(), 4u);
    CPPUNIT_ASSERT_EQUAL(b.size(), 10u);
    CPPUNIT_ASSERT_EQUAL(b.capacity(), 20u);
    CPPUNIT_ASSERT(b.data(0) != 0);
  }

  {
    int* buf = new int[20];
    IntBuffer2D a(buf, 2, 10);
    CPPUNIT_ASSERT_EQUAL(a.rows(), 2u);
    CPPUNIT_ASSERT_EQUAL(a.size(), 10u);
    CPPUNIT_ASSERT_EQUAL(a.capacity(), 10u);
    CPPUNIT_ASSERT_EQUAL(a.data(0), buf);
    CPPUNIT_ASSERT_EQUAL(a.data(1), buf+10);
  }

  {
    IntBuffer2D a(2, 10);
    CPPUNIT_ASSERT_EQUAL(a.rows(), 2u);
    CPPUNIT_ASSERT_EQUAL(a.size(), 10u);
    CPPUNIT_ASSERT_EQUAL(a.capacity(), 10u);
    CPPUNIT_ASSERT(a.data(0) != 0);
    int* p = a.data(0);
    IntBuffer2D b(a);
    CPPUNIT_ASSERT_EQUAL(a.rows(), 0u);
    CPPUNIT_ASSERT_EQUAL(a.size(), 0u);
    CPPUNIT_ASSERT_EQUAL(a.capacity(), 0u);
    CPPUNIT_ASSERT_EQUAL(a.data(0), (int *)0);
    CPPUNIT_ASSERT_EQUAL(b.rows(), 2u);
    CPPUNIT_ASSERT_EQUAL(b.size(), 10u);
    CPPUNIT_ASSERT_EQUAL(b.capacity(), 10u);
    CPPUNIT_ASSERT_EQUAL(b.data(0), p);
  }

  {
    IntBuffer2D a(2, 10);
    CPPUNIT_ASSERT_EQUAL(a.rows(), 2u);
    CPPUNIT_ASSERT_EQUAL(a.size(), 10u);
    CPPUNIT_ASSERT_EQUAL(a.capacity(), 10u);
    CPPUNIT_ASSERT(a.data(0) != 0);
    int* p = a.data(0);
    IntBuffer2D b = a;
    CPPUNIT_ASSERT_EQUAL(a.rows(), 0u);
    CPPUNIT_ASSERT_EQUAL(a.size(), 0u);
    CPPUNIT_ASSERT_EQUAL(a.capacity(), 0u);
    CPPUNIT_ASSERT_EQUAL(a.data(0), (int *)0);
    CPPUNIT_ASSERT_EQUAL(b.rows(), 2u);
    CPPUNIT_ASSERT_EQUAL(b.size(), 10u);
    CPPUNIT_ASSERT_EQUAL(b.capacity(), 10u);
    CPPUNIT_ASSERT_EQUAL(b.data(0), p);
  }

  {
    int buf[20];
    IntBuffer2D a(buf, 2, 10);
    int* p = a.release();
    CPPUNIT_ASSERT_EQUAL(a.rows(), 0u);
    CPPUNIT_ASSERT_EQUAL(a.size(), 0u);
    CPPUNIT_ASSERT_EQUAL(a.capacity(), 0u);
    CPPUNIT_ASSERT_EQUAL(a.data(0), (int *)0);
    CPPUNIT_ASSERT_EQUAL(p, (int *)buf);
  }

  {
    IntBuffer2D a(2, 10);
    CPPUNIT_ASSERT_EQUAL(a.rows(), 2u);
    CPPUNIT_ASSERT_EQUAL(a.size(), 10u);
    CPPUNIT_ASSERT_EQUAL(a.capacity(), 10u);
    CPPUNIT_ASSERT(a.data(0) != 0);
    int *p = a.data(0);
    a.resize(5);
    CPPUNIT_ASSERT_EQUAL(a.rows(), 2u);
    CPPUNIT_ASSERT_EQUAL(a.size(), 5u);
    CPPUNIT_ASSERT_EQUAL(a.capacity(), 10u);
    CPPUNIT_ASSERT_EQUAL(a.data(0), p);
    CPPUNIT_ASSERT_EQUAL(a.data(1), p+10);
    a.resize(8);
    CPPUNIT_ASSERT_EQUAL(a.rows(), 2u);
    CPPUNIT_ASSERT_EQUAL(a.size(), 8u);
    CPPUNIT_ASSERT_EQUAL(a.capacity(), 10u);
    CPPUNIT_ASSERT_EQUAL(a.data(0), p);
    CPPUNIT_ASSERT_EQUAL(a.data(1), p+10);
  }

  {
    IntBuffer2D a(2, 10);
    CPPUNIT_ASSERT_EQUAL(a.rows(), 2u);
    CPPUNIT_ASSERT_EQUAL(a.size(), 10u);
    CPPUNIT_ASSERT_EQUAL(a.capacity(), 10u);
    CPPUNIT_ASSERT(a.data(0) != 0);
    int* p = a.data(0);
    a.reset(4, 15);
    CPPUNIT_ASSERT_EQUAL(a.rows(), 4u);
    CPPUNIT_ASSERT_EQUAL(a.size(), 15u);
    CPPUNIT_ASSERT_EQUAL(a.capacity(), 15u);
    CPPUNIT_ASSERT(a.data(0) != p);
  }

  {
    IntBuffer2D a(2, 10);
    CPPUNIT_ASSERT_EQUAL(a.rows(), 2u);
    CPPUNIT_ASSERT_EQUAL(a.size(), 10u);
    CPPUNIT_ASSERT_EQUAL(a.capacity(), 10u);
    CPPUNIT_ASSERT(a.data(0) != 0);
    int* p = a.data(0);
    a.reset(new int[4*15], 4, 15);
    CPPUNIT_ASSERT_EQUAL(a.rows(), 4u);
    CPPUNIT_ASSERT_EQUAL(a.size(), 15u);
    CPPUNIT_ASSERT_EQUAL(a.capacity(), 15u);
    CPPUNIT_ASSERT(a.data(0) != p);
    a.reset(new int[4*30], 4, 15, 30);
    CPPUNIT_ASSERT_EQUAL(a.rows(), 4u);
    CPPUNIT_ASSERT_EQUAL(a.size(), 15u);
    CPPUNIT_ASSERT_EQUAL(a.capacity(), 30u);
    CPPUNIT_ASSERT(a.data(0) != p);
  }

  {
    IntBuffer2D a(2, 10);
    CPPUNIT_ASSERT_EQUAL(a.rows(), 2u);
    CPPUNIT_ASSERT_EQUAL(a.size(), 10u);
    CPPUNIT_ASSERT_EQUAL(a.capacity(), 10u);
    CPPUNIT_ASSERT(a.data(0) != 0);
    int* p = a.data(0);
    IntBuffer2D& b(a);
    CPPUNIT_ASSERT_EQUAL(a.rows(), 2u);
    CPPUNIT_ASSERT_EQUAL(a.size(), 10u);
    CPPUNIT_ASSERT_EQUAL(a.capacity(), 10u);
    CPPUNIT_ASSERT_EQUAL(a.data(0), p);
    CPPUNIT_ASSERT_EQUAL(b.rows(), 2u);
    CPPUNIT_ASSERT_EQUAL(b.size(), 10u);
    CPPUNIT_ASSERT_EQUAL(b.capacity(), 10u);
    CPPUNIT_ASSERT_EQUAL(b.data(0), p);
    IntBuffer2D& c = a;
    CPPUNIT_ASSERT_EQUAL(a.rows(), 2u);
    CPPUNIT_ASSERT_EQUAL(a.size(), 10u);
    CPPUNIT_ASSERT_EQUAL(a.capacity(), 10u);
    CPPUNIT_ASSERT_EQUAL(a.data(0), p);
    CPPUNIT_ASSERT_EQUAL(c.rows(), 2u);
    CPPUNIT_ASSERT_EQUAL(c.size(), 10u);
    CPPUNIT_ASSERT_EQUAL(c.capacity(), 10u);
    CPPUNIT_ASSERT_EQUAL(c.data(0), p);
    c.resize(5);
    CPPUNIT_ASSERT_EQUAL(a.size(), 5u);
    CPPUNIT_ASSERT_EQUAL(c.size(), 5u);
  }

  {
    int buf[2][10] = {
      { 0,  1,  2,  3,  4,  5,  6,  7,  8,  9 },
      { 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 }
    };
    IntBuffer2D a(&buf[0][0], 2, 10);
    int r, i;

    for (r = 0; r < 2; ++r)
      for (i = 0; i < 10; ++i)
        CPPUNIT_ASSERT_EQUAL(a[r][i], i + r*10);

    for (r = 0; r < 2; ++r)
      for (i = 0; i < 10; ++i)
        a[r][i] = 100 + i + r*10;
    for (r = 0; r < 2; ++r)
      for (i = 0; i < 10; ++i)
        CPPUNIT_ASSERT_EQUAL(a[r][i], 100 + i + r*10);

    a.release();
  }

  {
    IntBuffer2D a(2, 10);
    int *p = a.data(0);

    size_t s = f_ref_2d(a);
    CPPUNIT_ASSERT_EQUAL(s, 10u);
    CPPUNIT_ASSERT_EQUAL(a.rows(), 2u);
    CPPUNIT_ASSERT_EQUAL(a.size(), 10u);
    CPPUNIT_ASSERT_EQUAL(a.data(0), p);
    CPPUNIT_ASSERT_EQUAL(a[0][0], 1234);
    CPPUNIT_ASSERT_EQUAL(a[1][0], 5678);

    s = f_const_ref_2d(a);
    CPPUNIT_ASSERT_EQUAL(s, 10u);
    CPPUNIT_ASSERT_EQUAL(a.rows(), 2u);
    CPPUNIT_ASSERT_EQUAL(a.size(), 10u);
    CPPUNIT_ASSERT_EQUAL(a.data(0), p);

    s = f_val_2d(a);
    CPPUNIT_ASSERT_EQUAL(s, 10u);
    CPPUNIT_ASSERT_EQUAL(a.rows(), 0u);
    CPPUNIT_ASSERT_EQUAL(a.size(), 0u);
    CPPUNIT_ASSERT_EQUAL(a.data(0), (int *)0);
    a.reset(2, 10);

    s = f_const_val_2d(a);
    CPPUNIT_ASSERT_EQUAL(s, 10u);
    CPPUNIT_ASSERT_EQUAL(a.rows(), 0u);
    CPPUNIT_ASSERT_EQUAL(a.size(), 0u);
    CPPUNIT_ASSERT_EQUAL(a.data(0), (int *)0);
  }
}
