var __assign = (this && this.__assign) || function () {
    __assign = Object.assign || function(t) {
        for (var s, i = 1, n = arguments.length; i < n; i++) {
            s = arguments[i];
            for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
                t[p] = s[p];
        }
        return t;
    };
    return __assign.apply(this, arguments);
};
import { _PdfRecord } from '@syncfusion/ej2-pdf';
import { _PdfPolygon } from './pdf-shape-redaction';
import { _TextProcessingMode } from '../enum';
import { _PdfBezierSegment, _PdfLineSegment, _PdfPathFigure } from './pdf-path-segment';
var _PdfShapeParser = /** @class */ (function () {
    function _PdfShapeParser() {
        this._pathAccumulator = [];
    }
    _PdfShapeParser.prototype._clearPathAccumulator = function () {
        this._pathAccumulator = [];
    };
    _PdfShapeParser.prototype._findRedactPath = function (recordCollection, i, page, redaction, mode, stream) {
        var startCount = 1;
        var _currentPath;
        var bezierSegment;
        for (var k = i; k < recordCollection.length; k++) {
            var record = recordCollection[k];
            var value = record._operator.trim();
            var element = record._operands;
            if (value === _PdfPathCommands.drawStroke || value === _PdfPathCommands.drawCloseStroke ||
                value === _PdfPathCommands.fill || value === _PdfPathCommands.fillEvenOdd ||
                value === _PdfPathCommands.fillStroke || value === _PdfPathCommands.fillEvenOddStroke ||
                value === _PdfPathCommands.fillCloseStroke || value === _PdfPathCommands.fillEvenOddCloseStroke) {
                if (mode !== _TextProcessingMode.redaction) {
                    this._clearPathAccumulator();
                    return -1;
                }
                var shapePaths = this._getGeometry(_currentPath);
                shapePaths = this._flattenIfNeeded(shapePaths);
                var fullPath = { commands: shapePaths, renderingMode: value };
                var shapePoints = [this._extractPoints(fullPath.commands)];
                var _redaction = redaction._redactionRegion;
                var _a = this._clipAgainstRedactions(shapePoints, _redaction, page), updatedShapePoints = _a.updatedShapePoints, intersectionsPoints = _a.intersectionsPoints, isInSide = _a.isInSide, isOutSide = _a.isOutSide, inSideRects = _a.inSideRects, totalRedactionPoints = _a.totalRedactionPoints;
                if (this._shouldSkipRendering(updatedShapePoints, fullPath.commands, isInSide)) {
                    this._clearPathAccumulator();
                    return -1;
                }
                var records = this._buildRenderingRecords(value, updatedShapePoints, intersectionsPoints, isInSide, isOutSide, inSideRects, totalRedactionPoints);
                if (records && records.length > 0 && !isOutSide) {
                    for (var j = 0; j < records.length; j++) {
                        redaction._optimizeContent(records, j, '', stream);
                    }
                }
                this._clearPathAccumulator();
                return k - 1;
            }
            switch (value) {
                case _PdfPathCommands.moveTo:
                    startCount++;
                    if (startCount === 2) {
                        _currentPath = new _PdfPathFigure();
                        _currentPath._startPoint = { x: Number(element[0]), y: Number(element[1]) };
                        this._currentLocation = [Number(element[0]), Number(element[1])];
                    }
                    break;
                case _PdfPathCommands.lineTo:
                    if (startCount === 2) {
                        _currentPath._segments.push(new _PdfLineSegment({ x: Number(element[0]), y: Number(element[1]) }));
                    }
                    break;
                case _PdfPathCommands.curveTo:
                    if (startCount === 2) {
                        bezierSegment = new _PdfBezierSegment();
                        bezierSegment._point1 = { x: Number(element[0]), y: Number(element[1]) };
                        bezierSegment._point2 = { x: Number(element[2]), y: Number(element[3]) };
                        bezierSegment._point3 = { x: Number(element[4]), y: Number(element[5]) };
                        _currentPath._segments.push(bezierSegment);
                    }
                    break;
                case _PdfPathCommands.curveToV:
                    if (startCount === 2) {
                        bezierSegment = new _PdfBezierSegment();
                        bezierSegment._point1 = { x: this._currentLocation[0], y: this._currentLocation[1] };
                        bezierSegment._point2 = { x: Number(element[0]), y: Number(element[1]) };
                        bezierSegment._point3 = { x: Number(element[2]), y: Number(element[3]) };
                        _currentPath._segments.push(bezierSegment);
                    }
                    break;
                case _PdfPathCommands.curveToY:
                    if (startCount === 2) {
                        bezierSegment = new _PdfBezierSegment();
                        bezierSegment._point1 = { x: Number(element[0]), y: Number(element[1]) };
                        bezierSegment._point2 = { x: Number(element[2]), y: Number(element[3]) };
                        bezierSegment._point3 = bezierSegment._point2;
                        _currentPath._segments.push(bezierSegment);
                    }
                    break;
                case _PdfPathCommands.closePath:
                    if (_currentPath) {
                        _currentPath._isClosed = true;
                    }
                    break;
                default:
                    return -1;
            }
        }
        return -1;
    };
    _PdfShapeParser.prototype._flattenIfNeeded = function (shapePaths) {
        for (var p = 0; p < shapePaths.length; p++) {
            if (shapePaths[p].operator.indexOf('c') !== -1) {
                shapePaths = this._flattenPdfPathCommands(shapePaths);
                break;
            }
        }
        return shapePaths;
    };
    _PdfShapeParser.prototype._clipAgainstRedactions = function (shapePoints, redactions, page) {
        var intersectionsPoints = [];
        var inSideRects = [];
        var totalRedactionPoints = [];
        var isInSide = false;
        var isOutSide = false;
        for (var _i = 0, redactions_1 = redactions; _i < redactions_1.length; _i++) {
            var redaction = redactions_1[_i];
            if (redaction._isTextOnly) {
                continue;
            }
            var bounds = this._adjustRedactionBounds(redaction._bounds, shapePoints[0][0].y, page);
            var redactionPath = this._rectToPathCommands(bounds);
            var redactionPoints = this._extractPoints(redactionPath);
            totalRedactionPoints.push(redactionPoints);
            var newNonRedactedPoints = [];
            for (var _a = 0, shapePoints_1 = shapePoints; _a < shapePoints_1.length; _a++) {
                var shape = shapePoints_1[_a];
                var source = new _PdfPolygon(shape);
                var clipPoly = new _PdfPolygon(redactionPoints);
                var clipped = source._clip(clipPoly, false, true);
                intersectionsPoints.push.apply(intersectionsPoints, source._globalIntersections);
                newNonRedactedPoints.push.apply(newNonRedactedPoints, clipped);
            }
            if (newNonRedactedPoints.length === 2) {
                if (this._pointsArraysEqual(newNonRedactedPoints[1], redactionPoints)) {
                    isInSide = true;
                    newNonRedactedPoints.splice(1, 1);
                    inSideRects.push(redactionPath);
                }
                else if (this._pointsArraysEqual(newNonRedactedPoints[0], redactionPoints)) {
                    isOutSide = true;
                    newNonRedactedPoints.splice(1, 1);
                }
            }
            shapePoints = newNonRedactedPoints;
        }
        return { updatedShapePoints: shapePoints, intersectionsPoints: intersectionsPoints, isInSide: isInSide, isOutSide: isOutSide, inSideRects: inSideRects, totalRedactionPoints: totalRedactionPoints };
    };
    _PdfShapeParser.prototype._adjustRedactionBounds = function (bounds, y, page) {
        var adjusted = __assign({}, bounds);
        if (y < 0) {
            adjusted.y = -adjusted.y;
            adjusted.height = -adjusted.height;
        }
        else {
            adjusted.y = page.size.height - adjusted.y - adjusted.height;
        }
        return adjusted;
    };
    _PdfShapeParser.prototype._shouldSkipRendering = function (shapePoints, originalPoints, isInSide) {
        return shapePoints.length === 0 ||
            shapePoints.length === 1 &&
                this._pointsArraysEqual(this._extractPoints(originalPoints), shapePoints[0]) &&
                !isInSide;
    };
    _PdfShapeParser.prototype._buildRenderingRecords = function (value, shapePoints, intersectionsPoints, isInSide, isOutSide, inSideRects, totalRedactionPoints) {
        var records = [];
        if (value === _PdfPathCommands.drawStroke || value === _PdfPathCommands.drawCloseStroke) {
            if (!isOutSide) {
                shapePoints = this._removeRedactionPoints(this._removeDuplicatePoints(shapePoints), totalRedactionPoints);
                records.push.apply(records, this._buildRecords(this._convertPointsToPath(shapePoints, intersectionsPoints)));
                if (value === _PdfPathCommands.drawCloseStroke) {
                    records.push(new _PdfRecord('S', []));
                }
            }
        }
        else if (value === _PdfPathCommands.fill || value === _PdfPathCommands.fillEvenOdd) {
            records.push.apply(records, this._buildRecords(this._convertPointsToPath(shapePoints)));
            if (isInSide && inSideRects) {
                for (var _i = 0, inSideRects_1 = inSideRects; _i < inSideRects_1.length; _i++) {
                    var rect = inSideRects_1[_i];
                    records.push(new _PdfRecord('h', []));
                    records.push(new _PdfRecord('f', []), new _PdfRecord('rg', ['1.000', '1.000', '1.000']));
                    records.push.apply(records, this._buildRecords(rect));
                }
            }
        }
        else {
            records.push.apply(records, this._buildRecords(this._convertPointsToPath(shapePoints)));
            if (!isOutSide) {
                if (isInSide) {
                    records.push(new _PdfRecord('h', []));
                }
                records.push(new _PdfRecord('f', []));
                shapePoints = this._removeRedactionPoints(this._removeDuplicatePoints(shapePoints), totalRedactionPoints);
                records.push.apply(records, this._buildRecords(this._convertPointsToPath(shapePoints, intersectionsPoints)));
                if (isInSide) {
                    records.push(new _PdfRecord('h', []));
                }
                records.push(new _PdfRecord('S', []));
            }
            if (isInSide && inSideRects) {
                for (var _a = 0, inSideRects_2 = inSideRects; _a < inSideRects_2.length; _a++) {
                    var inSide = inSideRects_2[_a];
                    records.push(new _PdfRecord('f', []), new _PdfRecord('rg', ['1.000', '1.000', '1.000']));
                    records.push.apply(records, this._buildRecords(inSide).concat([new _PdfRecord('f', [])]));
                }
            }
        }
        return records;
    };
    _PdfShapeParser.prototype._getGeometry = function (figure) {
        this._pathAccumulator = [];
        var point = { x: figure._startPoint.x, y: figure._startPoint.y };
        this._pathAccumulator.push({ operator: 'm', points: [point] });
        for (var _i = 0, _a = figure._segments; _i < _a.length; _i++) {
            var segment = _a[_i];
            if (segment instanceof _PdfLineSegment) {
                var line = segment;
                var points = { x: line._point.x, y: line._point.y };
                this._pathAccumulator.push({ operator: 'l', points: [points] });
            }
            else if (segment instanceof _PdfBezierSegment) {
                var bezier = segment;
                var points = [
                    { x: bezier._point1.x, y: bezier._point1.y },
                    { x: bezier._point2.x, y: bezier._point2.y },
                    { x: bezier._point3.x, y: bezier._point3.y }
                ];
                this._pathAccumulator.push({ operator: 'c', points: points });
            }
        }
        if (figure._isClosed) {
            this._pathAccumulator.push({ operator: 'h', points: [] });
        }
        return this._pathAccumulator;
    };
    _PdfShapeParser.prototype._isValidRectangle = function (recordCollection, i) {
        for (var _i = 0, _a = [1, 2, 3]; _i < _a.length; _i++) {
            var offset = _a[_i];
            var item = recordCollection[i + offset];
            if (item && item._operator === 'n') {
                return false;
            }
        }
        return true;
    };
    _PdfShapeParser.prototype._processRectangle = function (recordCollection, i, element) {
        if (this._isValidRectangle(recordCollection, i)) {
            return this._generateRectanglePath(recordCollection, i, element);
        }
        return [];
    };
    _PdfShapeParser.prototype._generateRectanglePath = function (recordCollection, i, element) {
        var records = [];
        var rect = {
            x: parseFloat(element[0]),
            y: parseFloat(element[1]),
            width: parseFloat(element[2]),
            height: parseFloat(element[3])
        };
        var pathCommands = this._rectToPathCommands(rect);
        for (var _i = 0, pathCommands_1 = pathCommands; _i < pathCommands_1.length; _i++) {
            var cmd = pathCommands_1[_i];
            if (cmd.operator === 'h' && recordCollection[i + 1]._operator !== 'h') {
                records.push(new _PdfRecord('h', []));
            }
            else {
                for (var _a = 0, _b = cmd.points; _a < _b.length; _a++) {
                    var pt = _b[_a];
                    var operands = [pt.x.toFixed(3), pt.y.toFixed(3)];
                    records.push(new _PdfRecord(cmd.operator, operands));
                }
            }
        }
        return records;
    };
    _PdfShapeParser.prototype._rectToPathCommands = function (rect) {
        return [
            {
                operator: 'm',
                points: [{ x: rect.x + rect.width, y: rect.y + rect.height }]
            },
            {
                operator: 'l',
                points: [{ x: rect.x, y: rect.y + rect.height }]
            },
            {
                operator: 'l',
                points: [{ x: rect.x, y: rect.y }]
            },
            {
                operator: 'l',
                points: [{ x: rect.x + rect.width, y: rect.y }]
            },
            {
                operator: 'h',
                points: []
            }
        ];
    };
    _PdfShapeParser.prototype._extractPoints = function (commands) {
        var points = [];
        for (var _i = 0, commands_1 = commands; _i < commands_1.length; _i++) {
            var cmd = commands_1[_i];
            points.push.apply(points, cmd.points);
        }
        return points;
    };
    _PdfShapeParser.prototype._isIntersecting = function (pt, polygonIntersections) {
        if (!polygonIntersections) {
            return false;
        }
        for (var i = 0; i < polygonIntersections.length; i++) {
            if (polygonIntersections[i].x === pt.x && polygonIntersections[i].y === pt.y) {
                return true;
            }
        }
        return false;
    };
    _PdfShapeParser.prototype._convertPointsToPath = function (input, polygonIntersections) {
        var commands = [];
        var hasIntersections = polygonIntersections && polygonIntersections.length > 0;
        for (var _i = 0, input_1 = input; _i < input_1.length; _i++) {
            var points = input_1[_i];
            if (points.length === 0) {
                continue;
            }
            commands.push({ operator: 'm', points: [points[0]] });
            for (var i = 1; i < points.length; i++) {
                var prevPoint = points[i - 1];
                var currentPoint = points[i];
                if (hasIntersections) {
                    var prevIsIntersect = this._isIntersecting(prevPoint, polygonIntersections);
                    var currIsIntersect = this._isIntersecting(currentPoint, polygonIntersections);
                    if (prevIsIntersect && currIsIntersect) {
                        commands.push({ operator: 'm', points: [currentPoint] });
                    }
                    else {
                        commands.push({ operator: 'l', points: [currentPoint] });
                    }
                }
                else {
                    commands.push({ operator: 'l', points: [currentPoint] });
                }
            }
        }
        return commands;
    };
    _PdfShapeParser.prototype._flattenBezierCurve = function (p0, p1, p2, p3, segments) {
        if (segments === void 0) { segments = 8; }
        var points = [];
        for (var i = 1; i <= segments; i++) {
            var t = i / segments;
            var mt = 1 - t;
            var x = mt * mt * mt * p0.x +
                3 * mt * mt * t * p1.x +
                3 * mt * t * t * p2.x +
                t * t * t * p3.x;
            var y = mt * mt * mt * p0.y +
                3 * mt * mt * t * p1.y +
                3 * mt * t * t * p2.y +
                t * t * t * p3.y;
            points.push({ x: x, y: y });
        }
        return points;
    };
    _PdfShapeParser.prototype._flattenPdfPathCommands = function (commands, segmentsPerCurve) {
        if (segmentsPerCurve === void 0) { segmentsPerCurve = 8; }
        var flattened = [];
        var currentPoint;
        for (var _i = 0, commands_2 = commands; _i < commands_2.length; _i++) {
            var cmd = commands_2[_i];
            if (cmd.operator === 'm') {
                flattened.push(cmd);
                currentPoint = cmd.points[0];
            }
            else if (cmd.operator === 'l') {
                flattened.push(cmd);
                currentPoint = cmd.points[0];
            }
            else if (cmd.operator === 'c') {
                if (!currentPoint) {
                    throw new Error('Invalid path: curve without current point');
                }
                var _a = cmd.points, cp1 = _a[0], cp2 = _a[1], endPoint = _a[2];
                var flattenedPoints = this._flattenBezierCurve(currentPoint, cp1, cp2, endPoint, segmentsPerCurve);
                for (var _b = 0, flattenedPoints_1 = flattenedPoints; _b < flattenedPoints_1.length; _b++) {
                    var pt = flattenedPoints_1[_b];
                    flattened.push({
                        operator: 'l',
                        points: [pt]
                    });
                }
                currentPoint = endPoint;
            }
            else if (cmd.operator === 'h') {
                flattened.push(cmd);
            }
        }
        return flattened;
    };
    _PdfShapeParser.prototype._pointsArraysEqual = function (a, b) {
        if (a.length !== b.length) {
            return false;
        }
        return a.every(function (point, i) { return point.x === b[i].x && //eslint-disable-line
            point.y === b[i].y; });
    };
    _PdfShapeParser.prototype._removeDuplicatePoints = function (pointsArrays) {
        return pointsArrays.map(function (points) {
            var uniquePoints = new Set();
            return points.filter(function (pt) {
                var key = pt.x + "," + pt.y;
                if (uniquePoints.has(key)) {
                    return false;
                }
                else {
                    uniquePoints.add(key);
                    return true;
                }
            });
        });
    };
    _PdfShapeParser.prototype._removeRedactionPoints = function (nonRedactedPointsArray, redactionPointsArray) {
        var redactionPoints = new Set();
        for (var _i = 0, redactionPointsArray_1 = redactionPointsArray; _i < redactionPointsArray_1.length; _i++) {
            var redactionArray = redactionPointsArray_1[_i];
            for (var _a = 0, redactionArray_1 = redactionArray; _a < redactionArray_1.length; _a++) {
                var pt = redactionArray_1[_a];
                redactionPoints.add(pt.x + "," + pt.y);
            }
        }
        var filteredPointsArray = nonRedactedPointsArray.map(function (points) {
            return points.filter(function (pt) { return !redactionPoints.has(pt.x + "," + pt.y); });
        });
        return filteredPointsArray;
    };
    _PdfShapeParser.prototype._buildRecords = function (commands) {
        var result = [];
        for (var a = 0; a < commands.length; a++) {
            var cmd = commands[a];
            for (var b = 0; b < cmd.points.length; b++) {
                var pt = cmd.points[b];
                result.push(new _PdfRecord(cmd.operator, [pt.x.toFixed(3), pt.y.toFixed(3)]));
            }
        }
        return result;
    };
    return _PdfShapeParser;
}());
export { _PdfShapeParser };
var _PdfPathCommands;
(function (_PdfPathCommands) {
    _PdfPathCommands["drawStroke"] = "S";
    _PdfPathCommands["drawCloseStroke"] = "s";
    _PdfPathCommands["fill"] = "f";
    _PdfPathCommands["fillEvenOdd"] = "f*";
    _PdfPathCommands["fillStroke"] = "B";
    _PdfPathCommands["fillEvenOddStroke"] = "B*";
    _PdfPathCommands["fillCloseStroke"] = "b";
    _PdfPathCommands["fillEvenOddCloseStroke"] = "b*";
    _PdfPathCommands["moveTo"] = "m";
    _PdfPathCommands["lineTo"] = "l";
    _PdfPathCommands["curveTo"] = "c";
    _PdfPathCommands["curveToV"] = "v";
    _PdfPathCommands["curveToY"] = "y";
    _PdfPathCommands["closePath"] = "h";
})(_PdfPathCommands || (_PdfPathCommands = {}));
