//    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 "common.h"

string cleanDir(const string &path)
{
    if (path[path.size() - 1] == '/')
    {
        return path.substr(0, path.size() - 1);
    }
    else
    {
        return path;
    }
}

bool createDir(const string &dir)
{
    auto ret = mkdir(dir.c_str(), 0777);

    if (ret == -1)
    {
        return errno == EEXIST;
    }
    else
    {
        return true;
    }
}

bool createDirTree(const string &full_path)
{
    size_t pos = 0;
    auto   ret = true;

    while (ret == true && pos != string::npos)
    {
        pos = full_path.find('/', pos + 1);
        ret = createDir(full_path.substr(0, pos));
    }

    return ret;
}

bool fileExists(const string& name)
{
    return access(name.c_str(), F_OK) != -1;
}

void replaceAll(string &str, const string &from, const string &to)
{
    if(from.empty())
        return;

    size_t startPos = 0;

    while((startPos = str.find(from, startPos)) != string::npos)
    {
        str.replace(startPos, from.length(), to);

        startPos += to.length();
    }
}

vector<string> lsFilesInDir(const string &path, const string &ext)
{
    DIR           *dir;
    struct dirent *ent;
    vector<string> names;

    if ((dir = opendir(path.c_str())) != NULL)
    {
        while ((ent = readdir(dir)) != NULL)
        {
            auto name = string(ent->d_name);

            if ((name.size() >= 4) && (ent->d_type & DT_REG))
            {
                if (name.substr(name.size() - 4) == ext)
                {
                    names.push_back(name);
                }
            }
        }

        closedir(dir);
    }

    sort(names.begin(), names.end());

    return names;
}

void enforceMaxDays(shared_t *share)
{
    auto names = lsFilesInDir(share->outDir, ".m3u");

    while (names.size() > share->maxDays)
    {
        auto name    = names[0];
        auto plsFile = cleanDir(share->outDir) + "/" + name;
        auto vidFold = cleanDir(share->outDir) + "/." + name.substr(0, name.size() - 4);

        remove(plsFile.c_str());
        remove_all(vidFold.c_str());

        names.erase(names.begin());
    }
}

string genTimeStr(const char *fmt)
{
    time_t rawtime;

    time(&rawtime);

    auto timeinfo = localtime(&rawtime);

    char ret[50];

    strftime(ret, 50, fmt, timeinfo);

    return string(ret);
}

string genDstFile(const string &dirOut, const char *fmt, const string &ext)
{
    createDirTree(cleanDir(dirOut));

    return cleanDir(dirOut) + string("/") + genTimeStr(fmt) + ext;
}

string genTmpFile(const string &dirOut, const string &ext, shared_t *share)
{
    createDirTree(cleanDir(dirOut));

    if (share->tmpId == 9999999)
    {
        share->tmpId = 0;
    }

    return cleanDir(dirOut) + string("/") + to_string(share->tmpId++) + ext;
}

Mat toGray(const Mat &src)
{
    Mat ret;

    cvtColor(src, ret, COLOR_BGR2GRAY);

    return ret;
}

void rdLine(const string &param, const string &line, string *value)
{
    if (line.rfind(param.c_str(), 0) == 0)
    {
        *value = line.substr(param.size());

        //cout << param << *value << endl;
    }
}

void rdLine(const string &param, const string &line, int *value)
{
    if (line.rfind(param.c_str(), 0) == 0)
    {
        *value = strtol(line.substr(param.size()).c_str(), NULL, 10);

        //cout << param << *value << endl;
    }
}

bool rdConf(shared_t *share)
{
    ifstream varFile(share->conf.c_str());

    if (!varFile.is_open())
    {
        share->retCode = ENOENT;

        cerr << "err: Failed to open the config file: " << share->conf << " for reading. please check file permissions or if it exists." << endl;
    }
    else
    {
        string line;

        share->recordUrl.clear();
        share->outDir.clear();
        share->postCmd.clear();
        share->buffDir.clear();

        share->colorThresh  = 5;
        share->secs         = 60;
        share->blockX       = 32;
        share->blockY       = 32;
        share->blockThresh  = 900;
        share->maxDays      = 5;
        share->vidExt       = "mp4";
        share->recLoopWait  = false;
        share->skipCmd      = false;

        do
        {
            getline(varFile, line);

            if (line.rfind("#", 0) != 0)
            {
                rdLine("recording_stream = ", line, &share->recordUrl);
                rdLine("output_dir = ",       line, &share->outDir);
                rdLine("post_cmd = ",         line, &share->postCmd);
                rdLine("color_threshold = ",  line, &share->colorThresh);
                rdLine("duration = ",         line, &share->secs);
                rdLine("buff_dir = ",         line, &share->buffDir);
                rdLine("block_x = ",          line, &share->blockX);
                rdLine("block_y = ",          line, &share->blockY);
                rdLine("block_threshold = ",  line, &share->blockThresh);
                rdLine("max_days = ",         line, &share->maxDays);
                rdLine("vid_container = ",    line, &share->vidExt);
            }

        } while(!line.empty());

        if (share->init)
        {
            remove_all(share->buffDir.c_str());

            share->init = false;
        }

        new thread(enforceMaxDays, share);

        share->retCode = 0;
    }

    varFile.close();

    return share->retCode == 0;
}

bool capPair(Mat &prev, Mat &next, VideoCapture &capture, shared_t *share)
{
    capture >> prev;
    capture >> next;

    return !prev.empty() && !next.empty();
}

void wrOut(const string &buffFile, shared_t *share)
{
    auto clnDir = cleanDir(share->outDir);
    auto vidOut = genDstFile(clnDir + "/." + genTimeStr("%Y-%m-%d"), "%H%M%S", "." + share->vidExt);
    auto m3uOut = vidOut.substr(clnDir.size() + 1);
    auto lisOut = genDstFile(share->outDir, "%Y-%m-%d", ".m3u");

    copy_file(buffFile.c_str(), vidOut.c_str());
    remove(buffFile.c_str());

    ofstream file;

    if (fileExists(lisOut))
    {
        file.open(lisOut.c_str(), ios_base::app);
    }
    else
    {
        file.open(lisOut.c_str());
    }

    file << m3uOut << endl;

    file.close();
}

string parseForParam(const string &arg, int argc, char** argv, bool argOnly)
{
    for (int i = 0; i < argc; ++i)
    {
        auto argInParams = string(argv[i]);

        if (arg.compare(argInParams) == 0)
        {
            if (!argOnly)
            {
                // check ahead, make sure i + 1 won't cause out-of-range exception
                if ((i + 1) <= (argc - 1))
                {
                    return string(argv[i + 1]);
                }
            }
            else
            {
                return string("true");
            }
        }
    }

    return string();
}
