//
// Created by 阳坤 on 2022/3/2.
//


#include "image_fingerprint.h"
#include "../utils/yuv_convert.h"

//AVFrame 转 cv::mat
int avframeToCvmat(cv::Mat &image, const AVFrame *frame) {
    int width = frame->width;
    int height = frame->height;
    int cvLinesizes[1];
    cvLinesizes[0] = image.step1();
//    SwsContext *conversion = sws_getContext(width, height, (AVPixelFormat) frame->format, width, height,
//                                            AVPixelFormat::AV_PIX_FMT_BGR24, SWS_FAST_BILINEAR, NULL, NULL, NULL);
//    sws_scale(conversion, frame->data, frame->linesize, 0, height, &image.data, cvLinesizes);
//    sws_freeContext(conversion);
    if (frame->format != AV_PIX_FMT_YUV420P)return -1;
    auto *yuv420p = (uint8_t *) av_malloc(sizeof(uint8_t) * width * height * 3 / 2);
    int yLen = height * width;
    int uLen = yLen / 4;
    int vLen = yLen / 4;
    if (width == frame->linesize[0]) {
        memcpy(yuv420p, frame->data[0], yLen);
        memcpy(yuv420p + yLen, frame->data[1], uLen);
        memcpy(yuv420p + yLen + uLen, frame->data[2], vLen);
    } else {
        for (int i = 0; i < height; i++) {//y
            memcpy(yuv420p + width * i,
                   frame->data[0] + frame->linesize[0] * i,
                   width);
        }
        for (int j = 0; j < height / 2; j++) {//u
            memcpy((yuv420p + yLen) + width / 2 * j,
                   frame->data[1] + frame->linesize[1] * j,
                   width / 2);
        }
        for (int k = 0; k < height / 2; k++) {//v
            memcpy((yuv420p + yLen + uLen) + width / 2 * k,
                   frame->data[2] + frame->linesize[2] * k,
                   width / 2);
        }
    }
    I420ToRGB24(yuv420p, width, height, image.data);
    free(yuv420p);
    yuv420p = NULL;
    return 0;
}

//cv::Mat 转 AVFrame
AVFrame *cvmatToAvframe(cv::Mat *image, AVFrame *frame) {
    int width = image->cols;
    int height = image->rows;
    int cvLinesizes[1];
    cvLinesizes[0] = image->step1();
    if (frame == NULL) {
        frame = av_frame_alloc();
        av_image_alloc(frame->data, frame->linesize, width, height, AVPixelFormat::AV_PIX_FMT_YUV420P, 1);
    }
    SwsContext *conversion = sws_getContext(width, height, AVPixelFormat::AV_PIX_FMT_BGR24, width, height,
                                            (AVPixelFormat) frame->format, SWS_FAST_BILINEAR, NULL, NULL, NULL);
    sws_scale(conversion, &image->data, cvLinesizes, 0, height, frame->data, frame->linesize);
    sws_freeContext(conversion);
    return frame;
}

int fingerprintFromFFAVFrame(FILE *file, AVFrame *frame, char buf[64]) {
    if (file) fprintf(file, "fingerprintFromFFAVFrame\n");
    printf("fingerprintFromFFAVFrame\n");
    if (!frame || frame->width <= 0 || frame->height <= 0)return NULL;

    int width = frame->width;
    int height = frame->height;
    printf("fingerprintFromFFAVFrame -1\n");
    cv::Mat img(height, width, CV_8UC3);
    printf("fingerprintFromFFAVFrame -2\n");
    int ret = avframeToCvmat(img, frame);
    if (ret < 0) {
        printf("fingerprintFromFFAVFrame format=%d not support！\n", frame->format);
        return -1;
    }
//    int cvLinesizes[1];
//    cvLinesizes[0] = img.step1();
//    printf("fingerprintFromFFAVFrame -3\n");
//    SwsContext *conversion = sws_getContext(width, height, (AVPixelFormat) frame->format, width, height,
//                                            AVPixelFormat::AV_PIX_FMT_BGR24, SWS_FAST_BILINEAR, NULL, NULL, NULL);
//    if (conversion) {
//        printf("fingerprintFromFFAVFrame -4\n");
//        int ret = sws_scale(conversion, frame->data, frame->linesize, 0, height, &img.data, cvLinesizes);
//        printf("fingerprintFromFFAVFrame -5 ret=%d\n", ret);
//        sws_freeContext(conversion);
//        conversion = NULL;
//    } else {
//        printf("sws_getContext init error .");
//        return NULL;
//    }
    printf("fingerprintFromFFAVFrame -6\n");
    if (img.empty()) {
        printf("image is empty.");
        return NULL;
    }
    if (file) fprintf(file, "fingerprintFromFFAVFrame 2\n");
    printf("fingerprintFromFFAVFrame 2\n");
//    std::string str = frame->pts + "";
//    imshow(str, img);
    int scale_width = 8, scale_height = 8;
    cv::Mat gray, res;
    if (file) fprintf(file, "fingerprintFromFFAVFrame 3\n");
    printf("fingerprintFromFFAVFrame 3\n");
    //缩放成8x8大小灰度图
    resize(img, res, Size(scale_width, scale_height));
    img.release();
    if (file) fprintf(file, "fingerprintFromFFAVFrame 4\n");
    printf("fingerprintFromFFAVFrame 4\n");
    cvtColor(res, gray, COLOR_BGR2GRAY);
    res.release();
    if (file) fprintf(file, "fingerprintFromFFAVFrame 5\n");
    printf("fingerprintFromFFAVFrame 5\n");
    //获取灰度平均值
    cv::Mat mat_mean, mat_stddev;
    meanStdDev(gray, mat_mean, mat_stddev);//求灰度图像的均值、均方差
    double mn = 0;
    if (!mat_mean.empty()) {
        mn = mat_mean.at<double>(0, 0);
    } else {
        printf("灰度平均值 empty！\n");
        return -1;
    };
    if (file) fprintf(file, "fingerprintFromFFAVFrame 6\n");
    printf("fingerprintFromFFAVFrame 6\n");
    if (gray.cols * gray.rows < scale_height * scale_width)return NULL;
//    //得到图像指纹值
    for (int i = 0; i < 8; i++) {
        for (int j = 0; j < 8; j++) {
            buf[i * 8 + j] = (gray.at<unsigned char>(i, j) > mn) ? 1 : 0;
        }
    }
    if (file) fprintf(file, "fingerprintFromFFAVFrame 7\n");
    printf("fingerprintFromFFAVFrame 7\n");
    gray.release();
    printf("fingerprintFromFFAVFrame 8\n");
    return scale_width * scale_height;
}

int fingerprintFromFFAVFrameV2(FILE *file, AVFrame *frame, char buf[64]) {
    if (file) fprintf(file, "fingerprintFromFFAVFrame\n");
    if (!frame)return NULL;

    int width = frame->width;
    int height = frame->height;
    cv::Mat img(height, width, CV_8UC3);
    int cvLinesizes[1];
    cvLinesizes[0] = img.step1();
    SwsContext *conversion = sws_getContext(width, height, (AVPixelFormat) frame->format, width, height,
                                            AVPixelFormat::AV_PIX_FMT_BGR24, SWS_FAST_BILINEAR, NULL, NULL, NULL);
    sws_scale(conversion, frame->data, frame->linesize, 0, height, &img.data, cvLinesizes);
    sws_freeContext(conversion);
    if (img.empty()) {
        printf("image is empty.");
        return NULL;
    }
    int scale_width = 8, scale_height = 8;
    cv::Mat gray, res;
    cvtColor(img, gray, COLOR_BGR2GRAY);
    //缩放成8x8大小灰度图
    resize(gray, gray, Size(scale_width, scale_height));

    //获取灰度平均值
    cv::Mat mat_mean, mat_stddev;
    meanStdDev(gray, mat_mean, mat_stddev);//求灰度图像的均值、均方差
    double mn = 0;
    if (!mat_mean.empty()) {
        mn = mat_mean.at<double>(0, 0);
    } else {
        printf("灰度平均值 empty！\n");
        return -1;
    };
    if (gray.cols * gray.rows < scale_height * scale_width)return NULL;
//    //得到图像指纹值
    for (int i = 0; i < 8; i++) {
        for (int j = 0; j < 8; j++) {
            buf[i * 8 + j] = (gray.at<unsigned char>(i, j) > mn) ? 1 : 0;
        }
    }
    if (file) fprintf(file, "fingerprintFromFFAVFrame 7\n");
    return scale_width * scale_height;
}


float fingerprint_compare(const char *arr, const char *arr2, int len) {
    int size = len;
    int sim_sum = 0;
    for (int i = 0; i < size; ++i) {
        if (arr[i] == arr2[i])
            sim_sum++;
    }
    return sim_sum * 1.0 / size;
}
