/*
 * Copyright 2010 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.
 */
import ResultPoint from '../../ResultPoint';
import NotFoundException from '../../NotFoundException';
/**
 * @author Mariusz Dąbrowski
 */
var CornerDetector = /** @class */ (function () {
    /**
     * @throws NotFoundException if image is too small to accommodate {@code initSize}
     */
    function CornerDetector(image, initSize, x, y, targetMatrixSize) {
        this.image = image;
        this.height = image.getHeight();
        this.width = image.getWidth();
        var halfsize = initSize / 2;
        this.leftInit = x - halfsize;
        this.rightInit = x + halfsize;
        this.upInit = y - halfsize;
        this.downInit = y + halfsize;
        this.targetMatrixSize = targetMatrixSize * 2;
        if (this.upInit < 0 || this.leftInit < 0 || this.downInit >= this.height || this.rightInit >= this.width) {
            throw new NotFoundException();
        }
    }
    /**
     * @throws NotFoundException if no Data Matrix Code can be found
     */
    CornerDetector.prototype.detect = function () {
        var left = this.leftInit;
        var right = this.rightInit;
        var up = this.upInit;
        var down = this.downInit;
        var sizeExceeded = false;
        var aBlackPointFoundOnBorder = true;
        var atLeastOneBlackPointFoundOnBorder = false;
        var atLeastOneBlackPointFoundOnRight = false;
        var atLeastOneBlackPointFoundOnBottom = false;
        var atLeastOneBlackPointFoundOnLeft = false;
        var atLeastOneBlackPointFoundOnTop = false;
        while (aBlackPointFoundOnBorder) {
            aBlackPointFoundOnBorder = false;
            // .....
            // . |
            // .....
            var rightBorderNotWhite = true;
            while ((rightBorderNotWhite || !atLeastOneBlackPointFoundOnRight) && right < this.width) {
                rightBorderNotWhite = this.containsBlackPoint(up, down, right, false);
                if (rightBorderNotWhite) {
                    right++;
                    aBlackPointFoundOnBorder = true;
                    atLeastOneBlackPointFoundOnRight = true;
                }
                else if (!atLeastOneBlackPointFoundOnRight) {
                    right++;
                }
            }
            if (right >= this.width) {
                sizeExceeded = true;
                break;
            }
            // .....
            // . .
            // .___.
            var bottomBorderNotWhite = true;
            while ((bottomBorderNotWhite || !atLeastOneBlackPointFoundOnBottom) && down < this.height) {
                bottomBorderNotWhite = this.containsBlackPoint(left, right, down, true);
                if (bottomBorderNotWhite) {
                    down++;
                    aBlackPointFoundOnBorder = true;
                    atLeastOneBlackPointFoundOnBottom = true;
                }
                else if (!atLeastOneBlackPointFoundOnBottom) {
                    down++;
                }
            }
            if (down >= this.height) {
                sizeExceeded = true;
                break;
            }
            // .....
            // | .
            // .....
            var leftBorderNotWhite = true;
            while ((leftBorderNotWhite || !atLeastOneBlackPointFoundOnLeft) && left >= 0) {
                leftBorderNotWhite = this.containsBlackPoint(up, down, left, false);
                if (leftBorderNotWhite) {
                    left--;
                    aBlackPointFoundOnBorder = true;
                    atLeastOneBlackPointFoundOnLeft = true;
                }
                else if (!atLeastOneBlackPointFoundOnLeft) {
                    left--;
                }
            }
            if (left < 0) {
                sizeExceeded = true;
                break;
            }
            // .___.
            // . .
            // .....
            var topBorderNotWhite = true;
            while ((topBorderNotWhite || !atLeastOneBlackPointFoundOnTop) && up >= 0) {
                topBorderNotWhite = this.containsBlackPoint(left, right, up, true);
                if (topBorderNotWhite) {
                    up--;
                    aBlackPointFoundOnBorder = true;
                    atLeastOneBlackPointFoundOnTop = true;
                }
                else if (!atLeastOneBlackPointFoundOnTop) {
                    up--;
                }
            }
            if (up < 0) {
                sizeExceeded = true;
                break;
            }
            if (aBlackPointFoundOnBorder) {
                atLeastOneBlackPointFoundOnBorder = true;
            }
        }
        if (!sizeExceeded && atLeastOneBlackPointFoundOnBorder) {
            return this.findCorners(right, left, down, up);
        }
        else {
            throw new NotFoundException();
        }
    };
    CornerDetector.prototype.findCorners = function (right, left, down, up) {
        //
        //      A------------              ------------B
        //      |           |      up      |           |
        //      |    -------|--------------|-------    |
        //      |    |      |              |      |    |
        //      |    |      |              |      |    |
        //      ------------AP            BP------------
        //           |                            |
        //           |                            |
        //      left |                            | right
        //           |                            |
        //           |                            |
        //      ------------DP            CP------------
        //      |    |      |             |       |    |
        //      |    |      |   down      |       |    |
        //      |    -------|-------------|--------    |
        //      |           |             |            |
        //      D-----------|             |------------C
        //
        var width = right - left;
        var height = down - up;
        var sampler = 16 / this.targetMatrixSize;
        var sampler2 = 4 / this.targetMatrixSize;
        var deltaX = width * sampler2;
        var deltaY = height * sampler2;
        var areaWidth = deltaX + (right - left) * sampler;
        var areaHeight = deltaY + (down - up) * sampler;
        var a = new ResultPoint(left - deltaX, up - deltaY);
        var b = new ResultPoint(right + deltaX, up - deltaY);
        var c = new ResultPoint(right + deltaX, down + deltaY);
        var d = new ResultPoint(left - deltaX, down + deltaY);
        var ap = new ResultPoint(a.getX() + areaWidth, a.getY() + areaHeight);
        var bp = new ResultPoint(b.getX() - areaWidth, b.getY() + areaHeight);
        var cp = new ResultPoint(c.getX() - areaWidth, c.getY() - areaHeight);
        var dp = new ResultPoint(d.getX() + areaWidth, d.getY() - areaHeight);
        var topLeftCorner = this.getCornerFromArea(a.getX(), ap.getX(), a.getY(), ap.getY(), false, false);
        var topRightCorner = this.getCornerFromArea(bp.getX(), b.getX(), b.getY(), bp.getY(), true, false);
        var bottomRightCorner = this.getCornerFromArea(cp.getX(), c.getX(), cp.getY(), c.getY(), true, true);
        var bottomLeftCorner = this.getCornerFromArea(d.getX(), dp.getX(), dp.getY(), d.getY(), false, true);
        var xCorrection = (topRightCorner.getX() - topLeftCorner.getX()) / this.targetMatrixSize;
        var yCorrection = (bottomRightCorner.getY() - topRightCorner.getY()) / this.targetMatrixSize;
        var topLeftCornerCenter = new ResultPoint(topLeftCorner.getX() + xCorrection, topLeftCorner.getY() + yCorrection);
        var topRightCornerCenter = new ResultPoint(topRightCorner.getX() - xCorrection, topRightCorner.getY() + yCorrection);
        var bottomRightCornerCenter = new ResultPoint(bottomRightCorner.getX() - xCorrection, bottomRightCorner.getY() - yCorrection);
        var bottomLeftCornerCenter = new ResultPoint(bottomLeftCorner.getX() + xCorrection, bottomLeftCorner.getY() - yCorrection);
        var result = [topLeftCornerCenter, topRightCornerCenter, bottomRightCornerCenter, bottomLeftCornerCenter];
        return result;
    };
    CornerDetector.prototype.getCornerFromArea = function (left, right, top, bottom, maximizeX, maximizeY) {
        var resX = maximizeX ? 0 : Number.MAX_VALUE;
        var resY = maximizeY ? 0 : Number.MAX_VALUE;
        for (var x = left; x < right; x++) {
            for (var y = top; y < bottom; y++) {
                if (x > 0 && y > 0 && x < this.image.getWidth() && y < this.image.getHeight()) {
                    if (this.image.get(x, y)) {
                        if (maximizeX) {
                            if (x > resX) {
                                resX = x;
                            }
                        }
                        else {
                            if (x < resX) {
                                resX = x;
                            }
                        }
                        if (maximizeY) {
                            if (y > resY) {
                                resY = y;
                            }
                        }
                        else {
                            if (y < resY) {
                                resY = y;
                            }
                        }
                    }
                }
            }
        }
        if (resX === 0 || resY === 0) {
            throw new NotFoundException();
        }
        else {
            return new ResultPoint(resX, resY);
        }
    };
    /**
     * Determines whether a segment contains a black point
     *
     * @param a          min value of the scanned coordinate
     * @param b          max value of the scanned coordinate
     * @param fixed      value of fixed coordinate
     * @param horizontal set to true if scan must be horizontal, false if vertical
     * @return true if a black point has been found, else false.
     */
    CornerDetector.prototype.containsBlackPoint = function (a, b, fixed, horizontal) {
        if (horizontal) {
            for (var x = a; x <= b; x++) {
                if (this.image.get(x, fixed)) {
                    return true;
                }
            }
        }
        else {
            for (var y = a; y <= b; y++) {
                if (this.image.get(fixed, y)) {
                    return true;
                }
            }
        }
        return false;
    };
    return CornerDetector;
}());
export default CornerDetector;
