<?php
/**
 * Created by PhpStorm.
 * User: Ashot
 * Date: 3/24/15
 * Time: 21:23
 */
namespace  Zxing\Common\Detector;

use \Zxing\NotFoundException;
use \Zxing\ResultPoint;
use \Zxing\BitMatrix;

/*
 *
 *
import com.google.zxing.NotFoundException;
import com.google.zxing.ResultPoint;
import com.google.zxing.common.BitMatrix;

 */
//require_once('./lib/NotFoundException.php');
//require_once('./lib/ResultPoint.php');
//require_once('./lib/common/BitMatrix.php');


/**
 * <p>A somewhat generic detector that looks for a barcode-like rectangular region within an image.
 * It looks within a mostly white region of an image for a region of black and white, but mostly
 * black. It returns the four corners of the region, as best it can determine.</p>
 *
 * @author Sean Owen
 * @port Ashot Khanamiryan
 */
class MonochromeRectangleDetector {
    private static $MAX_MODULES = 32;
    private  $image;
    function __construct($image){
        $this->image = $image;
        
    }

    /**
     * <p>Detects a rectangular region of black and white -- mostly black -- with a region of mostly
     * white, in an image.</p>
     *
     * @return {@link ResultPoint}[] describing the corners of the rectangular region. The first and
     *  last points are opposed on the diagonal, as are the second and third. The first point will be
     *  the topmost point and the last, the bottommost. The second point will be leftmost and the
     *  third, the rightmost
     * @throws NotFoundException if no Data Matrix Code can be found
     */
    public function detect(){

        $height = $this->image->getHeight();
        $width = $this->image->getWidth();
        $halfHeight = $height / 2;
        $halfWidth = $width / 2;

        $deltaY = max(1, $height / (self::$MAX_MODULES * 8));
        $deltaX = max(1, $width / (self::$MAX_MODULES * 8));


        $top = 0;
        $bottom = $height;
        $left = 0;
        $right = $width;
        $pointA = $this->findCornerFromCenter($halfWidth, 0, $left, $right,
                $halfHeight, -$deltaY, $top, $bottom, $halfWidth / 2);
    $top = (int) $pointA->getY() - 1;
    $pointB = $this->findCornerFromCenter($halfWidth, -$deltaX, $left,$right,
                $halfHeight, 0, $top, $bottom, $halfHeight / 2);
    $left = (int) $pointB->getX() - 1;
    $pointC = $this->findCornerFromCenter($halfWidth, $deltaX, $left, $right,
                $halfHeight, 0, $top, $bottom, $halfHeight / 2);
    $right = (int) $pointC->getX() + 1;
    $pointD = $this->findCornerFromCenter($halfWidth, 0, $left, $right,
                $halfHeight, $deltaY, $top, $bottom, $halfWidth / 2);
    $bottom = (int) $pointD->getY() + 1;

    // Go try to find po$A again with better information -- might have been off at first.
    $pointA = $this->findCornerFromCenter($halfWidth, 0, $left, $right,
        $halfHeight, -$deltaY, $top, $bottom, $halfWidth / 4);

    return new ResultPoint( $pointA, $pointB, $pointC, $pointD );
  
    }



    /**
     * Attempts to locate a corner of the barcode by scanning up, down, left or right from a center
     * point which should be within the barcode.
     *
     * @param centerX center's x component (horizontal)
     * @param deltaX same as deltaY but change in x per step instead
     * @param left minimum value of x
     * @param right maximum value of x
     * @param centerY center's y component (vertical)
     * @param deltaY change in y per step. If scanning up this is negative; down, positive;
     *  left or right, 0
     * @param top minimum value of y to search through (meaningless when di == 0)
     * @param bottom maximum value of y
     * @param maxWhiteRun maximum run of white pixels that can still be considered to be within
     *  the barcode
     * @return a {@link com.google.zxing.ResultPoint} encapsulating the corner that was found
     * @throws NotFoundException if such a point cannot be found
     */
    private function  findCornerFromCenter($centerX,
                                           $deltaX,
                                           $left,
                                           $right,
                                           $centerY,
                                           $deltaY,
                                           $top,
                                           $bottom,
                                           $maxWhiteRun){
        $lastRange = null;
        for ($y = $centerY, $x = $centerX;
             $y < $bottom && $y >= $top && $x < $right && $x >= $left;
        $y += $deltaY, $x += $deltaX) {
            $range = 0;
      if ($deltaX == 0) {
          // horizontal slices, up and down
          $range = $this->blackWhiteRange($y, $maxWhiteRun, $left, $right, true);
      } else {
          // vertical slices, left and right
          $range = $this->blackWhiteRange($x, $maxWhiteRun, $top, $bottom, false);
      }
      if ($range == null) {
          if ($lastRange == null) {
              throw NotFoundException::getNotFoundInstance();
          }
          // lastRange was found
          if ($deltaX == 0) {
              $lastY = $y - $deltaY;
          if ($lastRange[0] < $centerX) {
                  if ($lastRange[1] > $centerX) {
                      // straddle, choose one or the other based on direction
                      return new ResultPoint($deltaY > 0 ? $lastRange[0] : $lastRange[1], $lastY);
            }
            return new ResultPoint($lastRange[0], $lastY);
          } else {
                  return new ResultPoint($lastRange[1], $lastY);
          }
        } else {
              $lastX = $x - $deltaX;
          if ($lastRange[0] < $centerY) {
                  if ($lastRange[1] > $centerY) {
                      return new ResultPoint($lastX, $deltaX < 0 ? $lastRange[0] : $lastRange[1]);
            }
            return new ResultPoint($lastX, $lastRange[0]);
          } else {
                  return new ResultPoint($lastX, $lastRange[1]);
          }
        }
      }
      $lastRange = $range;
    }
    throw NotFoundException::getNotFoundInstance();
    }



    /**
     * Computes the start and end of a region of pixels, either horizontally or vertically, that could
     * be part of a Data Matrix barcode.
     *
     * @param fixedDimension if scanning horizontally, this is the row (the fixed vertical location)
     *  where we are scanning. If scanning vertically it's the column, the fixed horizontal location
     * @param maxWhiteRun largest run of white pixels that can still be considered part of the
     *  barcode region
     * @param minDim minimum pixel location, horizontally or vertically, to consider
     * @param maxDim maximum pixel location, horizontally or vertically, to consider
     * @param horizontal if true, we're scanning left-right, instead of up-down
     * @return int[] with start and end of found range, or null if no such range is found
     *  (e.g. only white was found)
     */

    private function blackWhiteRange($fixedDimension, $maxWhiteRun, $minDim, $maxDim, $horizontal){
        $center = ($minDim + $maxDim) / 2;

    // Scan left/up first
    $start = $center;
    while ($start >= $minDim) {
        if ($horizontal ? $this->image->get($start, $fixedDimension) : $this->image->get($fixedDimension, $start)) {
            $start--;
        } else {
            $whiteRunStart = $start;
        do {
           $start--;
        } while ($start >= $minDim && !($horizontal ? $this->image->get($start, $fixedDimension) :
                $this->image->get($fixedDimension, $start)));
        $whiteRunSize = $whiteRunStart - $start;
        if ($start < $minDim || $whiteRunSize > $maxWhiteRun) {
            $start = $whiteRunStart;
            break;
        }
      }
    }
    $start++;

    // Then try right/down
    $end = $center;
    while ($end < $maxDim) {
        if ($horizontal ? $this->image->get($end, $fixedDimension) : $this->image->get($fixedDimension, $end)) {
            $end++;
        } else {
            $whiteRunStart = $end;
        do {
            $end++;
        } while ($end < $maxDim && !($horizontal ? $this->image->get($end, $fixedDimension) :
                $this->image->get($fixedDimension, $end)));
        $whiteRunSize = $end - $whiteRunStart;
        if ($end >= $maxDim || $whiteRunSize > $maxWhiteRun) {
            $end = $whiteRunStart;
            break;
        }
      }
    }
    $end--;

    return $end > $start ? [$start, $end] : null;
    }
}