///////////////////////////////////////////////////////////////////////////////
//
// 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, int, T &)
{
    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", nullptr != layer);

    OGRFeatureDefnH featDefn = OGR_L_GetLayerDefn(layer);
    ensure("Layer schema is NULL", nullptr != 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 = nullptr;
    OGRFieldDefnH fldDefn = nullptr;
    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", nullptr != 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 = (nullptr == 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
