#include "videoplay.h"

#include "confile.h"

#include "htime.h"


#define DEFAULT_RETRY_INTERVAL 10000 // ms
#define DEFAULT_RETRY_MAXCNT 6

static renderer_type_e renderer_type_enum(const char* str)
{
    if (stricmp(str, "opengl") == 0)
    {
        return RENDERER_TYPE_OPENGL;
    }
    else if (stricmp(str, "sdl") == 0 || stricmp(str, "sdl2") == 0)
    {
        return RENDERER_TYPE_SDL2;
    }
    /*
    else if (stricmp(str, "d3d") == 0) {
        return RENDERER_TYPE_D3D;
    }
    */
    return DEFAULT_RENDERER_TYPE;
}

VideoPlay::VideoPlay()
{
    playerid = 0;
    status = STOP;
    pImpl_player = NULL;
    fps = g_confile->Get<int>("fps", "video");
    // aspect_ratio
    string str = g_confile->GetValue("aspect_ratio", "video");
    const char* c_str = str.c_str();
    aspect_ratio.type = ASPECT_FULL;
    if (str.empty() || strcmp(c_str, "100%") == 0)
    {
        aspect_ratio.type = ASPECT_FULL;
    }
    else if (stricmp(c_str, "w:h") == 0)
    {
        aspect_ratio.type = ASPECT_ORIGINAL_RATIO;
    }
    else if (stricmp(c_str, "wxh") == 0 || stricmp(c_str, "w*h") == 0)
    {
        aspect_ratio.type = ASPECT_ORIGINAL_SIZE;
    }
    else if (strchr(c_str, '%'))
    {
        int percent = 0;
        sscanf(c_str, "%d%%", &percent);
        if (percent)
        {
            aspect_ratio.type = ASPECT_PERCENT;
            aspect_ratio.w = percent;
            aspect_ratio.h = percent;
        }
    }
    else if (strchr(c_str, ':'))
    {
        int w = 0;
        int h = 0;
        sscanf(c_str, "%d:%d", &w, &h);
        if (w && h)
        {
            aspect_ratio.type = ASPECT_CUSTOM_RATIO;
            aspect_ratio.w = w;
            aspect_ratio.h = h;
        }
    }
    else if (strchr(c_str, 'x'))
    {
        int w = 0;
        int h = 0;
        sscanf(c_str, "%dx%d", &w, &h);
        if (w && h)
        {
            aspect_ratio.type = ASPECT_CUSTOM_SIZE;
            aspect_ratio.w = w;
            aspect_ratio.h = h;
        }
    }
    else if (strchr(c_str, 'X'))
    {
        int w = 0;
        int h = 0;
        sscanf(c_str, "%dX%d", &w, &h);
        if (w && h)
        {
            aspect_ratio.type = ASPECT_CUSTOM_SIZE;
            aspect_ratio.w = w;
            aspect_ratio.h = h;
        }
    }
    else if (strchr(c_str, '*'))
    {
        int w = 0;
        int h = 0;
        sscanf(c_str, "%d*%d", &w, &h);
        if (w && h)
        {
            aspect_ratio.type = ASPECT_CUSTOM_SIZE;
            aspect_ratio.w = w;
            aspect_ratio.h = h;
        }
    }
    else
    {
        aspect_ratio.type = ASPECT_FULL;
    }
    //printf("aspect_ratio type=%d w=%d h=%d\n", aspect_ratio.type, aspect_ratio.w, aspect_ratio.h);
    // renderer
    str = g_confile->GetValue("renderer", "video");
    if (str.empty())
    {
        renderer_type = RENDERER_TYPE_OPENGL;
    }
    else
    {
        renderer_type = renderer_type_enum(str.c_str());
    }
    // retry
    retry_interval = g_confile->Get<int>("retry_interval", "video", DEFAULT_RETRY_INTERVAL);
    retry_maxcnt = g_confile->Get<int>("retry_maxcnt", "video", DEFAULT_RETRY_MAXCNT);
    last_retry_time = 0;
    retry_cnt = 0;

    width = 700;
    height = 400;
}

VideoPlay::~VideoPlay()
{
    std::cout << "~HVideoWidget" << std::endl;
    close();
}


void VideoPlay::open(HMedia& media)
{
    this->media = media;
    start();
}

void VideoPlay::close()
{
    stop();
    this->media.type = MEDIA_TYPE_NONE;
    title = "";
}

void VideoPlay::start()
{
    if (media.type == MEDIA_TYPE_NONE)
    {
        std::cout << "Please first set media source, then start." << std::endl;
        return;
    }

    if (!pImpl_player)
    {
        pImpl_player = HVideoPlayerFactory::create(media.type);
        pImpl_player->set_media(media);
        //pImpl_player->set_event_callback(hplayer_event_callback, this);
        title = media.src.c_str();
        int ret = pImpl_player->start();
        if (ret != 0)
        {
            onOpenFailed();
        }
        else
        {
            onOpenSucceed();
        }
    }
    else
    {
        if (status == PAUSE)
        {
            resume();
        }
    }
}

void VideoPlay::stop()
{
    //timer->stop();

    if (pImpl_player)
    {
        pImpl_player->stop();
        SAFE_DELETE(pImpl_player);
    }

    last_frame.buf.cleanup();
    //videownd->update();
    status = STOP;

    last_retry_time = 0;
    retry_cnt = 0;

}

void VideoPlay::pause()
{
    if (pImpl_player)
    {
        pImpl_player->pause();
    }
    //timer->stop();
    status = PAUSE;

}

void VideoPlay::resume()
{
    if (status == PAUSE && pImpl_player)
    {
        pImpl_player->resume();
        //timer->start(1000 / (fps ? fps : pImpl_player->fps));
        status = PLAY;

    }
}

void VideoPlay::restart()
{
    std::cout << "restart..." << std::endl;
    if (pImpl_player)
    {
        pImpl_player->stop();
        pImpl_player->start();
    }
    else
    {
        start();
    }
}

void VideoPlay::retry()
{
    if (retry_maxcnt < 0 || retry_cnt < retry_maxcnt)
    {
        ++retry_cnt;
        int64_t cur_time = gettimeofday_ms();
        int64_t timespan = cur_time - last_retry_time;
        if (timespan >= retry_interval)
        {
            last_retry_time = cur_time;
            restart();
        }
        else
        {
            last_retry_time += retry_interval;
            if (pImpl_player)
            {
                pImpl_player->stop();
            }
            int retry_after = retry_interval - timespan;
            std::cout << "retry after " << retry_after << "ms" << std::endl;
            //QTimer::singleShot(retry_after, this, SLOT(restart()));
        }
    }
    else
    {
        stop();
    }
}

void VideoPlay::onOpenSucceed()
{
    //timer->start(1000 / (fps ? fps : pImpl_player->fps));
    status = PLAY;
    setAspectRatio(aspect_ratio);
    if (pImpl_player->duration > 0)
    {        
        int duration_sec = pImpl_player->duration / 1000;
        char szTime[16];
        duration_fmt(duration_sec, szTime);
        //toolbar->lblDuration->setText(szTime);
        //toolbar->sldProgress->setRange(0, duration_sec);
        //toolbar->lblDuration->show();
        //toolbar->sldProgress->show();
    }

    if (retry_cnt != 0)
    {
        printf("retry succeed: cnt=%d media.src=%s", retry_cnt, media.src.c_str());
    }
}

void VideoPlay::onOpenFailed()
{
    if (retry_cnt == 0)
    {
        std::cerr << "Could not open media: " << pImpl_player->error << std::endl;
        stop();
    }
    else
    {
        printf("retry failed: cnt=%d media.src=%s", retry_cnt, media.src.c_str());
        retry();
    }
}

void VideoPlay::onPlayerEOF()
{
    switch (media.type)
    {
    case MEDIA_TYPE_NETWORK:
        retry();
        break;
    case MEDIA_TYPE_FILE:
        if (g_confile->Get<bool>("loop_playback", "video"))
        {
            restart();
        }
        else
        {
            stop();
        }
        break;
    default:
        stop();
        break;
    }
}

void VideoPlay::onPlayerError()
{
    switch (media.type)
    {
    case MEDIA_TYPE_NETWORK:
        retry();
        break;
    default:
        stop();
        break;
    }
}

void VideoPlay::onTimerUpdate()
{
    if (pImpl_player == NULL)
        return;
    static int _i = 0;
    if (pImpl_player->pop_frame(&last_frame) == 0)
    {
        // update progress bar
        //if (toolbar->sldProgress->isVisible())
        //{
        //    int progress = (last_frame.ts - pImpl_player->start_time) / 1000;
        //    if (toolbar->sldProgress->value() != progress &&
        //        !toolbar->sldProgress->isSliderDown())
        //    {
        //        toolbar->sldProgress->setValue(progress);
        //    }
        //}
        // update video frame
        //videownd->update();
    }
}

void VideoPlay::setAspectRatio(aspect_ratio_t ar)
{
    aspect_ratio = ar;
    int border = 1;
    int scr_w = width - border * 2;
    int scr_h = height - border * 2;
    if (scr_w <= 0 || scr_h <= 0)
        return;
    int pic_w = 0;
    int pic_h = 0;
    if (pImpl_player && status != STOP)
    {
        pic_w = pImpl_player->width;
        pic_h = pImpl_player->height;
    }
    if (pic_w == 0)
        pic_w = scr_w;
    if (pic_h == 0)
        pic_h = scr_h;
    // calc videownd rect
    int dst_w, dst_h;
    switch (ar.type)
    {
    case ASPECT_FULL:
        dst_w = scr_w;
        dst_h = scr_h;
        break;
    case ASPECT_PERCENT:
        dst_w = pic_w * ar.w / 100;
        dst_h = pic_h * ar.h / 100;
        break;
    case ASPECT_ORIGINAL_SIZE:
        dst_w = pic_w;
        dst_h = pic_h;
        break;
    case ASPECT_CUSTOM_SIZE:
        dst_w = ar.w;
        dst_h = ar.h;
        break;
    case ASPECT_ORIGINAL_RATIO:
    case ASPECT_CUSTOM_RATIO:
    {
        double scr_ratio = (double)scr_w / (double)scr_h;
        double dst_ratio = 1.0;
        if (ar.type == ASPECT_CUSTOM_RATIO)
        {
            dst_ratio = (double)ar.w / (double)ar.h;
        }
        else
        {
            dst_ratio = (double)pic_w / (double)pic_h;
        }
        if (dst_ratio > scr_ratio)
        {
            dst_w = scr_w;
            dst_h = scr_w / dst_ratio;
        }
        else
        {
            dst_h = scr_h;
            dst_w = scr_h * dst_ratio;
        }
    }
    break;
    }
    dst_w = MIN(dst_w, scr_w);
    dst_h = MIN(dst_h, scr_h);
    // align 4
    dst_w = dst_w >> 2 << 2;
    dst_h = dst_h >> 2 << 2;

    int x = border + (scr_w - dst_w) / 2;
    int y = border + (scr_h - dst_h) / 2;
    // videownd->setGeometry(QRect(x, y, dst_w, dst_h));
}
