///////////////////////////////////////////////////////////////////////////////
//
// Project:  C++ Test Suite for GDAL/OGR
// Purpose:  TUT: C++ Unit Test Framework extensions for GDAL Test Suite
// Author:   Mateusz Loskot <mateusz@loskot.net>
//
///////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2006, Mateusz Loskot <mateusz@loskot.net>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// 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
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the
// Free Software Foundation, Inc., 59 Temple Place - Suite 330,
// Boston, MA 02111-1307, USA.
///////////////////////////////////////////////////////////////////////////////
#ifndef TUT_GDAL_H_INCLUDED
#define TUT_GDAL_H_INCLUDED

#include <ogr_api.h> // GDAL
#include <cassert>
#include <sstream>
#include <string>

#include <tut.hpp>

namespace tut
{

#if defined(WIN32)
#define SEP '\\'
#else
#define SEP '/'
#endif

//
// Template of attribute reading function and its specializations
//
template <typename T>
inline void read_feature_attribute(OGRFeatureH feature, int index, T& val)
{
    assert(!"Can't find read_feature_attribute specialization for given type");
}

template <>
inline void read_feature_attribute(OGRFeatureH feature, int index, int& val)
{
    val = OGR_F_GetFieldAsInteger(feature, index);
}

template <>
inline void read_feature_attribute(OGRFeatureH feature, int index, double& val)
{
    val = OGR_F_GetFieldAsDouble(feature, index);
}

template <>
inline void read_feature_attribute(OGRFeatureH feature, int index, std::string& val)
{
    val = OGR_F_GetFieldAsString(feature, index);
}

//
// Test equality of two OGR geometries according to passed tolerance.
//
void ensure_equal_geometries(OGRGeometryH lhs, OGRGeometryH rhs, double tolerance);

//
// Test layer attributes from given field against expected list of values
//
template <typename T>
void ensure_equal_attributes(OGRLayerH layer, std::string const& field, T const& list)
{
    ensure("Layer is NULL", NULL != layer);

    OGRFeatureDefnH featDefn = OGR_L_GetLayerDefn(layer);
    ensure("Layer schema is NULL", NULL != featDefn);

    int fldIndex = OGR_FD_GetFieldIndex(featDefn, field.c_str());
    std::ostringstream os;
    os << "Can't find field '" << field << "'";
    ensure(os.str(), fldIndex >= 0);

    // Test value in tested field from subsequent features
    OGRFeatureH feat = NULL;
    OGRFieldDefnH fldDefn = NULL;
    typename T::value_type attrVal;

    for (typename T::const_iterator it = list.begin(); it != list.end(); ++it)
    {
        feat = OGR_L_GetNextFeature(layer);

        fldDefn = OGR_F_GetFieldDefnRef(feat, fldIndex);
        ensure("Field schema is NULL", NULL != fldDefn);

        read_feature_attribute(feat, fldIndex, attrVal);

        OGR_F_Destroy(feat);

        // Test attribute against expected value
        ensure_equals("Attributes not equal", (*it), attrVal);
    }

    // Check if not too many features filtered
    feat = OGR_L_GetNextFeature(layer);
    bool notTooMany = (NULL == feat);
    OGR_F_Destroy(feat);

    ensure("Got more features than expected", notTooMany);
}


template <typename T>
void ensure_approx_equals(T const& a, T const& b)
{
    std::ostringstream os;
    os << "Approx. equality failed: " << a << " != " << b;
    ensure(os.str(), (a == 0.0 || b == 0.0) ? fabs(a-b) <= .00000000001 : fabs(1.0 * b / a - 1.0) <= .00000000001);
}

} // namespace tut

#endif // TUT_GDAL_H_INCLUDED
