//
// Created by 阳坤 on 2022/1/12.
//

#ifndef JNI_WEB_TOOLS_H
#define JNI_WEB_TOOLS_H

#define LOGE(format, ...)  Log(format,##__VA_ARGS__);
#define MIN(X, Y)  ((X) < (Y) ? (X) : (Y))
#define MAX(X, Y)  ((X) > (Y) ? (X) : (Y))

extern "C"{
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavutil/imgutils.h>
#include <libavutil/avutil.h>
#include <libswscale/swscale.h>
#include <libavutil/fifo.h>
#include <libavformat/avio.h>
#include <libavfilter/avfilter.h>
#include <sys/time.h>
#include <unistd.h>
#include <libavfilter/buffersink.h>
#include <libavfilter/buffersrc.h>
#include <libswresample/swresample.h>
#include <libavutil/opt.h>
#include "stdio.h"
}


#define kCustomIoBufferSize  32 * 1024
#define kInitialPcmBufferSize  128 * 1024
#define kDefaultFifoSize  1 * 1024 * 1024
#define kMaxFifoSize  16 * 1024 * 1024

#define DEFAULT_AUDIO_SAMPLE_RATE 44100
#define DEFAULT_AUDIO_CHANNELS 2
#define DEFAULT_VIDEO_FPS 25
#define DEFAULT_FORCE_PRECISION 0

static FILE *pFile = NULL;

static inline void print_ffmpeg_suport_codec() {
    AVCodec *codec = av_codec_next(NULL);
    char *info = (char *) malloc(40000);
    memset(info, 0, 40000);
    while (codec != NULL) {
        if (codec->decode != NULL) {
            strcat(info, "[Decode]");
        } else {
            strcat(info, "[Encode]");
        }
        switch (codec->type) {
            case AVMEDIA_TYPE_VIDEO:
                strcat(info, "[Video]");
                break;
            case AVMEDIA_TYPE_AUDIO:
                strcat(info, "[Audeo]");
                break;
            default:
                strcat(info, "[Other]");
                break;
        }
        sprintf(info, "%s %10s\n", info, codec->name);
        codec = codec->next;
    }
    puts(info);
    free(info);
}

static inline void split(char *str_, char delims[], long ret[], int *count) {
    int len = strlen(str_);
    char str[strlen(str_) + 1];
    for (int i = 0; i < len; i++) {
        str[i] = str_[i];
    }
    str[len] = '\0';
    char *result = NULL;
    result = strtok(str, delims);
    int index = 0;
    while (result != NULL) {
        ret[index++] = atol(result);
        result = strtok(NULL, delims);
    }
    *count = index;
}

static inline long getCurrentTimeMills() {
    struct timeval tv;
    gettimeofday(&tv, NULL);
    return tv.tv_sec * 1000 + tv.tv_usec / 1000;
}

static inline void Log(const char *format, ...) {
    char szBuffer[1024] = {0};
    char szTime[1024] = {0};
    char *p = NULL;
    int prefixLength = 0;
    const char *tag = "Core";

    time_t t;  //秒时间
    struct tm *local; //本地时间

    t = time(NULL); //获取目前秒时间

    local = localtime(&t); //转为本地时间，注意，该函数非线程安全，下面的例子会使用线程安全函数localtime_r

    if (1) {
        strftime(szTime, 64, "%Y-%m-%d %H:%M:%S", local); //根据需要自定义格式
    }

    prefixLength = sprintf(szBuffer, "[%s][%s][DT] ", szTime, tag);
    p = szBuffer + prefixLength;

    if (1) {
        va_list ap;
        va_start(ap, format);
        vsnprintf(p, 1024 - prefixLength, format, ap);
        va_end(ap);
    }
    printf("%s\n", szBuffer);
}



static int getVideoRotate(AVDictionary *metadata);

static int
drop_or_add_frame_countV2(int cur_fps, int fix_fps, double sync_ipts, double sync_opts, int64_t next_pkt_duration,
                          AVRational st_time_base);


static int
drop_or_add_frame_countV2(int cur_fps, int fix_fps, double sync_ipts, double sync_opts, int64_t next_pkt_duration,
                          AVRational st_time_base) {
    int nb_frames;
    double delta, delta0;
    double duration = 1.0 / ((cur_fps / 1.0) * (1.0 / fix_fps));
    duration = FFMIN(duration, 1.0 / ((fix_fps / 1.0) * (1.0 / fix_fps)));

    duration = lrintf(next_pkt_duration * av_q2d(st_time_base) / (1.0 / fix_fps));
    delta0 = sync_ipts -
             sync_opts;
    delta = delta0 + duration;
//    LOGE("sync_ipts=%f sync_opts=%f delta=%f drop_frame=%d  duration=%f \n", sync_ipts, sync_opts, delta,
//         delta < -1.1, duration);
    nb_frames = 1;

    //��ʱ�����  ffmpeg.c Դ��Ҫ����Щ��û���õ���!
    if (delta0 < 0 &&
        delta > 0
            ) {
//        if (delta0 < -0.6) {
//            LOGE("Past duration %f too large\n", -delta0);
//        } else
//            LOGE("Clipping frame in rate conversion by %f\n", -delta0);
        sync_ipts = sync_opts;
        duration += delta0;
        delta0 = 0;
    }
    if (delta < -1.1)
        nb_frames = 0;
    else if (delta > 1.1) {
        nb_frames = lrintf(delta);
    }
    return nb_frames;
}


static int getVideoRotate(AVDictionary *metadata) {
    AVDictionaryEntry *tag = NULL;
    int m_Rotate = 0;
    tag = av_dict_get(metadata, "rotate", tag, m_Rotate);
    if (tag == NULL) {
        m_Rotate = 0;
    } else {
        int angle = atoi(tag->value);
        angle %= 360;
        if (angle == 90) {
            m_Rotate = 90;
        } else if (angle == 180) {
            m_Rotate = 180;
        } else if (angle == 270) {
            m_Rotate = 270;
        } else {
            m_Rotate = 0;
        }
    }
    return m_Rotate;
}

static void ffmpegLogCallback(void *ptr, int level, const char *fmt, va_list vl) {
    static int printPrefix = 1;
    static int count = 0;
    static char prev[1024] = {0};
    char line[1024] = {0};
    static int is_atty;
    AVClass *avc = ptr ? *(AVClass **) ptr : NULL;
    if (level > AV_LOG_DEBUG) {
        return;
    }

    line[0] = 0;

    if (printPrefix && avc) {
        if (avc->parent_log_context_offset) {
            AVClass **parent = *(AVClass ***) (((uint8_t *) ptr) + avc->parent_log_context_offset);
            if (parent && *parent) {
                snprintf(line, sizeof(line), "[%s @ %p] ", (*parent)->item_name(parent), parent);
            }
        }
        snprintf(line + strlen(line), sizeof(line) - strlen(line), "[%s @ %p] ", avc->item_name(ptr), ptr);
    }

    vsnprintf(line + strlen(line), sizeof(line) - strlen(line), fmt, vl);
    line[strlen(line) + 1] = 0;
    LOGE("%s", line);
}

static int GetAACSampleRateIndex(int sampling_frequency) {
    switch (sampling_frequency) {
        case 96000:
            return 0;
        case 88200:
            return 1;
        case 64000:
            return 2;
        case 48000:
            return 3;
        case 44100:
            return 4;
        case 32000:
            return 5;
        case 24000:
            return 6;
        case 22050:
            return 7;
        case 16000:
            return 8;
        case 12000:
            return 9;
        case 11025:
            return 10;
        case 8000:
            return 11;
        case 7350:
            return 12;
        default:
            return 0;
    }
}


static void close_file() {
    if (pFile) {
        fclose(pFile);
        pFile = NULL;
    }
}

static void savaFrame(uint8_t *pFrame, int width, int height, int frame, int size, int isRgb) {

    char szFilename[1024];
    //生成文件名称
    sprintf(szFilename, "/Users/devyk/Data/Project/piaoquan/PQMedia/temp/image/frame%d.yuv", frame);

    //创建或打开文件
    if (!pFile)
        pFile = fopen(szFilename, "wb");

    if (pFile == NULL) {
        return;
    }
    if (isRgb)
        //写入头信息
        fprintf(pFile, "P6\n%d %d\n225\n", width, height);
    //写入数据
    fwrite(pFrame, 1, size, pFile);

}


//创建默认的帧
static int bf_create_default_frame(enum AVMediaType type, AVCodecContext *ctx, AVFrame **frame) {
    if (frame) {
        *frame = av_frame_alloc();
        if (!(*frame = av_frame_alloc())) {
            LOGE("Could not allocate output frame\n");
            return -1;
        }
        if (type == AVMEDIA_TYPE_AUDIO){
            (*frame)->format = AV_SAMPLE_FMT_FLTP;
            (*frame)->channel_layout = AV_CH_LAYOUT_STEREO;
            (*frame)->sample_rate = DEFAULT_AUDIO_SAMPLE_RATE;
            (*frame)->channels = av_get_channel_layout_nb_channels((*frame)->channel_layout);
            (*frame)->nb_samples = 1024;
            if ((av_frame_get_buffer((*frame), 1)) != 0) {
                LOGE("av_frame_get_buffer fatal��\n");
                av_frame_free(&(*frame));
                (*frame) = NULL;
                return -1;
            }
            av_samples_set_silence((*frame)->data, 0, (*frame)->nb_samples, (*frame)->channels,
                                   (enum AVSampleFormat) (*frame)->format);
            (*frame)->pts = (*frame)->nb_samples;
            (*frame)->pkt_pts = (*frame)->nb_samples;
            (*frame)->pkt_dts = (*frame)->nb_samples;
            (*frame)->pkt_duration = (*frame)->nb_samples;
        } else if (type == AVMEDIA_TYPE_VIDEO){
            (*frame)->format = AV_PIX_FMT_YUV420P;
            (*frame)->width = ctx->width;
            (*frame)->height = ctx->height;
            int ret = av_frame_get_buffer(*frame, 1);
            if (ret < 0) {
                LOGE("Could not allocate the video frame data\n");
                return -1;
            }
            /* 生成黑色背景 */
            /* Y */
            for (int y = 0; y < (*frame)->height; y++) {
                for (int x = 0; x < (*frame)->width; x++) {
                    (*frame)->data[0][y * (*frame)->linesize[0] + x] = 0;
                }
            }
            /* Cb and Cr */
            for (int y = 0; y < (*frame)->height / 2; y++) {
                for (int x = 0; x < (*frame)->width / 2; x++) {
                    (*frame)->data[1][y * (*frame)->linesize[1] + x] = 0x80;
                    (*frame)->data[2][y * (*frame)->linesize[2] + x] = 0x80;
                }
            }
        }
    }
    return 0;
}

static int bf_frame_2_uint8_ptr(enum AVMediaType type,AVFrame *in,uint8_t**out_data,int format){
    int sampleSize = 0;
    int audioDataSize = 0;
    int offset = 0;
    int i = 0;
    int ch = 0;
    enum AVSampleFormat sampleFormat = (enum AVSampleFormat)format;
    sampleSize = av_get_bytes_per_sample(sampleFormat);
    if (sampleSize < 0) {
        LOGE("Failed to calculate data size.");
        return -1;
    }
    audioDataSize = in->nb_samples * in->channels * sampleSize;
    int size = av_samples_get_buffer_size(NULL, av_get_channel_layout_nb_channels(AV_CH_LAYOUT_STEREO),
                                          in->nb_samples,
                                          sampleFormat, 0);
    *out_data = (uint8_t *) av_mallocz(audioDataSize);
    if (!(*out_data))return -1;

    if (av_sample_fmt_is_planar(sampleFormat)) {//如果是平面的
        for (i = 0; i < in->nb_samples; i++) {
            for (ch = 0; ch < in->channels; ch++) {
                memcpy((*out_data) + offset, in->data[ch] + sampleSize * i, sampleSize);
                offset += sampleSize;
            }
        }
    } else {
        memcpy(*out_data, in->data[0], audioDataSize);
    }
    return audioDataSize;
}


#endif //JNI_WEB_TOOLS_H
