"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var WhiteRectangleDetector_1 = require("../../common/detector/WhiteRectangleDetector");
var DetectorResult_1 = require("../../common/DetectorResult");
var GridSamplerInstance_1 = require("../../common/GridSamplerInstance");
var NotFoundException_1 = require("../../NotFoundException");
var ResultPoint_1 = require("../../ResultPoint");
/*
 * Copyright 2008 ZXing authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/**
 * <p>Encapsulates logic that can detect a Data Matrix Code in an image, even if the Data Matrix Code
 * is rotated or skewed, or partially obscured.</p>
 *
 * @author Sean Owen
 */
var Detector = /** @class */ (function () {
    function Detector(image) {
        this.image = image;
        this.rectangleDetector = new WhiteRectangleDetector_1.default(this.image);
    }
    /**
     * <p>Detects a Data Matrix Code in an image.</p>
     *
     * @return {@link DetectorResult} encapsulating results of detecting a Data Matrix Code
     * @throws NotFoundException if no Data Matrix Code can be found
     */
    Detector.prototype.detect = function () {
        var cornerPoints = this.rectangleDetector.detect();
        var points = this.detectSolid1(cornerPoints);
        points = this.detectSolid2(points);
        points[3] = this.correctTopRight(points);
        if (!points[3]) {
            throw new NotFoundException_1.default();
        }
        points = this.shiftToModuleCenter(points);
        var topLeft = points[0];
        var bottomLeft = points[1];
        var bottomRight = points[2];
        var topRight = points[3];
        var dimensionTop = this.transitionsBetween(topLeft, topRight) + 1;
        var dimensionRight = this.transitionsBetween(bottomRight, topRight) + 1;
        if ((dimensionTop & 0x01) === 1) {
            dimensionTop += 1;
        }
        if ((dimensionRight & 0x01) === 1) {
            dimensionRight += 1;
        }
        if (4 * dimensionTop < 7 * dimensionRight && 4 * dimensionRight < 7 * dimensionTop) {
            // The matrix is square
            dimensionTop = dimensionRight = Math.max(dimensionTop, dimensionRight);
        }
        var bits = Detector.sampleGrid(this.image, topLeft, bottomLeft, bottomRight, topRight, dimensionTop, dimensionRight);
        return new DetectorResult_1.default(bits, [topLeft, bottomLeft, bottomRight, topRight]);
    };
    Detector.shiftPoint = function (point, to, div) {
        var x = (to.getX() - point.getX()) / (div + 1);
        var y = (to.getY() - point.getY()) / (div + 1);
        return new ResultPoint_1.default(point.getX() + x, point.getY() + y);
    };
    Detector.moveAway = function (point, fromX, fromY) {
        var x = point.getX();
        var y = point.getY();
        if (x < fromX) {
            x -= 1;
        }
        else {
            x += 1;
        }
        if (y < fromY) {
            y -= 1;
        }
        else {
            y += 1;
        }
        return new ResultPoint_1.default(x, y);
    };
    /**
     * Detect a solid side which has minimum transition.
     */
    Detector.prototype.detectSolid1 = function (cornerPoints) {
        // 0  2
        // 1  3
        var pointA = cornerPoints[0];
        var pointB = cornerPoints[1];
        var pointC = cornerPoints[3];
        var pointD = cornerPoints[2];
        var trAB = this.transitionsBetween(pointA, pointB);
        var trBC = this.transitionsBetween(pointB, pointC);
        var trCD = this.transitionsBetween(pointC, pointD);
        var trDA = this.transitionsBetween(pointD, pointA);
        // 0..3
        // :  :
        // 1--2
        var min = trAB;
        var points = [pointD, pointA, pointB, pointC];
        if (min > trBC) {
            min = trBC;
            points[0] = pointA;
            points[1] = pointB;
            points[2] = pointC;
            points[3] = pointD;
        }
        if (min > trCD) {
            min = trCD;
            points[0] = pointB;
            points[1] = pointC;
            points[2] = pointD;
            points[3] = pointA;
        }
        if (min > trDA) {
            points[0] = pointC;
            points[1] = pointD;
            points[2] = pointA;
            points[3] = pointB;
        }
        return points;
    };
    /**
     * Detect a second solid side next to first solid side.
     */
    Detector.prototype.detectSolid2 = function (points) {
        // A..D
        // :  :
        // B--C
        var pointA = points[0];
        var pointB = points[1];
        var pointC = points[2];
        var pointD = points[3];
        // Transition detection on the edge is not stable.
        // To safely detect, shift the points to the module center.
        var tr = this.transitionsBetween(pointA, pointD);
        var pointBs = Detector.shiftPoint(pointB, pointC, (tr + 1) * 4);
        var pointCs = Detector.shiftPoint(pointC, pointB, (tr + 1) * 4);
        var trBA = this.transitionsBetween(pointBs, pointA);
        var trCD = this.transitionsBetween(pointCs, pointD);
        // 0..3
        // |  :
        // 1--2
        if (trBA < trCD) {
            // solid sides: A-B-C
            points[0] = pointA;
            points[1] = pointB;
            points[2] = pointC;
            points[3] = pointD;
        }
        else {
            // solid sides: B-C-D
            points[0] = pointB;
            points[1] = pointC;
            points[2] = pointD;
            points[3] = pointA;
        }
        return points;
    };
    /**
     * Calculates the corner position of the white top right module.
     */
    Detector.prototype.correctTopRight = function (points) {
        // A..D
        // |  :
        // B--C
        var pointA = points[0];
        var pointB = points[1];
        var pointC = points[2];
        var pointD = points[3];
        // shift points for safe transition detection.
        var trTop = this.transitionsBetween(pointA, pointD);
        var trRight = this.transitionsBetween(pointB, pointD);
        var pointAs = Detector.shiftPoint(pointA, pointB, (trRight + 1) * 4);
        var pointCs = Detector.shiftPoint(pointC, pointB, (trTop + 1) * 4);
        trTop = this.transitionsBetween(pointAs, pointD);
        trRight = this.transitionsBetween(pointCs, pointD);
        var candidate1 = new ResultPoint_1.default(pointD.getX() + (pointC.getX() - pointB.getX()) / (trTop + 1), pointD.getY() + (pointC.getY() - pointB.getY()) / (trTop + 1));
        var candidate2 = new ResultPoint_1.default(pointD.getX() + (pointA.getX() - pointB.getX()) / (trRight + 1), pointD.getY() + (pointA.getY() - pointB.getY()) / (trRight + 1));
        if (!this.isValid(candidate1)) {
            if (this.isValid(candidate2)) {
                return candidate2;
            }
            return null;
        }
        if (!this.isValid(candidate2)) {
            return candidate1;
        }
        var sumc1 = this.transitionsBetween(pointAs, candidate1) + this.transitionsBetween(pointCs, candidate1);
        var sumc2 = this.transitionsBetween(pointAs, candidate2) + this.transitionsBetween(pointCs, candidate2);
        if (sumc1 > sumc2) {
            return candidate1;
        }
        else {
            return candidate2;
        }
    };
    /**
     * Shift the edge points to the module center.
     */
    Detector.prototype.shiftToModuleCenter = function (points) {
        // A..D
        // |  :
        // B--C
        var pointA = points[0];
        var pointB = points[1];
        var pointC = points[2];
        var pointD = points[3];
        // calculate pseudo dimensions
        var dimH = this.transitionsBetween(pointA, pointD) + 1;
        var dimV = this.transitionsBetween(pointC, pointD) + 1;
        // shift points for safe dimension detection
        var pointAs = Detector.shiftPoint(pointA, pointB, dimV * 4);
        var pointCs = Detector.shiftPoint(pointC, pointB, dimH * 4);
        //  calculate more precise dimensions
        dimH = this.transitionsBetween(pointAs, pointD) + 1;
        dimV = this.transitionsBetween(pointCs, pointD) + 1;
        if ((dimH & 0x01) === 1) {
            dimH += 1;
        }
        if ((dimV & 0x01) === 1) {
            dimV += 1;
        }
        // WhiteRectangleDetector returns points inside of the rectangle.
        // I want points on the edges.
        var centerX = (pointA.getX() + pointB.getX() + pointC.getX() + pointD.getX()) / 4;
        var centerY = (pointA.getY() + pointB.getY() + pointC.getY() + pointD.getY()) / 4;
        pointA = Detector.moveAway(pointA, centerX, centerY);
        pointB = Detector.moveAway(pointB, centerX, centerY);
        pointC = Detector.moveAway(pointC, centerX, centerY);
        pointD = Detector.moveAway(pointD, centerX, centerY);
        var pointBs;
        var pointDs;
        // shift points to the center of each modules
        pointAs = Detector.shiftPoint(pointA, pointB, dimV * 4);
        pointAs = Detector.shiftPoint(pointAs, pointD, dimH * 4);
        pointBs = Detector.shiftPoint(pointB, pointA, dimV * 4);
        pointBs = Detector.shiftPoint(pointBs, pointC, dimH * 4);
        pointCs = Detector.shiftPoint(pointC, pointD, dimV * 4);
        pointCs = Detector.shiftPoint(pointCs, pointB, dimH * 4);
        pointDs = Detector.shiftPoint(pointD, pointC, dimV * 4);
        pointDs = Detector.shiftPoint(pointDs, pointA, dimH * 4);
        return [pointAs, pointBs, pointCs, pointDs];
    };
    Detector.prototype.isValid = function (p) {
        return p.getX() >= 0 && p.getX() < this.image.getWidth() && p.getY() > 0 && p.getY() < this.image.getHeight();
    };
    Detector.sampleGrid = function (image, topLeft, bottomLeft, bottomRight, topRight, dimensionX, dimensionY) {
        var sampler = GridSamplerInstance_1.default.getInstance();
        return sampler.sampleGrid(image, dimensionX, dimensionY, 0.5, 0.5, dimensionX - 0.5, 0.5, dimensionX - 0.5, dimensionY - 0.5, 0.5, dimensionY - 0.5, topLeft.getX(), topLeft.getY(), topRight.getX(), topRight.getY(), bottomRight.getX(), bottomRight.getY(), bottomLeft.getX(), bottomLeft.getY());
    };
    /**
     * Counts the number of black/white transitions between two points, using something like Bresenham's algorithm.
     */
    Detector.prototype.transitionsBetween = function (from, to) {
        // See QR Code Detector, sizeOfBlackWhiteBlackRun()
        var fromX = Math.trunc(from.getX());
        var fromY = Math.trunc(from.getY());
        var toX = Math.trunc(to.getX());
        var toY = Math.trunc(to.getY());
        var steep = Math.abs(toY - fromY) > Math.abs(toX - fromX);
        if (steep) {
            var temp = fromX;
            fromX = fromY;
            fromY = temp;
            temp = toX;
            toX = toY;
            toY = temp;
        }
        var dx = Math.abs(toX - fromX);
        var dy = Math.abs(toY - fromY);
        var error = -dx / 2;
        var ystep = fromY < toY ? 1 : -1;
        var xstep = fromX < toX ? 1 : -1;
        var transitions = 0;
        var inBlack = this.image.get(steep ? fromY : fromX, steep ? fromX : fromY);
        for (var x = fromX, y = fromY; x !== toX; x += xstep) {
            var isBlack = this.image.get(steep ? y : x, steep ? x : y);
            if (isBlack !== inBlack) {
                transitions++;
                inBlack = isBlack;
            }
            error += dy;
            if (error > 0) {
                if (y === toY) {
                    break;
                }
                y += ystep;
                error -= dx;
            }
        }
        return transitions;
    };
    return Detector;
}());
exports.default = Detector;
