#include <CtrlLib/CtrlLib.h>

using namespace Upp;

#include "MediaFile.h"
#include "MediaPlayer.h"
#include "utility.h"
#include "mediaplayer_in.h"


static inline void fill_rectangle(SDL_Surface *screen,
                                  int x, int y, int w, int h, int color)
{
    SDL_Rect rect;
    rect.x = x;
    rect.y = y;
    rect.w = w;
    rect.h = h;
    SDL_FillRect(screen, &rect, color);
}


#define BPP 1

static void blend_subrect(AVPicture *dst, const AVSubtitleRect *rect, int imgw, int imgh)
{
    int wrap, wrap3, width2, skip2;
    int y, u, v, a, u1, v1, a1, w, h;
    uint8_t *lum, *cb, *cr;
    const uint8_t *p;
    const uint32_t *pal;
    int dstx, dsty, dstw, dsth;

    dstw = av_clip(rect->w, 0, imgw);
    dsth = av_clip(rect->h, 0, imgh);
    dstx = av_clip(rect->x, 0, imgw - dstw);
    dsty = av_clip(rect->y, 0, imgh - dsth);
    lum = dst->data[0] + dsty * dst->linesize[0];
    cb = dst->data[1] + (dsty >> 1) * dst->linesize[1];
    cr = dst->data[2] + (dsty >> 1) * dst->linesize[2];

    width2 = ((dstw + 1) >> 1) + (dstx & ~dstw & 1);
    skip2 = dstx >> 1;
    wrap = dst->linesize[0];
    wrap3 = rect->pict.linesize[0];
    p = rect->pict.data[0];
    pal = (const uint32_t *)rect->pict.data[1];  /* Now in YCrCb! */

    if (dsty & 1) {
        lum += dstx;
        cb += skip2;
        cr += skip2;

        if (dstx & 1) {
            YUVA_IN(y, u, v, a, p, pal);
            lum[0] = ALPHA_BLEND(a, lum[0], y, 0);
            cb[0] = ALPHA_BLEND(a >> 2, cb[0], u, 0);
            cr[0] = ALPHA_BLEND(a >> 2, cr[0], v, 0);
            cb++;
            cr++;
            lum++;
            p += BPP;
        }
        for(w = dstw - (dstx & 1); w >= 2; w -= 2) {
            YUVA_IN(y, u, v, a, p, pal);
            u1 = u;
            v1 = v;
            a1 = a;
            lum[0] = ALPHA_BLEND(a, lum[0], y, 0);

            YUVA_IN(y, u, v, a, p + BPP, pal);
            u1 += u;
            v1 += v;
            a1 += a;
            lum[1] = ALPHA_BLEND(a, lum[1], y, 0);
            cb[0] = ALPHA_BLEND(a1 >> 2, cb[0], u1, 1);
            cr[0] = ALPHA_BLEND(a1 >> 2, cr[0], v1, 1);
            cb++;
            cr++;
            p += 2 * BPP;
            lum += 2;
        }
        if (w) {
            YUVA_IN(y, u, v, a, p, pal);
            lum[0] = ALPHA_BLEND(a, lum[0], y, 0);
            cb[0] = ALPHA_BLEND(a >> 2, cb[0], u, 0);
            cr[0] = ALPHA_BLEND(a >> 2, cr[0], v, 0);
            p++;
            lum++;
        }
        p += wrap3 - dstw * BPP;
        lum += wrap - dstw - dstx;
        cb += dst->linesize[1] - width2 - skip2;
        cr += dst->linesize[2] - width2 - skip2;
    }
    for(h = dsth - (dsty & 1); h >= 2; h -= 2) {
        lum += dstx;
        cb += skip2;
        cr += skip2;

        if (dstx & 1) {
            YUVA_IN(y, u, v, a, p, pal);
            u1 = u;
            v1 = v;
            a1 = a;
            lum[0] = ALPHA_BLEND(a, lum[0], y, 0);
            p += wrap3;
            lum += wrap;
            YUVA_IN(y, u, v, a, p, pal);
            u1 += u;
            v1 += v;
            a1 += a;
            lum[0] = ALPHA_BLEND(a, lum[0], y, 0);
            cb[0] = ALPHA_BLEND(a1 >> 2, cb[0], u1, 1);
            cr[0] = ALPHA_BLEND(a1 >> 2, cr[0], v1, 1);
            cb++;
            cr++;
            p += -wrap3 + BPP;
            lum += -wrap + 1;
        }
        for(w = dstw - (dstx & 1); w >= 2; w -= 2) {
            YUVA_IN(y, u, v, a, p, pal);
            u1 = u;
            v1 = v;
            a1 = a;
            lum[0] = ALPHA_BLEND(a, lum[0], y, 0);

            YUVA_IN(y, u, v, a, p + BPP, pal);
            u1 += u;
            v1 += v;
            a1 += a;
            lum[1] = ALPHA_BLEND(a, lum[1], y, 0);
            p += wrap3;
            lum += wrap;

            YUVA_IN(y, u, v, a, p, pal);
            u1 += u;
            v1 += v;
            a1 += a;
            lum[0] = ALPHA_BLEND(a, lum[0], y, 0);

            YUVA_IN(y, u, v, a, p + BPP, pal);
            u1 += u;
            v1 += v;
            a1 += a;
            lum[1] = ALPHA_BLEND(a, lum[1], y, 0);

            cb[0] = ALPHA_BLEND(a1 >> 2, cb[0], u1, 2);
            cr[0] = ALPHA_BLEND(a1 >> 2, cr[0], v1, 2);

            cb++;
            cr++;
            p += -wrap3 + 2 * BPP;
            lum += -wrap + 2;
        }
        if (w) {
            YUVA_IN(y, u, v, a, p, pal);
            u1 = u;
            v1 = v;
            a1 = a;
            lum[0] = ALPHA_BLEND(a, lum[0], y, 0);
            p += wrap3;
            lum += wrap;
            YUVA_IN(y, u, v, a, p, pal);
            u1 += u;
            v1 += v;
            a1 += a;
            lum[0] = ALPHA_BLEND(a, lum[0], y, 0);
            cb[0] = ALPHA_BLEND(a1 >> 2, cb[0], u1, 1);
            cr[0] = ALPHA_BLEND(a1 >> 2, cr[0], v1, 1);
            cb++;
            cr++;
            p += -wrap3 + BPP;
            lum += -wrap + 1;
        }
        p += wrap3 + (wrap3 - dstw * BPP);
        lum += wrap + (wrap - dstw - dstx);
        cb += dst->linesize[1] - width2 - skip2;
        cr += dst->linesize[2] - width2 - skip2;
    }
    /* handle odd height */
    if (h) {
        lum += dstx;
        cb += skip2;
        cr += skip2;

        if (dstx & 1) {
            YUVA_IN(y, u, v, a, p, pal);
            lum[0] = ALPHA_BLEND(a, lum[0], y, 0);
            cb[0] = ALPHA_BLEND(a >> 2, cb[0], u, 0);
            cr[0] = ALPHA_BLEND(a >> 2, cr[0], v, 0);
            cb++;
            cr++;
            lum++;
            p += BPP;
        }
        for(w = dstw - (dstx & 1); w >= 2; w -= 2) {
            YUVA_IN(y, u, v, a, p, pal);
            u1 = u;
            v1 = v;
            a1 = a;
            lum[0] = ALPHA_BLEND(a, lum[0], y, 0);

            YUVA_IN(y, u, v, a, p + BPP, pal);
            u1 += u;
            v1 += v;
            a1 += a;
            lum[1] = ALPHA_BLEND(a, lum[1], y, 0);
            cb[0] = ALPHA_BLEND(a1 >> 2, cb[0], u, 1);
            cr[0] = ALPHA_BLEND(a1 >> 2, cr[0], v, 1);
            cb++;
            cr++;
            p += 2 * BPP;
            lum += 2;
        }
        if (w) {
            YUVA_IN(y, u, v, a, p, pal);
            lum[0] = ALPHA_BLEND(a, lum[0], y, 0);
            cb[0] = ALPHA_BLEND(a >> 2, cb[0], u, 0);
            cr[0] = ALPHA_BLEND(a >> 2, cr[0], v, 0);
        }
    }
}

static void free_subpicture(SubPicture *sp) 
{
    avsubtitle_free(&sp->sub);
}

void MediaPlayer::video_image_display()
{
    VideoPicture &vp = pictq[pictq_rindex];
    if (!vp.overlay && !vp.surface) 
        return;
    if (subtitleStream) {
        if (subpq_size > 0) {
            SubPicture *sp = &subpq[subpq_rindex];

            if (vp.pts >= sp->pts + (float(sp->sub.start_display_time)/1000.)) {
                SDL_LockYUVOverlay(vp.overlay);
				
				AVPicture pict;
                pict.data[0] = vp.overlay->pixels[0];
                pict.data[1] = vp.overlay->pixels[2];
                pict.data[2] = vp.overlay->pixels[1];

                pict.linesize[0] = vp.overlay->pitches[0];
                pict.linesize[1] = vp.overlay->pitches[2];
                pict.linesize[2] = vp.overlay->pitches[1];

                for (unsigned i = 0; i < sp->sub.num_rects; i++)
                    blend_subrect(&pict, sp->sub.rects[i], vp.overlay->w, vp.overlay->h);

                SDL_UnlockYUVOverlay(vp.overlay);
            }
        }
    }
    // We suppose the screen has a 1.0 pixel ratio 
	SDL_Rect rect;
	INTERLOCKED_(mtx) {
		rect.x = imgRect.left;
		rect.y = imgRect.top;
		rect.w = imgRect.GetWidth();
		rect.h = imgRect.GetHeight();
	}	
	if (vp.overlay)
    	SDL_DisplayYUVOverlay(vp.overlay, &rect);
	else if (vp.surface) {
		if (WhenFrame) {
			SDLSurface srf(vp.surface);
			WhenFrame(srf);
		}
		SDL_BlitSurface(vp.surface, 0, screen, &rect);
		SDL_Flip(screen);
	}
}

void MediaPlayer::Layout() {
	SDLCtrl::Layout();
	
	if (!screen)
		return;
	INTERLOCKED_(mtx) {
		if (forceAspect) {			// Fit image to screen considering aspect ratio
			int cx = screen->w;
			int cy = screen->h;
			double frameAspect  = cx/double(cy); 
			if (frameAspect > aspect_ratio) {	
				double x = (cx - aspect_ratio*cy)/2.;
				imgRect.Set(int(x), 0, int(x + aspect_ratio*cy), cy);
			} else {
				double y = (cy - cx/aspect_ratio)/2.;
				imgRect.Set(0, int(y), cx, int(y + cx/aspect_ratio));
			}
		} else 
			imgRect.Set(0, 0, screen->w, screen->h);
	}
}

// Allocate a picture (needs to do that in main thread to avoid potential locking problems)
bool MediaPlayer::alloc_picture()
{
    VideoPicture &vp = pictq[pictq_windex];

    if (vp.overlay)
        SDL_FreeYUVOverlay(vp.overlay);
    else if (vp.surface)
		SDL_FreeSurface(vp.surface);
    
	if (videoStream) {
	    vp.width   = videoStream->codec->width;
	    vp.height  = videoStream->codec->height;
	    vp.pix_fmt = videoStream->codec->pix_fmt;
	} else {
		vp.width   = screen->w;
	    vp.height  = screen->h;
	    vp.pix_fmt = PIX_FMT_YUV420P;
	}
	if (rgb) {
		vp.surface = SDL_CreateRGBSurface(0, vp.width, vp.height, 24, 0x0000FF, 0x00FF00, 0xFF0000, 0);
		vp.overlay = 0;
	} else {
    	vp.overlay = SDL_CreateYUVOverlay(vp.width, vp.height, SDL_YV12_OVERLAY, screen);
    	vp.surface = 0;
	}
 
    if ((rgb && !vp.surface) ||
    	(!rgb && (!vp.overlay || vp.overlay->pitches[0] < vp.width))) {	
        SDLCtrl::SetError(Format("Error: the video system does not support an image\n"
                        "size of %dx%d pixels.", vp.width, vp.height));
        return false;
    }

    INTERLOCKED_(pictq_mutex) {
    	vp.allocated = true;
    	pictq_cond.Signal();
    }
    return true;
}

void MediaPlayer::video_audioWaveform()
{
    int i_start, x, y1, y, ys, delay, n, nb_display_channels;
    int ch, channels, h, h2, bgcolor, fgcolor;
    int16_t time_diff;
    int rdft_bits, nb_freq;

    for(rdft_bits=1; (1<<rdft_bits)<2*screen->h; rdft_bits++)
        ;
    nb_freq= 1<<(rdft_bits-1);

    // compute display index : center on currently output samples 
    channels = audioStream->codec->channels;
    nb_display_channels = channels;
    if (!paused) {
        int data_used = screen->w;
        n = 2 * channels;
        delay = GetAudioOutputBufferSize();
        delay /= n;

        // to be more precise, we take into account the time spent since the last buffer computation 
        if (audio_callback_time) {
            time_diff = int16_t(GetUSec() - audio_callback_time);
            delay -= (time_diff * audioStream->codec->sample_rate) / 1000000;
        }

        delay += 2*data_used;
        if (delay < data_used)
            delay = data_used;

        i_start = x = compute_mod(sample_array_index - delay * channels, SAMPLE_ARRAY_SIZE);
        
        h= INT_MIN;
        for(int i = 0; i < 1000; i += channels) {
            int idx= (SAMPLE_ARRAY_SIZE + x - i) % SAMPLE_ARRAY_SIZE;
            int a= sample_array[idx];
            int b= sample_array[(idx + 4*channels)%SAMPLE_ARRAY_SIZE];
            int c= sample_array[(idx + 5*channels)%SAMPLE_ARRAY_SIZE];
            int d= sample_array[(idx + 9*channels)%SAMPLE_ARRAY_SIZE];
            int score= a-d;
            if(h < score && (b^c)<0){
                h = score;
                i_start= idx;
            }            
        }
        last_i_start = i_start;
    } else 
        i_start = last_i_start;

    bgcolor = SDL_MapRGB(screen->format, 0x80, 0x80, 0x80);
    
    fill_rectangle(screen, 0, 0, screen->w, screen->h, bgcolor);

    fgcolor = SDL_MapRGB(screen->format, 0x00, 0xff, 0x00);

    // total height for one channel 
    h = screen->h / nb_display_channels;
    // graph height / 2 
    h2 = (h * 9) / 20;
    for(ch = 0; ch < nb_display_channels; ch++) {
        int i = i_start + ch;
        y1 = ch * h + (h / 2); /* position of center line */
        for(x = 0; x < screen->w; x++) {
            y = (sample_array[i] * h2) >> 15;
            ys = y1 - y;
            /*if (y < 0) {
                y = -y;
                ys = y1 - y;
            } else {
                ys = y1;
            }*/
            fill_rectangle(screen, x, ys, 1, 1/*y*/, fgcolor);
            i += channels;
            if (i >= SAMPLE_ARRAY_SIZE)
                i -= SAMPLE_ARRAY_SIZE;
        }
    }

    fgcolor = SDL_MapRGB(screen->format, 0x00, 0x00, 0xff);

    for(ch = 1; ch < nb_display_channels; ch++) {
        y = ch * h;
        fill_rectangle(screen, 0, y, screen->w, 1, fgcolor);
    }
    SDL_UpdateRect(screen, 0, 0, screen->w, screen->h);
}

// called to display each frame 
void MediaPlayer::video_refresh() {
  	if (videoStream) {
        if (pictq_size > 0) {						// There is picture
            double masterClock = get_master_clock();

			double diff = video_current_pts - masterClock;
            if(diff > 0 && diff < 2) 
                return;

            // Update current video pts 
            VideoPicture &vp = pictq[pictq_rindex];	// Dequeue the picture 
            video_current_pts = vp.pts;
            video_current_pts_time = GetUSec();
			video_current_pos = vp.pos;
            
			diff = video_current_pts - masterClock;		
			if (diff > 2) {
	            // update queue size and signal for next picture 
	            if (++pictq_rindex == VIDEO_PICTURE_QUEUE_SIZE)
	                pictq_rindex = 0;
	
	            INTERLOCKED_(pictq_mutex) {
	            	pictq_size--;
	            	pictq_cond.Signal();
	            }
                return;
            } 
	
            if(subtitleStream) {
                if (subtitle_stream_changed) {
                    INTERLOCKED_(subpq_mutex) {
	                    while (subpq_size) {
	                        free_subpicture(&subpq[subpq_rindex]);
	
	                        // update queue size for next picture 
	                        if (++subpq_rindex == SUBPICTURE_QUEUE_SIZE)
	                            subpq_rindex = 0;
	
	                        subpq_size--;
	                    }
	                    subtitle_stream_changed = false;
	
	                    subpq_cond.Signal();
                    }
                } else {
                    if (subpq_size > 0) {
                        SubPicture *sp, *sp2;
                        sp = &subpq[subpq_rindex];

                        if (subpq_size > 1)
                            sp2 = &subpq[(subpq_rindex + 1) % SUBPICTURE_QUEUE_SIZE];
                        else
                            sp2 = NULL;

                        if ((video_current_pts > (sp->pts + (float(sp->sub.end_display_time) / 1000)))
                                || (sp2 && video_current_pts > (sp2->pts + (float(sp2->sub.start_display_time) / 1000)))) {
                            free_subpicture(sp);

                            // update queue size and signal for next picture 
                            if (++subpq_rindex == SUBPICTURE_QUEUE_SIZE)
                                subpq_rindex = 0;

                            INTERLOCKED_(subpq_mutex) {
	                            subpq_size--;
	                            subpq_cond.Signal();
                            }
                        }
                    }
                }
            }
            video_image_display();		// display image 

            // update queue size and signal for next picture 
            if (++pictq_rindex == VIDEO_PICTURE_QUEUE_SIZE)
                pictq_rindex = 0;

            INTERLOCKED_(pictq_mutex) {
            	pictq_size--;
            	pictq_cond.Signal();
            }
        }
    } else if (audioStream && showAudio) {
        if (get_external_clock() > lastAudioShow + 1./showAudioFps) {
        	video_audioWaveform();			// Display audio waveform    
        	lastAudioShow = get_external_clock();
        }
    } 
}

void MediaPlayer::close() {
    // Free all pictures 
    for(int i = 0; i < VIDEO_PICTURE_QUEUE_SIZE; i++) {
        VideoPicture &vp = pictq[i];
        if (vp.overlay) {
            SDL_FreeYUVOverlay(vp.overlay);
            vp.overlay = 0;
        } else if (vp.surface) {
            SDL_FreeSurface(vp.surface);
            vp.surface = 0;
        }
    }
}

bool MediaPlayer::Load0Failed() {
    requestAbort = true;

    // Close each stream
    if (idAudio >= 0)
        StreamClose(idAudio);
    if (idVideo >= 0)
        StreamClose(idVideo);
    if (idSubtitle >= 0)
        StreamClose(idSubtitle);
    if (fileData) {
        av_close_input_file(fileData);
        fileData = NULL;
    }
    url_set_interrupt_cb(NULL);
	return false;
}

bool MediaPlayer::Load(const char *_fileName) {
	ResetError();
	Reset(GetSurface(), GetVideoFlags());
	
    int err;
    
    AVFormatParameters params, *ap = &params;
    int eof=0;
    int pkt_in_play_range = 0;

    fileData = avformat_alloc_context();

    idVideo = idAudio = idSubtitle = -1;

    memset(ap, 0, sizeof(*ap));

    ap->prealloced_context = 1;
    ap->time_base.num = 1;
    ap->time_base.den = 25;
    ap->pix_fmt = frame_pix_fmt;

    err = av_open_input_file(&fileData, ToSystemCharset(_fileName), iformat, 0, ap);
    if (err < 0) {
        MediaFile::SetError(GetAvError(err));
        return Load0Failed();
    }

    if(genpts)
        fileData->flags |= AVFMT_FLAG_GENPTS;

    err = av_find_stream_info(fileData);
    if (err < 0) {
        MediaFile::SetError(GetAvError(err));
        return Load0Failed();
    }
    if(fileData->pb)
        fileData->pb->eof_reached= 0; //FIXME hack, ffplay maybe should not use url_feof() to test for the end

    // if seeking requested, we execute it 
    if (start_time != AV_NOPTS_VALUE) {
        int64_t timestamp;

        timestamp = start_time;
        // add the stream start time 
        if (fileData->start_time != AV_NOPTS_VALUE)
            timestamp += fileData->start_time;
        int ret = av_seek_frame(fileData, -1, timestamp, AVSEEK_FLAG_BACKWARD);
        if (ret < 0) {
            MediaFile::SetError(Format("Could not seek to position %0.3f", (double)timestamp / AV_TIME_BASE));
            return Load0Failed();
        }
    }

    for(unsigned i = 0; i < fileData->nb_streams; i++) {
        AVStream *st = fileData->streams[i];
        st->discard = AVDISCARD_ALL;
        switch(st->codec->codec_type) {
        case AVMEDIA_TYPE_AUDIO:
        	numAudioStreams++;
        	{
	        	AudioData &audioD = audioData.Add();
	        	audioD.id = i;
				audioD.codec = GetCodecName(st->codec, 0);
				audioD.lang = GetStreamLanguage(st);
				audioD.sampleRate = st->codec->sample_rate;
				audioD.channels = st->codec->channels;
				audioD.bitrate 	= GetCodecBitRate(st->codec);
				audioD.bits 	= GetStreamAudioBitFormat(st);
				audioD.tags 	= GetStreamTags(st);
        	}
            if (!audio_disable)
            	idAudio = i;
            break;
        case AVMEDIA_TYPE_VIDEO:
        	numVideoStreams++;
        	{
	        	VideoData &videoD = videoData.Add();
	        	videoD.id = i;
				videoD.codec = GetCodecName(st->codec, 0);
				if (st->codec->pix_fmt != PIX_FMT_NONE) 
    				videoD.pixFmt = avcodec_get_pix_fmt_name(st->codec->pix_fmt);
				videoD.lang = GetStreamLanguage(st);
				videoD.size.cx = st->codec->width;
				videoD.size.cy = st->codec->height;
				if (st->codec->sample_aspect_ratio.num) {
					AVRational display_aspect_ratio;
		         	av_reduce(&display_aspect_ratio.num, &display_aspect_ratio.den,
		                          st->codec->width*st->codec->sample_aspect_ratio.num,
		                          st->codec->height*st->codec->sample_aspect_ratio.den,
		                          1024*1024);
					videoD.par.cx = st->codec->sample_aspect_ratio.num;
					videoD.par.cy = st->codec->sample_aspect_ratio.den;
					videoD.dar.cx = display_aspect_ratio.num;
					videoD.dar.cy = display_aspect_ratio.den;
		       	} else
		       		videoD.par.cx = videoD.par.cy = videoD.dar.cx = videoD.dar.cy = 1;
				
				videoD.tags = GetStreamTags(st);				
				videoD.frameRate.cx = st->avg_frame_rate.num;
				videoD.frameRate.cy = st->avg_frame_rate.den;
				videoD.tbr.cx 	  	= st->r_frame_rate.num;
				videoD.tbr.cy 	  	= st->r_frame_rate.den;
				videoD.tbn.cx  	  	= st->time_base.num;
				videoD.tbn.cy 	  	= st->time_base.den;
				videoD.tbc.cx  	  	= st->codec->time_base.num;
				videoD.tbc.cy 	  	= st->codec->time_base.den;
				
				videoD.bitrate = GetCodecBitRate(st->codec);
	            if (!video_disable)
	                idVideo = i;
        	}
            break;
        case AVMEDIA_TYPE_SUBTITLE:
            if (!video_disable)
            	idSubtitle = i;
            break;
        default:
            break;
        }
    }
    tags = ::GetTags(fileData);
    
    if (idAudio >= 0) 
		audioStream = fileData->streams[idAudio];
    if (idVideo >= 0) 
		videoStream = fileData->streams[idVideo];
    if (idSubtitle >= 0) 
		audioStream = fileData->streams[idSubtitle];

	return true;
}

void MediaPlayer::Reset(SDL_Surface *_screen, int _videoFlags)
{
	MediaFile::Reset();
	
    iformat = 0;
    last_i_start = 0;
	
	aspect_ratio = 1;
    screen = 0;
    
    memset(&frame_pix_fmt, 0, sizeof(frame_pix_fmt));

	frame_pix_fmt = PIX_FMT_NONE;
	av_sync_type = AV_SYNC_AUDIO_MASTER;
	start_time = AV_NOPTS_VALUE;
	
	fast = false;
	genpts = 0;
	lowres = 0;
	idct = FF_IDCT_AUTO;

	screen = _screen;
	videoFlags = _videoFlags;
}

void MediaPlayer::Pause()
{
 	stream_pause();
    step = false;
}

void MediaPlayer::step_to_next_frame()
{
  	if (paused)
       	stream_pause();
    step = true;
}

/* handle an event sent by the GUI */
bool MediaPlayer::event_loop()
{
    for(;;) {
        Ctrl::ProcessEvents();
        
 		SDL_Event event;      
        if (SDL_PollEvent(&event)) {
	        switch(event.type) {
	        case SDL_MOUSEBUTTONDOWN:
	        	Pause();
	            break;
	        case FF_ALLOC_EVENT:
	            if (!alloc_picture())
	                return false;
	            // Repaints background when change of size
	        	fill_rectangle(screen, 0, 0, screen->w, screen->h, 
	        									SDL_MapRGB(screen->format, 0x00, 0x00, 0x00));
	    		SDL_UpdateRect(screen, 0, 0, screen->w, screen->h);
	            break;
	        default:
	            break;
	        }
        }
        if (decodeThEnded)
            return true;

        video_refresh();
	
        second = get_master_clock();
        if (fabs(second - lastSecond) > 1.) {
            lastSecond = second;
            if (WhenSecond)
                WhenSecond();
        }
    }
    return true;
}

MediaPlayer::MediaPlayer() {
	SetBpp(24);
		    
    Reset(0, 0);
}

bool MediaPlayer::Play() {
	ResetError();
	
	// open the streams and launch threads
	decodeThEnd = decodeThEnded = false;
	requestAbort = false;
  	StreamOpen(idAudio);
  	StreamOpen(idVideo);
	StreamOpen(idSubtitle);
    
  	if (idVideo < 0 && idAudio < 0) {
        MediaFile::SetError("Could not open codecs");
        return Load0Failed();
    }
    
	if (!alloc_picture()) {
    	SDLCtrl::SetError("Could not alloc picture");
		return Load0Failed();
	}
    
    VideoPicture &vp = pictq[pictq_rindex];
    
    if (vp.overlay || vp.surface) {
        if (videoStream) {
		   	if (videoStream->sample_aspect_ratio.num)
		        aspect_ratio = float(av_q2d(videoStream->sample_aspect_ratio));
		    else if (videoStream->codec->sample_aspect_ratio.num)
		        aspect_ratio = float(av_q2d(videoStream->codec->sample_aspect_ratio));
		    else
		        aspect_ratio = 0;
		
		    if (aspect_ratio <= 0.0)
		        aspect_ratio = 1.0;
		    aspect_ratio *= float(vp.width) / float(vp.height);
        } else
            aspect_ratio = 1;
    }
    Layout();	// After aspect_ratio setting
    
    SetSecond(0);  
	externalClock.Reset();
	
	if (!fileData)
		throw Exc("MediaFile has been deleted");
	
    if (!BeginDecoder()) 
        return false;
 
    event_loop();
	close();
	
	return true;
}

void MediaPlayer::Stop() {
	requestAbort = true;
};

MediaPlayer::~MediaPlayer() {
}