/*!
    \internal
*/
QVariant QDeclarativeSearchModelBase::searchArea() const
{
    QGeoShape s = m_request.searchArea();
    if (s.type() == QGeoShape::RectangleType)
        return QVariant::fromValue(QGeoRectangle(s));
    else if (s.type() == QGeoShape::CircleType)
        return QVariant::fromValue(QGeoCircle(s));
    else
        return QVariant::fromValue(s);
}
void tst_QGeoCircle::assignment()
{
    QGeoCircle c1 = QGeoCircle(QGeoCoordinate(10.0, 0.0), 20.0);
    QGeoCircle c2 = QGeoCircle(QGeoCoordinate(20.0, 0.0), 30.0);

    QVERIFY(c1 != c2);

    c2 = c1;
    QCOMPARE(c2.center(), QGeoCoordinate(10.0, 0.0));
    QCOMPARE(c2.radius(), 20.0);
    QCOMPARE(c1, c2);

    c2.setCenter(QGeoCoordinate(30.0, 0.0));
    c2.setRadius(15.0);
    QCOMPARE(c1.center(), QGeoCoordinate(10.0, 0.0));
    QCOMPARE(c1.radius(), 20.0);

    // Assign c1 to an area
    QGeoShape area = c1;
    QCOMPARE(area.type(), c1.type());
    QVERIFY(area == c1);

    // Assign the area back to a bounding circle
    QGeoCircle ca = area;
    QCOMPARE(ca.center(), c1.center());
    QCOMPARE(ca.radius(), c1.radius());

    // Check that the copy is not modified when modifying the original.
    c1.setCenter(QGeoCoordinate(15.0, 15.0));
    QVERIFY(ca.center() != c1.center());
    QVERIFY(ca != c1);
}
static bool addAtForBoundingArea(const QGeoShape &area,
                                 QUrlQuery *queryItems)
{
    QGeoCoordinate center;
    switch (area.type()) {
    case QGeoShape::RectangleType:
        center = QGeoRectangle(area).center();
        break;
    case QGeoShape::CircleType:
        center = QGeoCircle(area).center();
        break;
    case QGeoShape::UnknownType:
        break;
    }

    if (!center.isValid()) {
        return false;
    } else {
        queryItems->addQueryItem(QLatin1String("at"),
                                 QString::number(center.latitude()) +
                                 QLatin1Char(',') +
                                 QString::number(center.longitude()));
        return true;
    }
}
QDebug operator<<(QDebug dbg, const QGeoShape &shape)
{
    //dbg << *shape.d_func();
    dbg.nospace() << "QGeoShape(";
    switch (shape.type()) {
    case QGeoShape::UnknownType:
        dbg.nospace() << "Unknown";
        break;
    case QGeoShape::RectangleType:
        dbg.nospace() << "Rectangle";
        break;
    case QGeoShape::CircleType:
        dbg.nospace() << "Circle";
    }

    dbg.nospace() << ')';

    return dbg;
}
/*!
    \qmlmethod QtLocation::Map::fitViewportToGeoShape(QGeoShape shape)

    Fits the current viewport to the boundary of the shape. The camera is positioned
    in the center of the shape, and at the largest integral zoom level possible which
    allows the whole shape to be visible on screen

*/
void QDeclarativeGeoMap::fitViewportToGeoShape(const QVariant &variantShape)
{
    if (!map_ || !mappingManagerInitialized_)
        return;

    QGeoShape shape;

    if (variantShape.userType() == qMetaTypeId<QGeoRectangle>())
        shape = variantShape.value<QGeoRectangle>();
    else if (variantShape.userType() == qMetaTypeId<QGeoCircle>())
        shape = variantShape.value<QGeoCircle>();
    else if (variantShape.userType() == qMetaTypeId<QGeoShape>())
        shape = variantShape.value<QGeoShape>();

    if (!shape.isValid())
        return;

    double bboxWidth;
    double bboxHeight;
    QGeoCoordinate centerCoordinate;

    switch (shape.type()) {
    case QGeoShape::RectangleType:
    {
        QGeoRectangle rect = shape;
        QDoubleVector2D topLeftPoint = map_->coordinateToScreenPosition(rect.topLeft(), false);
        QDoubleVector2D botRightPoint = map_->coordinateToScreenPosition(rect.bottomRight(), false);
        bboxWidth = qAbs(topLeftPoint.x() - botRightPoint.x());
        bboxHeight = qAbs(topLeftPoint.y() - botRightPoint.y());
        centerCoordinate = rect.center();
        break;
    }
    case QGeoShape::CircleType:
    {
        QGeoCircle circle = shape;
        centerCoordinate = circle.center();
        QGeoCoordinate edge = centerCoordinate.atDistanceAndAzimuth(circle.radius(), 90);
        QDoubleVector2D centerPoint = map_->coordinateToScreenPosition(centerCoordinate, false);
        QDoubleVector2D edgePoint = map_->coordinateToScreenPosition(edge, false);
        bboxWidth = qAbs(centerPoint.x() - edgePoint.x()) * 2;
        bboxHeight = bboxWidth;
        break;
    }
    case QGeoShape::UnknownType:
        //Fallthrough to default
    default:
        return;
    }

    // position camera to the center of bounding box
    setProperty("center", QVariant::fromValue(centerCoordinate));

    //If the shape is empty we just change centerposition, not zoom
    if (bboxHeight == 0 && bboxWidth == 0)
        return;

    // adjust zoom
    double bboxWidthRatio = bboxWidth / (bboxWidth + bboxHeight);
    double mapWidthRatio = width() / (width() + height());
    double zoomRatio;

    if (bboxWidthRatio > mapWidthRatio)
        zoomRatio = bboxWidth / width();
    else
        zoomRatio = bboxHeight / height();

    qreal newZoom = log10(zoomRatio) / log10(0.5);
    newZoom = floor(qMax(minimumZoomLevel(), (map_->mapController()->zoom() + newZoom)));
    setProperty("zoomLevel", QVariant::fromValue(newZoom));
}
QPlaceSearchReply *QPlaceManagerEngineOsm::search(const QPlaceSearchRequest &request)
{
    bool unsupported = false;

    // Only public visibility supported
    unsupported |= request.visibilityScope() != QLocation::UnspecifiedVisibility &&
                   request.visibilityScope() != QLocation::PublicVisibility;
    unsupported |= request.searchTerm().isEmpty() && request.categories().isEmpty();

    if (unsupported)
        return QPlaceManagerEngine::search(request);

    QUrlQuery queryItems;

    queryItems.addQueryItem(QStringLiteral("format"), QStringLiteral("jsonv2"));

    //queryItems.addQueryItem(QStringLiteral("accept-language"), QStringLiteral("en"));

    QGeoRectangle boundingBox;
    QGeoShape searchArea = request.searchArea();
    switch (searchArea.type()) {
    case QGeoShape::CircleType: {
        QGeoCircle c(searchArea);
        qreal radius = c.radius();
        if (radius < 0)
            radius = 50000;

        boundingBox = QGeoRectangle(c.center().atDistanceAndAzimuth(radius, -45),
                                    c.center().atDistanceAndAzimuth(radius, 135));
        break;
    }
    case QGeoShape::RectangleType:
        boundingBox = searchArea;
        break;
    default:
        ;
    }

    if (!boundingBox.isEmpty()) {
        queryItems.addQueryItem(QStringLiteral("bounded"), QStringLiteral("1"));
        QString coordinates;
        coordinates = QString::number(boundingBox.topLeft().longitude()) + QLatin1Char(',') +
                      QString::number(boundingBox.topLeft().latitude()) + QLatin1Char(',') +
                      QString::number(boundingBox.bottomRight().longitude()) + QLatin1Char(',') +
                      QString::number(boundingBox.bottomRight().latitude());
        queryItems.addQueryItem(QStringLiteral("viewbox"), coordinates);
    }

    QStringList queryParts;
    if (!request.searchTerm().isEmpty())
        queryParts.append(request.searchTerm());

    foreach (const QPlaceCategory &category, request.categories()) {
        QString id = category.categoryId();
        int index = id.indexOf(QLatin1Char('='));
        if (index != -1)
            id = id.mid(index+1);
        queryParts.append(QLatin1Char('[') + id + QLatin1Char(']'));
    }

    queryItems.addQueryItem(QStringLiteral("q"), queryParts.join(QLatin1Char('+')));

    QVariantMap parameters = request.searchContext().toMap();

    QStringList placeIds = parameters.value(QStringLiteral("ExcludePlaceIds")).toStringList();
    if (!placeIds.isEmpty())
        queryItems.addQueryItem(QStringLiteral("exclude_place_ids"), placeIds.join(QLatin1Char(',')));

    queryItems.addQueryItem(QStringLiteral("addressdetails"), QStringLiteral("1"));

    QUrl requestUrl(m_urlPrefix);
    requestUrl.setQuery(queryItems);

    QNetworkReply *networkReply = m_networkManager->get(QNetworkRequest(requestUrl));

    QPlaceSearchReplyOsm *reply = new QPlaceSearchReplyOsm(request, networkReply, this);
    connect(reply, SIGNAL(finished()), this, SLOT(replyFinished()));
    connect(reply, SIGNAL(error(QPlaceReply::Error,QString)),
            this, SLOT(replyError(QPlaceReply::Error,QString)));

    return reply;
}