var _PdfIntersection = /** @class */ (function () {
    function _PdfIntersection(s1, s2, c1, c2) {
        this._x = 0.0;
        this._y = 0.0;
        this._toSource = 0.0;
        this._toClip = 0.0;
        var d = (c2._y - c1._y) * (s2._x - s1._x) - (c2._x - c1._x) * (s2._y - s1._y);
        if (d !== 0) {
            this._toSource =
                ((c2._x - c1._x) * (s1._y - c1._y) - (c2._y - c1._y) * (s1._x - c1._x)) / d;
            this._toClip =
                ((s2._x - s1._x) * (s1._y - c1._y) - (s2._y - s1._y) * (s1._x - c1._x)) / d;
            if (this._isValid()) {
                this._x = s1._x + this._toSource * (s2._x - s1._x);
                this._y = s1._y + this._toSource * (s2._y - s1._y);
            }
        }
    }
    _PdfIntersection.prototype._isValid = function () {
        return (0 < this._toSource &&
            this._toSource < 1 &&
            0 < this._toClip &&
            this._toClip < 1);
    };
    return _PdfIntersection;
}());
export { _PdfIntersection };
var _PdfPolygon = /** @class */ (function () {
    function _PdfPolygon(p, arrayVertices) {
        var _this = this;
        this._globalIntersections = [];
        this._first = null;
        this._vertices = 0;
        this._lastUnprocessed = null;
        this._arrayVertices =
            typeof arrayVertices === 'undefined' ? Array.isArray(p[0]) : arrayVertices;
        p.forEach(function (point) {
            _this._addVertex(new _PdfVertex(point.x, point.y));
        });
    }
    _PdfPolygon.prototype._addVertex = function (vertex) {
        if (this._first === null) {
            this._first = vertex;
            this._first._next = vertex;
            this._first._prev = vertex;
        }
        else {
            var next = this._first;
            var prev = next._prev;
            next._prev = vertex;
            vertex._next = next;
            vertex._prev = prev;
            if (prev) {
                prev._next = vertex;
            }
        }
        this._vertices++;
    };
    _PdfPolygon.prototype._insertVertex = function (vertex, start, end) {
        var curr = start;
        while (!curr._equals(end) && curr._distance < vertex._distance) {
            curr = curr._next;
        }
        vertex._next = curr;
        var prev = curr._prev;
        vertex._prev = prev;
        if (prev) {
            prev._next = vertex;
        }
        curr._prev = vertex;
        this._vertices++;
    };
    _PdfPolygon.prototype._getNext = function (v) {
        var c = v;
        while (c._isIntersection) {
            c = c._next;
        }
        return c;
    };
    _PdfPolygon.prototype._getFirstIntersect = function () {
        var v = this._firstIntersect || this._first;
        do {
            if (v._isIntersection && !v._visited) {
                break;
            }
            v = v._next;
        } while (!v._equals(this._first));
        this._firstIntersect = v;
        return v;
    };
    _PdfPolygon.prototype._hasUnprocessed = function () {
        var v = this._lastUnprocessed || this._first;
        do {
            if (v._isIntersection && !v._visited) {
                this._lastUnprocessed = v;
                return true;
            }
            v = v._next;
        } while (!v._equals(this._first));
        this._lastUnprocessed = null;
        return false;
    };
    _PdfPolygon.prototype._getPoints = function () {
        var points = [];
        var v = this._first;
        do {
            points.push({ x: v._x, y: v._y });
            v = v._next;
        } while (v !== this._first);
        return points;
    };
    _PdfPolygon.prototype._clip = function (clip, sourceForwards, clipForwards) {
        var _a;
        this._computeIntersections(clip);
        (_a = this._setEntryExitFlags(clip, sourceForwards, clipForwards), sourceForwards = _a.sourceForwards, clipForwards = _a.clipForwards);
        var result = this._constructClippedPolygons();
        return result.length > 0 ? result : this._handleEmptyResult(clip);
    };
    _PdfPolygon.prototype._computeIntersections = function (clip) {
        var sourceVertex = this._first;
        do {
            if (!sourceVertex._isIntersection) {
                var clipVertex = clip._first;
                do {
                    if (!clipVertex._isIntersection) {
                        var intersection = new _PdfIntersection(sourceVertex, this._getNext(sourceVertex._next), clipVertex, clip._getNext(clipVertex._next));
                        if (intersection._isValid()) {
                            var sourceIntersection = sourceVertex._createIntersection(intersection._x, intersection._y, intersection._toSource);
                            var clipIntersection = clipVertex._createIntersection(intersection._x, intersection._y, intersection._toClip);
                            this._globalIntersections.push({ x: sourceIntersection._x, y: sourceIntersection._y });
                            sourceIntersection._corresponding = clipIntersection;
                            clipIntersection._corresponding = sourceIntersection;
                            this._insertVertex(sourceIntersection, sourceVertex, this._getNext(sourceVertex._next));
                            clip._insertVertex(clipIntersection, clipVertex, clip._getNext(clipVertex._next));
                        }
                    }
                    clipVertex = clipVertex._next;
                } while (!clipVertex._equals(clip._first));
            }
            sourceVertex = sourceVertex._next;
        } while (!sourceVertex._equals(this._first));
    };
    _PdfPolygon.prototype._setEntryExitFlags = function (clip, sourceForwards, clipForwards) {
        var sourceInClip = this._first._isInside(clip);
        var clipInSource = clip._first._isInside(this);
        sourceForwards = sourceForwards !== sourceInClip;
        clipForwards = clipForwards !== clipInSource;
        var sourceVertex = this._first;
        do {
            if (sourceVertex._isIntersection) {
                sourceVertex._isEntry = sourceForwards;
                sourceForwards = !sourceForwards;
            }
            sourceVertex = sourceVertex._next;
        } while (!sourceVertex._equals(this._first));
        var clipVertex = clip._first;
        do {
            if (clipVertex._isIntersection) {
                clipVertex._isEntry = clipForwards;
                clipForwards = !clipForwards;
            }
            clipVertex = clipVertex._next;
        } while (!clipVertex._equals(clip._first));
        return { sourceForwards: sourceForwards, clipForwards: clipForwards };
    };
    _PdfPolygon.prototype._constructClippedPolygons = function () {
        var list = [];
        while (this._hasUnprocessed()) {
            var current = this._getFirstIntersect();
            var clipped = new _PdfPolygon([], this._arrayVertices);
            clipped._addVertex(new _PdfVertex(current._x, current._y));
            do {
                current._visit();
                if (current._isEntry) {
                    do {
                        current = current._next;
                        clipped._addVertex(new _PdfVertex(current._x, current._y));
                    } while (!current._isIntersection);
                }
                else {
                    do {
                        current = current._prev;
                        clipped._addVertex(new _PdfVertex(current._x, current._y));
                    } while (!current._isIntersection);
                }
                current = current._corresponding;
            } while (!current._visited);
            list.push(clipped._getPoints());
        }
        return list;
    };
    _PdfPolygon.prototype._handleEmptyResult = function (clip) {
        var sourceInClip = this._first._isInside(clip);
        var clipInSource = clip._first._isInside(this);
        if (sourceInClip) {
            return [clip._getPoints(), this._getPoints()];
        }
        else if (clipInSource) {
            return [this._getPoints(), clip._getPoints()];
        }
        else {
            return [this._getPoints()];
        }
    };
    return _PdfPolygon;
}());
export { _PdfPolygon };
var _PdfVertex = /** @class */ (function () {
    function _PdfVertex(x, y) {
        if (typeof x !== 'number' || typeof y !== 'number') {
            throw new Error('Invalid coordinate input');
        }
        var xCoord = x;
        var yCoord = y;
        this._x = xCoord;
        this._y = yCoord;
        this._next = null;
        this._prev = null;
        this._corresponding = null;
        this._distance = 0.0;
        this._isEntry = true;
        this._isIntersection = false;
        this._visited = false;
    }
    _PdfVertex.prototype._createIntersection = function (x, y, distance) {
        var vertex = new _PdfVertex(x, y);
        vertex._distance = distance;
        vertex._isIntersection = true;
        vertex._isEntry = false;
        return vertex;
    };
    _PdfVertex.prototype._visit = function () {
        this._visited = true;
        if (this._corresponding !== null && !this._corresponding._visited) {
            this._corresponding._visit();
        }
    };
    _PdfVertex.prototype._equals = function (v) {
        return this._x === v._x && this._y === v._y;
    };
    _PdfVertex.prototype._isInside = function (poly) {
        var oddNodes = false;
        var vertex = poly._first;
        var next = vertex._next;
        var x = this._x;
        var y = this._y;
        do {
            if (((vertex._y < y && next._y >= y) ||
                (next._y < y && vertex._y >= y)) &&
                (vertex._x <= x || next._x <= x)) {
                oddNodes = (oddNodes !== (vertex._x + ((y - vertex._y) / (next._y - vertex._y)) * (next._x - vertex._x) < x));
            }
            vertex = vertex._next;
            next = vertex._next || poly._first;
        } while (!vertex._equals(poly._first));
        return oddNodes;
    };
    return _PdfVertex;
}());
export { _PdfVertex };
