#include "FfmpegInterface.h"

#include "Image.h"
#include "Video.h"
#include <iostream>

extern "C" {
#include <libavformat/avformat.h>
#include <libavutil/dict.h>
#include <libavutil/opt.h>
#include <libavutil/imgutils.h>
#include <libswscale/swscale.h>
}

std::vector<std::unique_ptr<Image> > FfmpegInterface::decodeToImages(const std::unique_ptr<Video>& video, unsigned maxImages)
{
    std::vector<std::unique_ptr<Image> > images;

    AVFormatContext* fmt_ctx{nullptr};

    auto ret = avformat_open_input(&fmt_ctx, video->GetPath().c_str(), nullptr, nullptr);
    if (ret)
    {
        return images;
    }

    //AVDictionaryEntry* tag{nullptr};
    //while ((tag = av_dict_get(fmt_ctx->metadata, "", tag, AV_DICT_IGNORE_SUFFIX)))
    //{
      //  std::cout << "key: " << tag->key << " value: " << tag->value << std::endl;
    //}

    ret = avformat_find_stream_info(fmt_ctx, nullptr);

    AVCodec* decoder;
    const auto bestStreamIdx = av_find_best_stream(fmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, &decoder, 0);

    //std::cout << "Best stream idx = " << std::to_string(bestStreamIdx) << std::endl;
    //std::cout << "Codec is: " << decoder->name << " |" << decoder->long_name << std::endl;

    AVCodecContext* decoder_context = avcodec_alloc_context3(decoder);

    avcodec_parameters_to_context(decoder_context, fmt_ctx->streams[bestStreamIdx]->codecpar);
    av_opt_set_int(decoder_context, "refcounted_frames", 1, 0);

    avcodec_open2(decoder_context, decoder, nullptr);

    AVPacket packet;
    AVFrame* frame = av_frame_alloc();
    AVFrame* scaled_frame = av_frame_alloc();

    int w = decoder_context->width;
    int h = decoder_context->height;

    scaled_frame->width = w;
    scaled_frame->height = h;
    scaled_frame->format = AV_PIX_FMT_RGB24;

    av_image_alloc(scaled_frame->data, scaled_frame->linesize, w, h, AV_PIX_FMT_RGB24, 32);


    auto img_convert_ctx = sws_getContext(w, h,
            decoder_context->pix_fmt,
                      w, h, AV_PIX_FMT_RGB24, SWS_BICUBIC,
                      NULL, NULL, NULL);


    for (unsigned idx=0; idx<100; idx++)
    {
        auto frame_ret = av_read_frame(fmt_ctx, &packet);
        if (frame_ret)
        {
            break;
        }

        if (packet.stream_index == bestStreamIdx)
        {
            avcodec_send_packet(decoder_context, &packet);
            ret = 0;
            while(ret >= 0)
            {
                ret = avcodec_receive_frame(decoder_context, frame);

                if (ret >= 0)
                {
                    frame->pts = frame->best_effort_timestamp;

                    sws_scale(img_convert_ctx, frame->data,
                             frame->linesize, 0,
                             decoder_context->height,
                             scaled_frame->data, scaled_frame->linesize);

                    auto image = Image::Create(frame->width, frame->height);
                    image->SetNumChannels(3);

                    auto bufferSize = av_image_get_buffer_size(AV_PIX_FMT_RGB24, frame->width, frame->height, 32);
                    std::vector<unsigned char> data(bufferSize, 0);

                    auto copyRet = av_image_copy_to_buffer(data.data(), bufferSize,
                            scaled_frame->data, scaled_frame->linesize,
                            AV_PIX_FMT_RGB24, frame->width, frame->height, 32);

                    image->SetData(data);
                    images.push_back(std::move(image));
                }
                av_frame_unref(frame);
                //av_frame_unref(scaled_frame);
            }
        }
        av_packet_unref(&packet);
    }

    avcodec_free_context(&decoder_context);
    avformat_close_input(&fmt_ctx);
    av_frame_free(&frame);

    return images;
}
