/*
    player - WAV player
    Copyright (C) 2010-2015 Ladislav Vaiz <ok1zia@nagano.cz>

    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
    version 2 as published by the Free Software Foundation.

*/

#include "header.h"

#include "bfu.h"
#include "fifo.h"
#include "hf.h"
#include "kbdbind.h"
#include "player.h"
#include "tsdl.h"
#include "session.h"
#include "ssbd.h"
#include "subwin.h"

#if defined(HAVE_SNDFILE)

#define MS2PX 20


int sw_player_kbd_func(struct subwin *sw, struct event *ev, int fw){
    switch(kbd_action(KM_MAIN,ev)){
        case ACT_LEFT:
            if (!gssbd) break;
            MUTEX_LOCK(gssbd->seek);
            gssbd->seek -= 2000;
            MUTEX_UNLOCK(gssbd->seek);
            return 1;
        case ACT_RIGHT:
            if (!gssbd) break;
            MUTEX_LOCK(gssbd->seek);
            gssbd->seek += 1000;
            MUTEX_UNLOCK(gssbd->seek);
            return 1;
        case ACT_DOWN:
            if (!gssbd) break;
            MUTEX_LOCK(gssbd->seek);
            gssbd->seek -= 10000;
            MUTEX_UNLOCK(gssbd->seek);
            return 1;
        case ACT_UP:
            if (!gssbd) break;
            MUTEX_LOCK(gssbd->seek);
            gssbd->seek += 10000;
            MUTEX_UNLOCK(gssbd->seek);
            return 1;
    }
	if ((ev->x == 'p' || ev->x == 'P' || ev->x == ' ') && ev->y == 0){
		if (sw->player_filename) 
			player_play(sw->player_filename);
		return 1;
	}

    return 0;
}

int sw_player_mouse_func(struct subwin *sw, struct event *ev, int fw){
//    if (!sdl) return 0;
    return 0;
}


void sw_player_redraw(struct subwin *sw, struct band *band, int flags){
    int i, j, x, y0, y, c, cursor, dsec1, dsec2;
    int prev_y, prev_h, play_y, play_h;
#ifdef Z_HAVE_SDL    
    SDL_Rect area;
#endif
    //dbg("sw_player_redraw(%d)\n", sw->gdirty);
    if (sw->pl_rate == 0) return;
    if (sw->pl_channels == 0) return;
#ifdef Z_HAVE_SDL    
    if (sdl) {
        if (sw->h - PLAYER_H <= 0) return;

        prev_y = 0;
        prev_h = PREVIEW_H * FONT_H; 
        play_y = PREVIEW_H * FONT_H;
        play_h = sw->screen->h - (PREVIEW_H) * FONT_H; // PLAYER_H is included in sw->screen->h
        fill_area(sw->x, sw->y, sw->w, sw->h - PLAYER_H , 0);
/*	area.x = 0;
	area.y = prev_y;
	area.w = sw->screen->w;
	area.h = prev_h;
    SDL_FillRect(sw->screen, &area, z_makecol(20, 20, 20));*/
        if (sw->pl_preview_screen){
            SDL_BlitSurface(sw->pl_preview_screen, NULL, sw->screen, NULL);
			if (sw->pl_total_frames > 0){
				x = ((long long)sw->pl_preview_w * gssbd->playedf) / sw->pl_total_frames;
				z_line(sw->screen, x, prev_y, x, prev_y + prev_h, sdl->gr[8]);
			}
        }


        area.x = 0;
        area.y = play_y;
        area.w = sw->screen->w;
        area.h = play_h;
        SDL_FillRect(sw->screen, &area, z_makecol(40, 40, 40));
 
        //dbg("shapelen=%d channels=%d\n", sw->pl_shapelen, sw->pl_channels);
        if (sw->pl_pxlen == 0) return;
        c = sdl->green;
        sw_player_check_bounds(sw);
        cursor = gssbd->playedf / sw->pl_pxlen;
        for (x = 0; x < sw->screen->w; x++){
            i = x + sw->pl_ofs;
            if (i >= sw->pl_shape_frames) continue;
            if (i < 0) continue;
        
            for (j = 0; j < sw->pl_channels; j++){
				y0 = play_y + play_h / (sw->pl_channels * 2) + (j * play_h) / (sw->pl_channels);
                y = (sw->pl_shapebuf[i * sw->pl_channels + j] * play_h / 2) / (sw->pl_channels * 256);
                if (cursor == i){
                    y = play_h / (sw->pl_channels * 2);
                    z_line(sw->screen, x, y0+y, x, y0-y, sdl->gr[15]);
                }else{
                    z_line(sw->screen, x, y0+y, x, y0-y, c);
					z_putpixel(sw->screen, x, y0, sdl->gr[10]);
					if (i % (1000 / MS2PX) == 0){
						z_line(sw->screen, x, y0+10, x, y0, sdl->gr[10]);
					}
					//sw->pl_pxlen = sw->pl_rate * MS2PX / 1000;
                }
            }
        }
    
    }
#endif
    dsec1 = (10 * gssbd->playedf) / sw->pl_rate;
    dsec2 = (10 * sw->pl_total_frames) / sw->pl_rate;
    clip_printf(sw, 1, sw->h - PLAYER_H, COL_NORM, "%d:%02d.%d / %d:%02d.%d", dsec1 / 600, (dsec1 % 600)/10, dsec1 % 10, dsec2 / 600, (dsec2 % 600)/10, dsec2 % 10);
}

void sw_player_check_bounds(struct subwin *sw){
	int cursor;

#ifdef Z_HAVE_SDL
    if (!sdl) return;
    if (sw->pl_pxlen == 0) return;

    cursor = gssbd->playedf / sw->pl_pxlen;

    if (cursor - sw->pl_ofs < 0){
        sw->pl_ofs = cursor - sw->screen->w / 5;
    }

    if (cursor - sw->pl_ofs > (sw->screen->w * 4) / 5){
        sw->pl_ofs = cursor - (sw->screen->w * 4) / 5;
    }
#endif
}

void sw_player_raise(struct subwin *sw){
#ifdef Z_HAVE_SDL
    sw->gdirty=1;      
#endif
}

void sw_player_free(struct subwin *sw){
#ifdef Z_HAVE_SDL
    zg_free0(sw->pl_shapebuf);
    if (sw->pl_preview_screen) SDL_FreeSurface(sw->pl_preview_screen);
#endif
}

void player_play(char *filename){
    struct subwin *sw;
    int i, j;
    SF_INFO sfinfo;
    SNDFILE *sf;
    short *pxbuf, *sh;
    double *sum;
    short *avg;
    short *max;
    int shapei = 0;
	int prev_sh;
	int total_frames = 0;
	int x, y0, y, oldx = 0;
	int shapesize = 0;

	sw = sw_raise_or_new(SWT_PLAYER);
    if (!sw) return;
    redraw_later();
#ifdef Z_HAVE_SDL
    sw->gdirty=1;
#endif

	if (filename != sw->player_filename){
		g_free(sw->player_filename);
		sw->player_filename = g_strdup(filename);
		z_wokna(sw->player_filename);
	}

    sw->pl_total_frames = 0;
    sw->pl_channels = 1;
    memset (&sfinfo, 0, sizeof (sfinfo));
    sf = sf_open(filename, SFM_READ, &sfinfo);
    if (!sf) {
        log_addf(VTEXT(T_CANT_PLAY), filename, sf_strerror (NULL));
        return;
    }

    sw->pl_total_frames = sf_seek(sf, 0L, SEEK_END);
    if (sw->pl_total_frames < 0) {
        log_addf(VTEXT(T_CANT_SEEK_TO_END), filename, sf_strerror(sf));
        sf_close(sf);
        sf = NULL;
        return;
    }

    if (sf_seek(sf, 0L, SEEK_SET) < 0) {
        log_addf(VTEXT(T_CANT_SEEK_TO_START), filename, sf_strerror(sf));
        sf_close(sf);
		sf = NULL;
        return;
    }

    sw->pl_channels = sfinfo.channels;
    sw->pl_rate = sfinfo.samplerate;
    sw->pl_pxlen = sw->pl_rate * MS2PX / 1000;

    dbg("pl_total_frames=%d pxlen=%d\n", sw->pl_total_frames, sw->pl_pxlen);
    zg_free0(sw->pl_shapebuf);
	shapesize = sw->pl_channels * ((sw->pl_total_frames / sw->pl_pxlen) + 1);
    sw->pl_shapebuf = g_new0(char, shapesize);
    sw->pl_shape_frames = 0;

#ifdef Z_HAVE_SDL
    if (sw->pl_preview_screen) SDL_FreeSurface(sw->pl_preview_screen);
    sw->pl_preview_screen = SDL_CreateRGBSurface(SDL_SWSURFACE, sw->screen->w, PREVIEW_H * FONT_H, sdl->bpp, 
                    sdl->screen->format->Rmask, sdl->screen->format->Gmask, sdl->screen->format->Bmask, 0);
    sw->pl_preview_w = sw->pl_preview_screen->w;
    SDL_FillRect(sw->pl_preview_screen, NULL, z_makecol(20, 20, 20));

    pxbuf = g_new0(short, sw->pl_pxlen * sw->pl_channels);
    sum = g_new0(double, sw->pl_channels);
    avg = g_new0(short, sw->pl_channels);
    max = g_new0(short, sw->pl_channels);
    shapei = 0;
    
    while(1){
        int frames = sf_readf_short(sf, pxbuf, sw->pl_pxlen);
        if (frames <= 0) break;

		total_frames += frames;
        for (j = 0; j < sw->pl_channels; j++) {
            sum[j] = 0.0;
            max[j] = 0;
        }

        sh = pxbuf;
        for (i = 0; i < frames; i++){
            for (j = 0; j < sw->pl_channels; j++){
                sum[j] += *sh++;
            }
        }

        for (j = 0; j < sw->pl_channels; j++) avg[j] = (short)(sum[j] / frames);

        sh = pxbuf;
        for (i = 0; i < frames; i++){
            for (j = 0; j < sw->pl_channels; j++){
                int d = *sh - avg[j];
                if (d < 0) d = -d;
                if (d > max[j]) max[j] = d;
                sh++;
            }
        }

        prev_sh = 0;
        for (j = 0; j < sw->pl_channels; j++) {
            int shape = 0;
          //  dbg("max[%d]=%5d ", j, max[j]);
            if (max[j] == 0) {
                shape = 0;
            }else{
                //shape = log(max[j]) * 256.0 / log(32768);
                shape = max[j] * 256.0 / 32768;
                if (shape > 255) shape = 255;
            }
            //dbg("shape[%d]=%3d ", j, shape);
            sw->pl_shapebuf[shapei] = shape;
            shapei++;
            prev_sh += shape;
        }
        //dbg("\n");
		prev_sh /= sw->pl_channels;

        x = ((long long)sw->pl_preview_screen->w * total_frames) / sw->pl_total_frames;
        y0 = sw->pl_preview_screen->h / 2;
        y = sw->pl_preview_screen->h * prev_sh / (2 * 256);
	    //dbg("x=%d prev_sh=%3d  y=%d\n", x, prev_sh, y);

		for (i = oldx; i <= x; i++) {
			z_line(sw->pl_preview_screen, i, y0+y, i, y0-y, z_makecol(0, 128, 0));
			z_putpixel(sw->pl_preview_screen, i, y0, sdl->gr[10]);
		}
		oldx = x + 1;
        sw->pl_shape_frames++;
    }
    g_free(pxbuf);
    g_free(sum);
    g_free(avg);
    g_free(max);
#endif
    sf_close(sf);
	sf = NULL;
    
    ssbd_play_file(gssbd, filename);
    gses->icon=gssbd->playicon;
}



#else
int sw_player_kbd_func(struct subwin *sw, struct event *ev, int fw){
    return 0;
}

int sw_player_mouse_func(struct subwin *sw, struct event *ev, int fw){
    return 0;
}

void sw_player_redraw(struct subwin *sw, struct band *b, int flags){
}

void sw_player_check_bounds(struct subwin *sw){
}

void sw_player_raise(struct subwin *sw){
}

#endif
