function setupROI(frame, trackWindow) {
  let roi = frame.roi(trackWindow);
  let hsvRoi = new cv.Mat();
  cv.cvtColor(roi, hsvRoi, cv.COLOR_RGBA2RGB);
  cv.cvtColor(hsvRoi, hsvRoi, cv.COLOR_RGB2HSV);
  let mask = new cv.Mat();
  let lowScalar = new cv.Scalar(30, 30, 0);
  let highScalar = new cv.Scalar(180, 180, 180);
  let low = new cv.Mat(hsvRoi.rows, hsvRoi.cols, hsvRoi.type(), lowScalar);
  let high = new cv.Mat(hsvRoi.rows, hsvRoi.cols, hsvRoi.type(), highScalar);
  cv.inRange(hsvRoi, low, high, mask);
  let roiHist = new cv.Mat();
  let hsvRoiVec = new cv.MatVector();
  hsvRoiVec.push_back(hsvRoi);
  cv.calcHist(hsvRoiVec, [0], mask, roiHist, [180], [0, 180]);
  cv.normalize(roiHist, roiHist, 0, 255, cv.NORM_MINMAX);
  return roiHist;
}

async function setupCapture() {
  let video = document.getElementById('videoInput');
  const stream = await navigator.mediaDevices.getUserMedia({
    video: true,
    audio: false,
  });
  video.srcObject = stream;
  video.play();

  let cap = new cv.VideoCapture(video);

  let frame = new cv.Mat(video.height, video.width, cv.CV_8UC4);
  cap.read(frame);
  return { video, cap, frame };
}

function drawRect(trackBox, frame) {
  let pts = cv.rotatedRectPoints(trackBox);
  cv.line(frame, pts[0], pts[1], [255, 0, 0, 255], 3);
  cv.line(frame, pts[1], pts[2], [255, 0, 0, 255], 3);
  cv.line(frame, pts[2], pts[3], [255, 0, 0, 255], 3);
  cv.line(frame, pts[3], pts[0], [255, 0, 0, 255], 3);
}

function onOpenCvReady() {
  cv['onRuntimeInitialized'] = async () => {
    const FPS = 30;

    const { video, cap, frame } = await setupCapture();

    let hsv = new cv.Mat(video.height, video.width, cv.CV_8UC3);
    let hsvVec = new cv.MatVector();
    hsvVec.push_back(hsv);
    let dst = new cv.Mat();
    let trackBox = null;
    let trackWindow = new cv.Rect(150, 60, 63, 125);
    let termCrit = new cv.TermCriteria(
      cv.TERM_CRITERIA_EPS | cv.TERM_CRITERIA_COUNT,
      10,
      1
    );
    const roiHist = setupROI(frame, trackWindow);

    function processVideo() {
      try {
        let begin = Date.now();

        cap.read(frame);
        cv.cvtColor(frame, hsv, cv.COLOR_RGBA2RGB);
        cv.cvtColor(hsv, hsv, cv.COLOR_RGB2HSV);
        cv.calcBackProject(hsvVec, [0], roiHist, dst, [0, 180], 1);

        [trackBox, trackWindow] = cv.CamShift(dst, trackWindow, termCrit);

        drawRect(trackBox, frame);
        cv.imshow('canvasOutput', frame);

        let delay = 1000 / FPS - (Date.now() - begin);
        setTimeout(processVideo, delay);
      } catch (err) {
        console.error(err);
      }
    }

    setTimeout(processVideo, 0);
  };
}
