/********************************************************

	Main AVI player widget
	Copyright 2000 Eugene Kuznetsov (divx@euro.ru)

*********************************************************/

#include <default.h>

#include <qapp.h>
#include <qfiledialog.h>
#include <qmessagebox.h>
#include <qpopupmenu.h>
#include <qslider.h>
#include <qtimer.h>
#include "mywidget.h"
#include "MyConfigImpl.h"

#include <configfile.h>
#include <renderer.h>
#include <StreamInfo.h>

#ifdef USE_SDL
#include <SDL_keysym.h>
#undef KeyPress
#undef KeyRelease
#endif

#include <stdlib.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/types.h>
#include <cstdio>
#include <cctype>
#include <math.h> //rint
using namespace std;

extern MyWidget* m;

class QMyResizeEvent : public QEvent
{
public:
    int m_x, m_y;
    QMyResizeEvent(int x, int y) : QEvent(Q_MyResizeEvent), m_x(x), m_y(y) {}
};

void MyWidget::send_event(QEvent ev)
{
    QEvent* e = new QEvent(ev);
    qApp->postEvent(m, e);
    qApp->wakeUpGuiThread();
}

void MyWidget::send_resize_event(int x, int y)
{
    QEvent* e = new QMyResizeEvent(x, y);
    qApp->postEvent(m, e);
    qApp->wakeUpGuiThread();
}

void MyWidget::send_key_event(int qtkey, int qtascii, int qtstate)
{
    m_bKeyProcessed = true;

    QEvent* e = new QKeyEvent(QEvent::KeyPress, qtkey, qtascii, qtstate);
    qApp->postEvent(m, e);
    qApp->wakeUpGuiThread();
}

bool MyWidget::event(QEvent* e)
{
    m_bKeyProcessed = false;
    switch(e->type())
    {
    case Q_MenuEvent:
	menu_func();
	return true;
    case Q_PosEvent:
	updatePos();
	return true;
    case Q_DrawEvent:
	draw_slot();
	return true;
    case Q_MyCloseEvent:
	close(true);
	return true;
    case Q_MyRefreshEvent:
	Refresh();
	return true;
    case Q_MyResizeEvent:
	{
	    QMyResizeEvent* er = (QMyResizeEvent*) e;
	    Resize(er->m_x, er->m_y);
	}
	return true;
    case QEvent::KeyPress:
	keyPressEvent((QKeyEvent*)e);
        return true;
    case QEvent::Quit:
    case QEvent::Close: // Have to be checked here - for safe destruction
	delete player;
        player = 0;
	break;
    default:
        break;
    }

    return QWidget::event(e);
}

void MyWidget::key_func(int sym, int mod)
{
#ifdef USE_SDL
    if (player)
    {
	//cout << "Key " << sym << "  mod " << mod << "   " << m_bKeyProcessed << endl;
	if (m_bKeyProcessed)
	    return;
        // queue new key only if the previous one has been processed


	// SDL -> QT key translation
	struct keytr {
            int sdlkey;
	    int qtkey;
	    int qtascii;
	    int qtstate;
	} sdlqttable[] =
	{
	    { SDLK_RETURN, Qt::Key_Return },
	    { SDLK_LEFT, Qt::Key_Left },
	    { SDLK_PAGEDOWN, Qt::Key_PageUp },
	    { SDLK_RIGHT, Qt::Key_Right },
	    { SDLK_PAGEUP, Qt::Key_Right },
	    { SDLK_UP, Qt::Key_Up },
	    { SDLK_DOWN, Qt::Key_Down },
	    { SDLK_HOME, Qt::Key_Home },
	    { SDLK_END, Qt::Key_End },
	    { SDLK_KP_PLUS, Qt::Key_Plus, 0, Qt::Keypad },
	    { SDLK_KP_MINUS, Qt::Key_Minus, 0, Qt::Keypad },
	    { SDLK_1, Qt::Key_1, '1' },
	    { SDLK_2, Qt::Key_2, '2' },
	    { SDLK_3, Qt::Key_3, '3' },
	    { SDLK_LEFTBRACKET, Qt::Key_BraceLeft, '[' },
	    { SDLK_RIGHTBRACKET, Qt::Key_BraceRight, ']' },
	    { 0 }
	};

	int i = 0;
	while (sdlqttable[i].sdlkey && sdlqttable[i].sdlkey != sym)
            i++;

	if (sdlqttable[i].sdlkey)
	    send_key_event(sdlqttable[i].qtkey,
			   sdlqttable[i].qtascii,
			   sdlqttable[i].qtstate);
    }
#endif
}

void MyWidget::menu_func()
{
    if (!popup)
    {
	popup = new QPopupMenu();
	//popup->insertTearOffHandle();
	popup->insertItem("Zoom 2x    &3", this, SLOT(zoom_2x()));
	popup->insertItem("Zoom 1x    &2", this, SLOT(zoom_1x()));
	popup->insertItem("Zoom 0.5x  &1", this, SLOT(zoom_0_5x()));
	popup->insertSeparator();
	popup->insertItem("&Fullscreen", this, SLOT(fullscreen()), CTRL+Key_F );
	popup->insertItem("&Maximize", this, SLOT(maximize()));

	const vector<VideoRenderer*>& vvr = player->GetVideoRenderers();
	if (vvr.size() > 0)
	{
	    modes = new QPopupMenu();

	    connect(modes, SIGNAL(activated(int)),
		    this, SLOT(maximize_mode(int)));

	    unsigned i = 0;
	    while (i < vvr.size())
	    {
		const vector<VideoMode>& vvm = vvr[i]->GetVideoModes();
		unsigned j = 0;
		while (j < vvm.size())
		{
		    modes->insertItem(vvm[j].name.c_str(), (i << 8) + j);
		    j++;
		}
		i++;
	    }
	    popup->insertItem("Maximize Modes", modes);
	}
	popup->insertSeparator();
	popup->insertItem("Properties", this, SLOT(about_movie()));

	if (player->GetCodecInfo().decoder_info.size())
	{
	    popup->insertSeparator();
	    popup->insertItem("Decoder config", this, SLOT(decoder_config()));
	}
    }

    Window root_return, child_return;
    int root_x_return, root_y_return;
    int win_x_return, win_y_return;
    unsigned int mask_return;

    XQueryPointer(x11Display(), handle(), &root_return, &child_return,
		  &root_x_return, &root_y_return,
		  &win_x_return, &win_y_return, &mask_return);

    if (popup->isVisible())
        popup->hide();
    else
	popup->popup(QPoint(root_x_return, root_y_return));
}

void MyWidget::zoom(double scale)
{
    if (player)
    {
        if (scale == 2.0)
	    resize_count = 2;
	else if (scale == 1.0)
	    resize_count = 1;
        else
	    resize_count = 0;

	int w = (int) (scale * player->GetWidth() + 0.5);
	int h = (int) (scale * player->GetHeight() + 0.5);

	player->Resize(w,h);
    }
}

void MyWidget::zoomWH(int new_w, int new_h, int ori_w, int ori_h)
{
    //cout << "zoomWH " << new_w << "   " << new_h << endl;

    if (ori_w > ori_h)
	new_h = (int) (new_w * ori_h / (double) ori_w + 0.5);
    else
	new_w = (int) (new_h * ori_w / (double) ori_h + 0.5);
    player->Resize(new_w, new_h);
    fullscreen();
}

void MyWidget::send_fullscreen()
{
    send_key_event(Qt::Key_F, 'F');
}

void MyWidget::fullscreen()
{
    if (player)
	player->ToggleFullscreen(false);
}

void MyWidget::send_maximize()
{
    send_key_event(Qt::Key_M, 'M');
}

void MyWidget::maximize()
{
    if (player)
	player->ToggleFullscreen(true);
}

void MyWidget::maximize_mode(int renderer_mode)
{
    //cout << "maximize " << i << endl;
    const vector<VideoRenderer*>& vvr = player->GetVideoRenderers();
    unsigned renderer = (renderer_mode >> 8);
    if (renderer < vvr.size())
    {
        unsigned mode = renderer_mode & 0xff;
	const vector<VideoMode>& vvm = vvr[renderer]->GetVideoModes();
	if (mode < vvm.size())
	{
	    int ori_w, ori_h;
	    int new_w = vvm[mode].width;
	    int new_h = vvm[mode].height;

	    if (vvr[renderer]->GetSize(ori_w, ori_h) < 0)
	    {
		ori_w = player->GetWidth();
		ori_h = player->GetHeight();
	    }
	    //else cout << " w " << ori_w << "   h " << ori_h << endl;
	    zoomWH(new_w, new_h, ori_w, ori_h);
	}
    }
}

void MyWidget::showconf_func()
{
    MyConfigDialog_impl dilg;
    dilg.exec();
    readConfig();
}

// this method should be used for all volume changes
// for common interpretation
void MyWidget::volume_func(int vpos)
{
    if (player)
	player->Set(IAviPlayer::AUDIO_VOLUME,
		    int(1000.0 * vpos * vpos /float(MAX_VBAR * MAX_VBAR)),
		    0);
}

void MyWidget::mute_func()
{
    int p = vbar->value();
    int n = vollast;
    if (p != 0) {
	vollast = p;
        n = 0;
    }
    vbar->setValue(n);
}


void MyWidget::pause_func()
{
    if (player && player->IsPlaying())
	player->Pause(!player->IsPaused());
}

void MyWidget::paintEvent(QPaintEvent *)
{
    if (player && !player->IsStopped())
	updatePos();
}

void MyWidget::rcreseek(double spos)
{
    if (player)
    {
	scroller->setValue((int)spos);
	player->ReseekExact(spos);
	updatePos();
    }
}

// static  - thus using m - pointer to MyWidget
void MyWidget::stopfunc(int)
{
    m->scroller->setValue(0);
}

void MyWidget::stop_func()
{
    if (player)
    {
	player->Stop();
	scroller->setValue(0);
    }
}

void MyWidget::changed_func(int cpos)
{
    if (player)
    {
	//cout << is_tracking << "diff " << last_slider - cpos << "  " << cpos << "  " << last_slider << endl;

        bool wrapAround = cpos < scroller->minValue() + 2
	    && last_slider > scroller->maxValue() - 2;

	// when paused or when mouse wheel movement...
	int minchange = (player->IsPaused()) ? 0 : 2;
	if (!m_bSettingScroller
            && (!is_tracking || player->IsPaused())
	    && !wrapAround && abs(last_slider - cpos) > minchange)
	{
	    player->ReseekExact(cpos);
	}
	last_slider = cpos;
    }
}

void MyWidget::nextpage_func()
{
    if (player && player->PageUp() == 0)
	updatePos();
}

void MyWidget::prevpage_func()
{
    if (player && player->PageDown() == 0)
	updatePos();
}

void MyWidget::play_func()
{
    if (player)
    {
	if (!player->IsPlaying() && !player->IsPaused())
	    player->Start();
	else
	    player->Play();
    }
}

void MyWidget::open_func()
{
    QString open_qs;
    string path = Registry::ReadString("aviplay", "file", ".");
    open_qs = QFileDialog::getOpenFileName(path.c_str(), "AVI files (*.avi *.AVI);;ASF files (*.asf *.ASF);;All files(*)");
//    open_qs=QFileDialog::getOpenFileName(QString::null,
//	 "AVI files (*.avi *.AVI);;ASF files (*.asf *.ASF);;All files (*.*)");
    if (open_qs.isNull())
	return;
    try
    {
	AviPlayerInitParams apip;
	apip.filename = open_qs.ascii();
	    //open_qs.local8Bit();
	initPlayer(apip);
	Registry::WriteString("aviplay", "file", open_qs.ascii());
    }
    catch (FatalError& error)
    {
	QMessageBox::information(this,
				 QString("Error in ") +
				 QString(error.GetModule()) +
				 QString(" module"),
				 error.GetDesc(), QMessageBox::Ok);
	error.PrintAll();
	repaint();
    }
}

void MyWidget::opensub_func()
{
    if (!player)
        return;

    QString open_qs;
    string path = Registry::ReadString("aviplay", "subtitlefile", ".");
    open_qs = QFileDialog::getOpenFileName(path.c_str(), "SUB files (*.sub *.SUB);;SRT files (*.srt *.SRT);;All files(*)");

    if (open_qs.isNull())
	return;
    try
    {
	player->InitSubtitles(open_qs.local8Bit());
	Registry::WriteString("aviplay", "subtitlefile", open_qs.ascii());
    }
    catch (FatalError& error)
    {
	QMessageBox::information(this,
				 QString("Error in ") +
				 QString(error.GetModule()) +
				 QString(" module"),
				 error.GetDesc(), QMessageBox::Ok);
	error.PrintAll();
	repaint();
    }
}

#define __MODULE__ "AVI player widget"

void MyWidget::keyPressEvent(QKeyEvent * kevent)
{
    // well-known xmms key events: 'x' stop, 'c' pause, 'v' play.
    // for SDL events are also mirrored in EventFilter ( see renderer.cpp )
    //cout << "keyPresss: "  << kevent->key() << "  acsii: "  << kevent->ascii() << endl;

    switch(toupper(kevent->ascii()))
    {
    case 'Q':
	send_event(Q_MyCloseEvent);
	return;
    case 'X':
	stop_func();
	return;
    case 'C':
    case 'P':
    case ' ':
        // try to support most natural keystrokes
	pause_func();
	return;
    case '[':
	if (player)
	{
            double a = rint((player->GetAsync() - 0.1) * 10.0) / 10.0;
	    //player->SetAsync(int(a * 10 + 1) / 10.0);
	    player->SetAsync(a);
	    cout << "Set async " << a << endl;
	}
	return;
    case ']':
	if (player)
	{
            double a = rint((player->GetAsync() + 0.1) * 10.0) / 10.0;
	    //player->SetAsync(int(a * 10 - 1) / 10.0);
	    player->SetAsync(a);
            cout << "Set async " << a << endl;
	}
	return;
    case 'V':
	play_func();
	return;
    case 'F':
	fullscreen();
	return;
    case 'M':
	maximize();
	return;
    case '1':
	zoom(0.5);
	return;
    case '2':
	zoom(1.0);
	return;
    case '3':
	zoom(2.0);
	return;
    }

    switch(kevent->key())
    {
    case Qt::Key_Return:
	player->NextFrame();
	return;
    case Qt::Key_Plus:
        vbar->addStep();
	return;
    case Qt::Key_Minus:
        vbar->subtractStep();
	return;
    case Qt::Key_PageUp:
    case Qt::Key_Left:
	player->PageDown();
	return;
    case Qt::Key_PageDown:
    case Qt::Key_Right:
	player->PageUp();
	return;
    case Qt::Key_Home:
	player->ReseekExact(0);
	return;
    case Qt::Key_End:
	player->ReseekExact(player->GetLengthTime() - 1.);
	return;
    case Qt::Key_Up:
	{
	    double o = player->GetPos();
	    double p = o + 60.;
	    double len = player->GetLengthTime();
	    if (p > len)
	    {
		// we are at the end - try 60 seconds before end
		p = len - 60.;
		if (p < 0)
		    // to short
		    p = 0.;
	    }
	    player->ReseekExact(p);
	    if (player->GetPos() < o)
		// some movies have very long intervals between KeyFrames
		// so always skip at least to the next keyframe
		player->PageUp();
	}
	return;
    case Qt::Key_Down:
	{
	    double p = player->GetPos() - 60.;
	    if (p < 0)
		p = 0.;
	    player->ReseekExact(p);
	}
	return;
    }
}
void MyWidget::mouseDoubleClickEvent(QMouseEvent *)
{
    showconf_func();
}

void MyWidget::resizeEvent(QResizeEvent* ev)
{
}
void MyWidget::mouseReleaseEvent( QMouseEvent * e)
{
    return QWidget::mouseReleaseEvent(e);
}

void MyWidget::about_func()
{
    if (!about_popup)
    {
	about_popup = new QPopupMenu();
	about_popup->insertItem("Usage", this, SLOT(help()));
	about_popup->insertItem("About", this, SLOT(about()));
	about_popup->insertItem("Config", this, SLOT(showconf_func()));

    }
    Window root_return, child_return;
    int root_x_return, root_y_return;
    int win_x_return, win_y_return;
    unsigned int mask_return;

    XQueryPointer(x11Display(), handle(), &root_return, &child_return, &
		  root_x_return, &root_y_return,
		  &win_x_return, &win_y_return, &mask_return);

    about_popup->popup(QPoint(root_x_return, root_y_return));
}

void MyWidget::Resize(int w, int h)
{
    if (player)
	player->Resize(w, h);
}

void MyWidget::Refresh()
{
    if (player)
	player->Refresh();
}

void MyWidget::remote_command()
{
    char command[32];
    int command_pos = 0;
    static long oldpos;
    if (! m)
	return;
    long newpos = (long) m->GetPos();
    fd_set fds; // stdin changed?
    struct timeval tv; // how long to w8
    int n; // indicating whether something happened on stdin

    if (m->isStopped())
	send_event(Q_MyCloseEvent);
    if (newpos != oldpos) {
	oldpos = newpos;
	printf ("Pos: %li s / %li s\n", (long) m->GetPos(),
		(long) m->GetPlayer()->GetLengthTime());
    }
    tv.tv_sec = 0;
    tv.tv_usec = 50000;
    FD_ZERO(&fds);
    FD_SET(STDIN_FILENO, &fds);
    if ((n = select(32, &fds, NULL, NULL, &tv)) != 0)
    {
	double cpos;
	int i;
	int complete = 0;

 	while (command_pos < 32 &&
	    (i = read (STDIN_FILENO, command + command_pos, 1)) > 0 &&
	    command[command_pos] != '\r' &&
	    command[command_pos] != '\n')
	    command_pos++;

	if (command_pos == 31 || command[command_pos] == '\r' ||
	    command[command_pos] == '\n') {
	    command[command_pos] = 0;
	    complete = 1;
	}
	if (complete == 1) {
	    if (command[0] == 'p' || command[0] == 'P')
		pause_func();
	    else if (command[0] == 'f' || command[0] == 'F')
		fullscreen();
	    else if (command[0] == 'm' || command[0] == ';')
		maximize();
	    else if (command[0] == 's' || command[0] == 'S')
		stop_func();
	    else if (command[0] == 'q' || command[0] == 'Q')
		send_event(Q_MyCloseEvent);
	    else if (command[0] == 'r' || command[0] == 'R') {
		for (i = 1; i < 32 && command[i] >= '0' && command[i] <= '9'; ++i)
		    ;
		command[i] = 0;
		cpos = atof (command + 1);
		rcreseek(cpos);
	    } else
		printf ("Unknown command: %s\n", command);
	    command_pos = 0;
	    complete = 0;
	}
	return;
    }
}

void MyWidget::about_movie()
{
    if (!player)
	return;

    char t[128];
    string s="File ";
    s+=player->GetFileName();
    s+="\n\n";
    if (player->GetAudioLengthTime() > 0)
    {
	sprintf(t, ", %.2f seconds.\n", player->GetAudioLengthTime());
	s+="Audio stream: ";
	s+=player->GetAudioFormat();
	s+=t;
    }
    if (player->GetVideoLengthTime() > 0)
    {
        StreamInfo* info = player->GetVideoStreamInfo();
        double bps = 0;;
	if (info)
	{
	    cout << "Stream info:" << endl << info;
	    bps = info->GetBps();
            delete info;
	}
	sprintf(t, "Video stream: %dx%d, %.2f fps, %.2fkbit/s (%.2fkB/s)\n",
		player->GetWidth(), player->GetHeight(), player->GetFps(),
		bps / 128.0, bps / 1024.0);
	s+=t;
	s+=player->GetVideoFormat();
	sprintf(t, ", %.2f seconds.\n", player->GetVideoLengthTime());
	s+=t;
    }
//    QMessageBox::information(this, "About file", s.c_str());
    QMessageBox *mb = new QMessageBox( "About file", s.c_str(), 
				       QMessageBox::Information,
				       QMessageBox::Ok, QMessageBox::NoButton, QMessageBox::NoButton,
				       this, "information" );
//    mb->setTextFormat(Qt::RichText);
    int reply = mb->exec();
    delete mb;
}

void MyWidget::middle_button()
{
    int qtkey = 0, qtascii = 0;

    switch((resize_count + 1) % 3)
    {
    case 0:
        // zoom 50%
	qtkey = Qt::Key_1;
	qtascii = '1';
	break;
    case 1:
        // zoom 100%
	qtkey = Qt::Key_2;
	qtascii = '2';
	break;
    case 2:
        // zoom 200%
	qtkey = Qt::Key_3;
	qtascii = '3';
	break;
    }

    send_key_event(qtkey, qtascii);

    m_bKeyProcessed = false;
    // intentionaly setting false here !
    // this is like sync point - this will
    // reset keyboard processed status
    // just in case something wrong happened before
}

#include "mywidget.moc"
