/**********************************************************************
 
	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	<fcntl.h>
#include	<stdio.h>
#include	<stdlib.h>
#include	<sys/types.h>
#include	"r64.h"
#include	"change_endian.h"
#include	"machine/include.h"
#include	"u_file.h"

void
delete_cr_ring(CR_ENT * e)
{
	e->next->prev = e->prev;
	e->prev->next = e->next;
}

void
insert_cr_ring(CR_FILE * f,CR_ENT * e)
{
	e->prev = &f->cache[0];
	e->next = f->cache[0].next;
	e->prev->next = e;
	e->next->prev = e;
}

void
touch_cr_ent(CR_FILE * f,CR_ENT * e)
{
	delete_cr_ring(e);
	insert_cr_ring(f,e);
}


CR_ENT *
new_cr_ent(CR_FILE * f)
{
CR_ENT * ret;
	if ( f->free_list ) {
		ret = f->free_list;
		f->free_list = ret->next;
		insert_cr_ring(f,ret);
		return ret;
	}
	else {
		ret = f->cache[0].prev;
		if ( ret->b.buf )
			free(ret->b.buf);
		ret->b.buf = 0;
		return ret;
	}
}

void
free_cr_ent(CR_FILE * f,CR_ENT * e)
{
	delete_cr_ring(e);
	if ( e->b.buf )
		free(e->b.buf);
	e->b.buf = 0;
	e->next = f->free_list;
	f->free_list = e;
}

void
change_endian_cr_tbl(CR_TBL * tbl)
{
	change_endian_i(tbl->ofs);
	change_endian_i(tbl->size);
}


int
cr_read(CR_MEM_TBL * b,
	CR_FILE * f,int width,int height,int level)
{
CR_ENT * e;
unsigned int ofs;
CR_TBL tbl;
int er;
	width = width - (width % f->header.rect_size);
	height = height - (height % f->header.rect_size);

	for ( e = f->cache[0].next ; e != &f->cache[0] ; e = e->next ) {
		if ( e->level != level )
			continue;
		if ( e->w_ofs != width )
			continue;
		if ( e->h_ofs != height )
			continue;
		goto cpy;
	}
	ofs = get_tbl_ofs(f,width,height,level);
	u_lseek64(f->fd,ofs,SEEK_SET);
	er = u_read(f->fd,(char*)&tbl,sizeof(tbl));
	if ( er < 0 ) {
		r64_error = E_SYS;
		goto err;
	}
	if ( er < sizeof(tbl) ) {
		r64_error = E_DFILE;
		goto err;
	}
	change_endian_cr_tbl(&tbl);
	e = new_cr_ent(f);
	memcpy(&e->b,&tbl,sizeof(e->b));
	e->b.buf = malloc(tbl.size);
	u_lseek64(f->fd,tbl.ofs,SEEK_SET);
	if ( tbl.ofs == 0 || tbl.size == 0 ) {
		free_cr_ent(f,e);
		r64_error = E_DFILE;
		goto err;
	}
	er = u_read(f->fd,(char*)e->b.buf,tbl.size);
	if ( er < 0 ) {
		free_cr_ent(f,e);
		r64_error = E_SYS;
		goto err;
	}
	if ( er < tbl.size ) {
		free_cr_ent(f,e);
		r64_error = E_DFILE;
		goto err;
	}
	e->level = level;
	e->w_ofs = width;
	e->h_ofs = height;
	if ( width + f->header.rect_size > f->level_w[level] )
		e->w_len = f->level_w[level] - width;
	else	e->w_len = f->header.rect_size;
	if ( height + f->header.rect_size > f->level_h[level] )
		e->h_len = f->level_h[level] - height;
	else	e->h_len = f->header.rect_size;
cpy:
	*b = e->b;
	b->buf = malloc(e->b.size);
	memcpy(b->buf,e->b.buf,e->b.size);
	touch_cr_ent(f,e);
	return 0;
err:
	return -1;
}
