/*
 * Copyright 2012 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */
#ifndef SkPathOpsPoint_DEFINED
#define SkPathOpsPoint_DEFINED

#include "SkPathOpsTypes.h"
#include "SkPoint.h"

inline bool AlmostEqualUlps(const SkPoint& pt1, const SkPoint& pt2) {
    return AlmostEqualUlps(pt1.fX, pt2.fX) && AlmostEqualUlps(pt1.fY, pt2.fY);
}

struct SkDVector {
    double fX, fY;

    friend SkDPoint operator+(const SkDPoint& a, const SkDVector& b);

    void operator+=(const SkDVector& v) {
        fX += v.fX;
        fY += v.fY;
    }

    void operator-=(const SkDVector& v) {
        fX -= v.fX;
        fY -= v.fY;
    }

    void operator/=(const double s) {
        fX /= s;
        fY /= s;
    }

    void operator*=(const double s) {
        fX *= s;
        fY *= s;
    }

    SkVector asSkVector() const {
        SkVector v = {SkDoubleToScalar(fX), SkDoubleToScalar(fY)};
        return v;
    }

    double cross(const SkDVector& a) const {
        return fX * a.fY - fY * a.fX;
    }

    double dot(const SkDVector& a) const {
        return fX * a.fX + fY * a.fY;
    }

    double length() const {
        return sqrt(lengthSquared());
    }

    double lengthSquared() const {
        return fX * fX + fY * fY;
    }
};

struct SkDPoint {
    double fX;
    double fY;

    void set(const SkPoint& pt) {
        fX = pt.fX;
        fY = pt.fY;
    }

    friend SkDVector operator-(const SkDPoint& a, const SkDPoint& b);

    friend bool operator==(const SkDPoint& a, const SkDPoint& b) {
        return a.fX == b.fX && a.fY == b.fY;
    }

    friend bool operator!=(const SkDPoint& a, const SkDPoint& b) {
        return a.fX != b.fX || a.fY != b.fY;
    }

    void operator=(const SkPoint& pt) {
        fX = pt.fX;
        fY = pt.fY;
    }


    void operator+=(const SkDVector& v) {
        fX += v.fX;
        fY += v.fY;
    }

    void operator-=(const SkDVector& v) {
        fX -= v.fX;
        fY -= v.fY;
    }

    // note: this can not be implemented with
    // return approximately_equal(a.fY, fY) && approximately_equal(a.fX, fX);
    // because that will not take the magnitude of the values
    bool approximatelyEqual(const SkDPoint& a) const {
        double denom = SkTMax(fabs(fX), SkTMax(fabs(fY),
                SkTMax(fabs(a.fX), fabs(a.fY))));
        if (precisely_zero(denom)) {
            return true;
        }
        double inv = 1 / denom;
        return approximately_equal(fX * inv, a.fX * inv)
                && approximately_equal(fY * inv, a.fY * inv);
    }

    bool approximatelyEqual(const SkPoint& a) const {
        return AlmostEqualUlps(SkDoubleToScalar(fX), a.fX)
                && AlmostEqualUlps(SkDoubleToScalar(fY), a.fY);
    }

    bool approximatelyEqualHalf(const SkDPoint& a) const {
        double denom = SkTMax(fabs(fX), SkTMax(fabs(fY),
                SkTMax(fabs(a.fX), fabs(a.fY))));
        if (denom == 0) {
            return true;
        }
        double inv = 1 / denom;
        return approximately_equal_half(fX * inv, a.fX * inv)
                && approximately_equal_half(fY * inv, a.fY * inv);
    }

    bool approximatelyZero() const {
        return approximately_zero(fX) && approximately_zero(fY);
    }

    SkPoint asSkPoint() const {
        SkPoint pt = {SkDoubleToScalar(fX), SkDoubleToScalar(fY)};
        return pt;
    }

    double distance(const SkDPoint& a) const {
        SkDVector temp = *this - a;
        return temp.length();
    }

    double distanceSquared(const SkDPoint& a) const {
        SkDVector temp = *this - a;
        return temp.lengthSquared();
    }

    static SkDPoint Mid(const SkDPoint& a, const SkDPoint& b) {
        SkDPoint result;
        result.fX = (a.fX + b.fX) / 2;
        result.fY = (a.fY + b.fY) / 2;
        return result;
    }

    double moreRoughlyEqual(const SkDPoint& a) const {
        return more_roughly_equal(a.fY, fY) && more_roughly_equal(a.fX, fX);
    }

    double roughlyEqual(const SkDPoint& a) const {
        return roughly_equal(a.fY, fY) && roughly_equal(a.fX, fX);
    }

    #ifdef SK_DEBUG
    void dump() {
        SkDebugf("{");
        DebugDumpDouble(fX);
        SkDebugf(", ");
        DebugDumpDouble(fY);
        SkDebugf("}");
    }

    static void DumpSkPoint(const SkPoint& pt) {
        SkDebugf("{");
        DebugDumpFloat(pt.fX);
        SkDebugf(", ");
        DebugDumpFloat(pt.fY);
        SkDebugf("}");
    }
   #endif
};

#endif
