/**********************************************************************
 
	Copyright (C) 2003 Hirohisa MORI <joshua@nichibun.ac.jp>
 
	This program is free software; you can redistribute it 
	and/or modify it under the terms of the GLOBALBASE 
	Library General Public License (G-LGPL) as published by 

	http://www.globalbase.org/
 
	This program is distributed in the hope that it will be 
	useful, but WITHOUT ANY WARRANTY; without even the 
	implied warranty of MERCHANTABILITY or FITNESS FOR A 
	PARTICULAR PURPOSE.

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


#include	<stdio.h>
#include	<stdlib.h>
#include	"u_math.h"
#include	<fcntl.h>
#include	"r64.h"
#include	"complex.h"

#define FFT_UNIT	8
#define FFT_BEKI	3

void
save_test(double * buf[3],int size)
{
unsigned char col[3];
int i,j,k;
int cc;
R64_FILE * f;
	printf("save_test %i %i\n",size,FFT_UNIT);
	f = r64_open_file(
		"test.r64",
		O_RDWR|O_TRUNC|O_CREAT,
		0644,
		'P',
		size,FFT_UNIT);
	for ( i = 0 ; i < FFT_UNIT/2 ; i ++ )
		for ( j = 0 ; j < size ; j ++ ) {
			if ( i == FFT_UNIT/2-1 || j == size-1 ) {
				col[0] = 255;
				col[1] = col[2] = 0;
			}
			else for ( k = 0 ; k < 3 ; k ++ ) {
				cc = buf[k][i*size+j];
				if ( cc < 0 )
					cc = 0;
				else if ( cc > 0xff )
					cc = 0xff;
				col[k] = cc;
			}
			r64_set(f,j,i,0,col);
		}
	r64_close_file(f);
}


double
window_fn(int x,int y)
{
static double weight[FFT_UNIT][FFT_UNIT];
int i,j;
	if ( weight[FFT_UNIT/2][FFT_UNIT/2] == 0 ) {
		for ( i = 0 ; i < FFT_UNIT ; i ++ )
			for ( j = 0 ;j < FFT_UNIT ; j ++ )
				weight[i][j] =
					(1-cos(2*M_PI*i/FFT_UNIT))*
					(1-cos(2*M_PI*j/FFT_UNIT))/4;
	}
	return weight[x][y];
}

void
set_rect_buf(
	R64_FILE * f,
	int i,
	int j,
	COMPLEX rect_buf[3][FFT_UNIT][FFT_UNIT],
	int level)
{
unsigned char col[3];
int n,m,k;
int vol;
	vol = ENT_UNIT_VOL(f);
	col[0] = col[1] = col[2] = 0;
	for ( n = 0 ; n < FFT_UNIT ; n ++ )
		for ( m = 0 ; m < FFT_UNIT ; m ++ ) {
			r64_red(col,f,j+m,i+n,level);
			for ( k = 0 ; k < vol ; k ++ ) {
				rect_buf[k][n][m].re = window_fn(m,n)*col[k];
				rect_buf[k][n][m].im = 0;
			}
		}
}

void
half_filter(COMPLEX rect_buf[3][FFT_UNIT][FFT_UNIT],int vol)
{
COMPLEX tmp[FFT_UNIT][FFT_UNIT];
COMPLEX * ret;
int n,k,i;
	for ( k = 0 ; k < vol ; k ++ ) {
		for ( n = 0 ; n < FFT_UNIT ; n ++ ) {
			ret = fastft(rect_buf[k][n],FFT_BEKI);
			for ( i = 0 ; i < FFT_UNIT ; i ++ )
				tmp[i][n] = ret[i];
			free(ret);
		}
		for ( n = 0 ; n < FFT_UNIT ; n ++ ) {
			ret = fastft(tmp[n],FFT_BEKI);
			for ( i = 0 ; i < FFT_UNIT ; i ++ )
				rect_buf[k][i][n] = ret[i];
			free(ret);
		}
		for ( n = 0 ; n < FFT_UNIT/4 ; n ++ )
			for ( i = 0 ; i < FFT_UNIT ; i ++ )
				rect_buf[k][n+FFT_UNIT/4][i]
					= rect_buf[k][n+3*FFT_UNIT/4][i];
		for ( n = 0 ; n < FFT_UNIT/4 ; n ++ )
			for ( i = 0 ; i < FFT_UNIT/2 ; i ++ )
				rect_buf[k][i][n+FFT_UNIT/4]
					= rect_buf[k][i][n+3*FFT_UNIT/4];
		for ( n = 0 ; n < FFT_UNIT/2 ; n ++ ) {
			ret = fastdft(rect_buf[k][n],FFT_BEKI-1);
			for ( i = 0 ; i < FFT_UNIT/2 ; i ++ )
				tmp[i][n] = ret[i];
			free(ret);
		}
		for ( n = 0 ; n < FFT_UNIT/2 ; n ++ ) {
			ret = fastdft(tmp[n],FFT_BEKI-1);
			for ( i = 0 ; i < FFT_UNIT/2 ; i ++ )
				rect_buf[k][i][n] = ret[i];
			free(ret);
		}
	}
}

void
set_buf(
	double * buf[3],
	int j,
	COMPLEX rect_buf[3][FFT_UNIT][FFT_UNIT],
	int buf_line,
	int vol)
{
int n,m,k;
	for ( k = 0 ; k < vol ; k ++ )
		for ( n = 0 ; n < FFT_UNIT/2 ; n ++ )
			for ( m = 0 ; m < FFT_UNIT/2 ; m ++ )
				buf[k][(j/2+FFT_UNIT/4+m)+buf_line*n]
					+= rect_buf[k][n][m].re;
}

void
flush_buf(
	R64_FILE * f,
	int i,
	double * buf[3],
	int level,
	int dest_w,
	int buf_line)
{
unsigned char col[3];
int n,m,k;
int vol;
	vol = ENT_UNIT_VOL(f);
	for ( n = 0 ; n < FFT_UNIT/4 ; n ++ )
		for ( m = 0 ; m < dest_w ; m ++ ) {
			for ( k = 0 ; k < vol ; k ++ ) {
			int cc;
				cc = buf[k][n*buf_line+m+FFT_UNIT/4];
				if ( cc >= 0x100 )
					cc = 0xff;
				else if ( cc < 0 )
					cc = 0;
				col[k] = cc;
			}
			r64_set(f,m,i/2+n,level,col);
		}
	for ( n = 0 ; n < FFT_UNIT/4 ; n ++ )
		for ( m = 0 ; m < buf_line ; m ++ )
			for ( k = 0 ; k < vol ; k ++ ) {
				buf[k][n*buf_line+m]
				 = buf[k][(n+FFT_UNIT/4)*buf_line+m];
				buf[k][(n+FFT_UNIT/4)*buf_line+m] = 0;
			}
}

int
r64_new_level_hifi(R64_FILE * f,int level)
{
int w,h;
int dest_w,dest_h;
int i,j,k,n,m;
double * buf[3];
int buf_size,buf_line;
COMPLEX rect_buf[3][FFT_UNIT][FFT_UNIT];
int c;
	if ( r64_check_level(f,level) == 0 ) {
		r64_error = E_NOLEV;
		return -1;
	}
	w = f->level_w[level];
	h = f->level_h[level];
	dest_w = (w>>1)+(w&1);
	dest_h = (h>>1)+(h&1);
	if ( dest_w == 0 || dest_h == 0 ) {
		r64_error = E_NOLEV;
		return -1;
	}
	buf_line = dest_w-(dest_w%(FFT_UNIT/2))+3*(FFT_UNIT/2);
	buf_size = buf_line*sizeof(double)*FFT_UNIT/2;
	buf[0] = malloc(buf_size);
	buf[1] = malloc(buf_size);
	buf[2] = malloc(buf_size);

	for ( i = -FFT_UNIT/2 ; i < h ; i += FFT_UNIT/2 ) {
		printf("h = %i/%i   \r",i,h);
		fflush(stdout);
		for ( j = -FFT_UNIT/2 ; j < w ; j += FFT_UNIT/2 ) {
			set_rect_buf(
				f,
				i,j,
				rect_buf,
				level);
			half_filter(rect_buf,ENT_UNIT_VOL(f));
			set_buf(buf,j,rect_buf,buf_line,ENT_UNIT_VOL(f));
		}
		flush_buf(f,i,buf,level+1,dest_w,buf_line);
	}

	printf("\n");
	for ( k = 0 ; k < 3 ; k ++ )
		free(buf[k]);
	return 0;
}


void
write_out(R64_FILE * f,int level,int x,int y)
{
unsigned char col[3];
unsigned int result[3];
int i,j,k;

int er;

	for ( k = 0 ; k < 3 ; k ++ )
		result[k] = 0;
	for ( i = 0 ; i < 2 ; i ++ )
		for ( j = 0 ; j < 2 ; j ++ ) {
			er = r64_red(col,f,x+i,y+j,level);
			if ( er < 0 )
				printf("writeout err %i:%i\n",
					er,r64_error);
			for ( k = 0 ; k < 3 ; k ++ )
				result[k] += col[k];
		}
	for ( k = 0 ; k < 3 ; k ++ )
		col[k] = result[k]/4;
	r64_set(f,x/2,y/2,level+1,col);

}


int
r64_new_level_lofi(R64_FILE * f,int level)
{
#define PITCH	64


int w,h;
int dest_w,dest_h;
int i,j,k,n,m;
int p,q;
int lim_w,lim_h;
int total,remain;
	if ( r64_check_level(f,level) == 0 ) {
		r64_error = E_NOLEV;
		return -1;
	}
	w = f->level_w[level];
	h = f->level_h[level];
	dest_w = (w>>1)+(w&1);
	dest_h = (h>>1)+(h&1);
	if ( dest_w == 0 || dest_h == 0 ) {
		r64_error = E_NOLEV;
		return -1;
	}
	remain = total = w*h/(PITCH*PITCH);
	for ( i = 0 ; i < w ; i += PITCH ) {
		if ( w - i < PITCH )
			lim_w = w - i;
		else	lim_w = PITCH;
		for ( j = 0 ; j < h ; j += PITCH ) {
			if ( h - j < PITCH )
				lim_h = h - j;
			else	lim_h = PITCH;
printf("total = %i/%i  \r",remain--,total);
			for ( n = 0 ; n < lim_w ; n += 2 )
				for ( m = 0 ; m < lim_h ; m += 2 )
					write_out(f,level,
						i+n,
						j+m);
		}
	}
printf("\n");
	return 0;
}


int
r64_new_level(R64_FILE * f,int level)
{
	return r64_new_level_lofi(f,level);
}

