#pragma once
#include "relation_infos.h"

#include <list>

namespace maps
{
namespace wiki
{

/**
* Order objects with in range topologycaly
* (neighbours in result touch each other)
* objects should not make loops
* @param itBegin - start of range
* @param itEnd - end of range
* @param touchesPredicate - functional object which computes to true
* with a and b if a touches b
* @result ordered range iterators list/empty lis, if range has gaps
*/
template <class ObjectIterator, class TouchesPredicate>
std::list<ObjectIterator>
topologycalOrder(ObjectIterator itBegin, ObjectIterator itEnd,
                 TouchesPredicate touchesPredicate)
{
    std::list<ObjectIterator> source;
    for(auto it = itBegin; it != itEnd; ++it){
        source.push_back(it);
    }
    std::list<ObjectIterator> result;
    while(source.size()){
        bool touchesFound = false;
        for(auto it = source.begin(); it != source.end();){
            if(result.empty() || touchesPredicate(**it, *result.front())){
                result.push_front(*it);
                touchesFound = true;
                it = source.erase(it);
            } else if(touchesPredicate(**it, *result.back())) {
                result.push_back(*it);
                touchesFound = true;
                it = source.erase(it);
            } else {
                ++it;
            }
        }
        if(!touchesFound) {
            return std::list<ObjectIterator>();
        }
    }
    return result;
}


template<class ObjectIterator>
bool
hasGeom(ObjectIterator beginIt, ObjectIterator endIt)
{
    for(auto it = beginIt; it != endIt; ++it){
        if(hasGeom(*it)){
            return true;
        }
    }
    return false;
}

template<class ObjectIterator>
bool
touches(const Geom& geom, ObjectIterator beginIt, ObjectIterator endIt)
{
    if(geom.isNull()){
        return false;
    }
    for(ObjectIterator it = beginIt; it != endIt; ++it){
        if(hasGeom(*it) && geom->touches(it->geom().geosGeometryPtr())){
            return true;
        }
    }
    return false;
}

template<class ObjectIterator>
bool
touches(ObjectIterator beginIt1, ObjectIterator endIt1, ObjectIterator beginIt2, ObjectIterator endIt2)
{
    for(ObjectIterator it1 = beginIt1; it1 != endIt1; ++it1){
        if(touches(it1->geom(), beginIt2, endIt2)){
            return true;
        }
    }
    return false;
}

template<class ObjectIterator>
bool
continous(ObjectIterator beginIt, ObjectIterator endIt)
{
    std::list<RelationInfo> sea;
    for(auto it = beginIt; it != endIt; ++it){
        if(hasGeom(*it)){
            sea.push_back(*it);
        }
    }
    if(sea.size() < 1){
        return true;
    }
    std::list<RelationInfo> curWave;
    curWave.push_back(sea.front());
    sea.pop_front();
    while(sea.size()){
        std::list<RelationInfo> nextWave;
        for(auto seaIt = sea.begin(); seaIt != sea.end(); ){
            if(touches(seaIt->geom(), curWave.begin(), curWave.end())){
                nextWave.push_back(*seaIt);
                seaIt = sea.erase(seaIt);
            } else {
                ++seaIt;
            }
        }
        if(nextWave.empty()){
            break;
        }
        std::swap(curWave, nextWave);
    }
    return sea.empty();
}

}//wiki
}//maps
