//    This file is part of Motion Watch.

//    Motion Watch is free software: you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    (at your option) any later version.

//    Motion Watch is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.

#include "mo_detect.h"

bool pixDiff(const uchar &pixA, const uchar &pixB, shared_t *share)
{
    auto diff = 0;

    if (pixA > pixB) diff = pixA - pixB;
    if (pixB > pixA) diff = pixB - pixA;

    if (diff < share->colorThresh)
    {
        diff = 0;
    }

    return diff != 0;
}

void secDiff(Mat imgA, Mat imgB, int rows, int cols, int rowOffs, int colOffs, Rect *block, bool *mod, shared_t *share)
{
    auto pnts = 0;

    for (auto y = rowOffs; y < rows; y++)
    {
        for (auto x = colOffs; x < cols; x++)
        {
            auto pixA = imgA.at<uchar>(Point(x, y));
            auto pixB = imgB.at<uchar>(Point(x, y));

            if (pixDiff(pixA, pixB, share))
            {
                pnts += 1;

                if (pnts >= share->blockThresh)
                {
                    block->x = colOffs;
                    block->y = rowOffs;

                    block->height = rows;
                    block->width  = cols;

                    *mod = true; return;
                }
            }
        }
    }
}

bool imgDiff(Mat prev, Mat next, Rect *block, shared_t *share)
{
    auto numOfXBlocks = prev.cols / share->blockX;
    auto numOfYBlocks = prev.rows / share->blockY;
    auto moInBlock    = false;

    vector<thread> threads;

    for (auto x = 0; (x < numOfXBlocks) && !moInBlock; x += share->blockX)
    {
        for (auto y = 0; (y < numOfYBlocks) && !moInBlock; y += share->blockY)
        {
            threads.push_back(thread(secDiff, prev, next, share->blockY, share->blockX, y, x, block, &moInBlock, share));
        }
    }

    for (auto &&thr : threads)
    {
        thr.join();
    }

    return moInBlock;
}

bool moDetect(const string &buffFile, Rect *block, Mat *img, shared_t *share)
{
    auto mod = false;

    VideoCapture capture(buffFile.c_str(), CAP_FFMPEG);

    if (capture.isOpened())
    {
        Mat prev;
        Mat next;

        while (capPair(prev, next, capture, share))
        {
            if (imgDiff(toGray(prev), toGray(next), block, share))
            {
                *img = next;
                mod  = true; break;
            }
        }
    }
    else
    {
        cerr << "err: Could not open buff file: " << buffFile << " for reading. check formatting/permissions." << endl;
        cerr << "     Also check if opencv was compiled with FFMPEG encoding enabled." << endl;
    }

    return mod;
}
