#include "node_neighborhood.h"
#include "geom.h"

#include <maps/libs/common/include/exception.h>

namespace maps {
namespace wiki {
namespace validator {
namespace utils {

namespace gl = geolib3;

NodeNeighborhood::TPartsList::iterator
NodeNeighborhood::split(const gl::Direction2& direction, TId edgeId)
{
    for (auto toSplit = parts_.begin(); toSplit != parts_.end(); ++toSplit) {
        const Sector& curSector = toSplit->sector;

        if (curSector.rightDirection.angle() == direction.angle()) {
            return toSplit;
        }

        double sectorAngle = utils::ccwAngle(
                curSector.rightDirection, curSector.leftDirection);
        if (utils::ccwAngle(curSector.rightDirection, direction) < sectorAngle
                && utils::ccwAngle(direction, curSector.leftDirection) < sectorAngle) {
            Part leftPart = *toSplit;
            leftPart.sector.rightDirection = direction;
            leftPart.sector.rightEdge = edgeId;

            toSplit->sector.leftDirection = direction;
            toSplit->sector.leftEdge = edgeId;

            toSplit++;
            return parts_.insert(toSplit, leftPart);
        }
    }

    // Preconditions must have been violated
    throw maps::Exception("Couldn't find sector to split");
}

void NodeNeighborhood::addToSector(const Sector& sector, const AdmUnit* admUnit)
{
    if (parts_.empty()) {
        parts_.push_back(Part{sector, { admUnit }});
        parts_.push_back(Part{sector.complement(), {}});
    } else {
        auto rightSplit = split(sector.rightDirection, sector.rightEdge);
        auto leftSplit = split(sector.leftDirection, sector.leftEdge);

        auto part = rightSplit;
        while (part != leftSplit) {
            part->admUnits.insert(admUnit);

            part++;
            if (part == parts_.end()) {
                part = parts_.begin();
            }
        }
    }
}

void NodeNeighborhood::addToAllParts(const AdmUnit* admUnit)
{
    admUnitsInAllParts_.push_back(admUnit);
}

std::vector<NodeNeighborhood::Part> NodeNeighborhood::parts() const
{
    std::vector<Part> result;
    for (const Part& part : parts_) {
        result.push_back(part);
        for (auto admUnit : admUnitsInAllParts_) {
            result.back().admUnits.insert(admUnit);
        }
    }
    return result;
}

} // namespace utils
} // namespace validator
} // namespace wiki
} // namespace maps
