/**********************************************************************
 
	Copyright (C) 2005- 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	"memory_debug.h"
#include	"matrix.h"

int _debug_flag;

int
mx_cache_boundary(MX_CACHE * c,int * ix);

//#define MXC_DEBUG	1

void *
xx_mxc_alloc(MX_CACHE * c,int size,char * __f,int __l)
{
int blk,r;
MBLK * ret;
	blk = size >> c->mblk_logsize;
	r = size & ((1<<c->mblk_logsize)-1);
	if ( r )
		blk ++;
#ifndef MXC_DEBUG
	if ( _debug_flag || (blk > c->mblk_hashsize-1) ) {
		ret = xx_d_alloc(sizeof(MBLK)+size,__f,__l);
#else
		ret = xx_d_alloc(sizeof(MBLK)+size,__f,__l);
/*
		ret = xx_d_alloc(sizeof(MBLK)+(blk<<c->mblk_logsize),
					__f,__l);
*/
#endif
		ret->size = MBLK_ALLOC;
		ret->alloc_file = __f;
		ret->alloc_line = __l;
		ret->free_file = 0;
		ret->free_line = 0;
		return ret+1;
#ifndef MXC_DEBUG
	}
#endif
	ret = c->mblk_hash[blk];
	if ( ret == 0 ) {
		ret = xx_d_alloc(sizeof(MBLK)+(blk<<c->mblk_logsize),
					__f,__l);
		ret->size = blk;
		ret->cnt = 1;
		ret->alloc_file = __f;
		ret->alloc_line = __l;
		ret->free_file = 0;
		ret->free_line = 0;
		return ret+1;
	}
	c->mblk_hash[blk] = ret->next;
	ret->cnt ++;
	if ( ret->cnt == MBLK_ALLOC )
		ret->cnt --;
	ret->free_file = 0;
	ret->free_line = 0;
	return ret+1;
}

void
xx_mxc_free(MX_CACHE * c,void * ptr,char * __f,int __l)
{
MBLK * p;
int blk;
	p = ptr;
	p --;
	if ( p->free_file )
		er_panic("xx_mxc_free");
	if ( p->size == MBLK_ALLOC ) {
		d_f_ree(p);
		return;
	}
	p->free_file = __f;
	p->free_line = __l;
	blk = p->size;
	if ( blk > c->mblk_hashsize-1 )
		er_panic("mxc_free");
	if ( blk < 0 )
		er_panic("mxc_free2");
	p->next = c->mblk_hash[blk];
	c->mblk_hash[blk] = p;
}

void
mxc_setup(MX_CACHE * c)
{
	if ( c->mblk_logsize == 0 )
		c->mblk_logsize = MBLK_LOGSIZE;
	if ( c->mblk_hashsize == 0 )
		c->mblk_hashsize = MBLK_HASHSIZE;
	c->mblk_hash = d_alloc(sizeof(MBLK*)*c->mblk_hashsize);
	memset(c->mblk_hash,0,sizeof(MBLK*)*c->mblk_hashsize);
}

void
flush_mx_cache(MX_CACHE * c,int f)
{
int i;
MBLK * p;
	if ( c->n == 0 )
		return;
	for ( i = 0 ; i < c->ds_len; i ++ )
		matrix_node_channel_unlock(c->n,c->dirty);
	unlock_node(c->n,0);
	c->n = 0;
	if ( f ) {
		for ( i = 0 ; i < c->mblk_hashsize ; i ++ ) {
			for ( ; c->mblk_hash[i] ; ) {
				p = c->mblk_hash[i];
				c->mblk_hash[i] = p->next;
				d_f_ree(p);
			}
		}
	}
}

int
get_mx_cache(MX_CACHE * c,INTEGER64 * src)
{
MATRIX_NODE * n;
int err;
int i;
void * d;
	if ( c->n ) {
		if ( cmp_dim_code(c->m,c->n->dim_code,src) ) {
			flush_mx_cache(c,0);
			goto load;
		}
	}
	else {
	load:
		n = 0;
		get_matrix_node_channel
			(&err,&n,c->m,
			src,c->access_ch[0],
			c->gn_tree_node,c->gn_create,0);
		if ( n ) {
			matrix_node_channel_unlock(n,0);
			unlock_node(n,0);
		}
		err = 0;
		n = get_matrix_node_wait(&err,c->m,src,
					c->gn_wait);
		if ( err ) {
			return -1;
		}

//ss_printf("CC %s %s %i\n",pt_dc(c->m,src),pt_dc(c->m,n->dim_code),n->status);
		c->n = n;
		if ( c->ds == 0 )
			c->ds = d_alloc(sizeof(MATRIX_DH_SET)*
						c->ds_len);
		for ( i = 0 ; i < c->ds_len ; i ++ ) {
			d = get_matrix_node_channel
				(&err,&n,c->m,src,c->access_ch[i],
				GN_NODE,GN_NODE_CREATE,0);
			c->ds[i].tp = c->m->channel_info
					[c->access_ch[i]].data_type;
			if ( c->ds[i].tp->parent ) {
				get_matrix_dh_set(&c->ds[i],d);
			}
			else {
				memset(&c->ds[i],0,sizeof(MATRIX_DH_SET));
				c->ds[i].offset = d;
			}
		}
	}
	return 0;
}

int
mx_cache_boundary(MX_CACHE * c,int * ix)
{
int i,j;
	for ( i = 0 ; i < c->ds_len ; i ++ ) {
		if ( c->ds[i].ix == 0 )
			return -1;
		for ( j = 0 ; j < c->m->p.dim ; j ++ ) {
			if ( c->ds[i].hd == 0 )
				continue;
			if ( c->ds[i].ix[j] <= ix[j] )
				return -1;
		}
	}
	return 0;
}


int
write_mx_cache(MX_CACHE_PARAM * p)
{
int i;
int * ofs;
MATRIX_DATA_TYPE * tp;
int el_size;
void * p1;
MATRIX * m;
MX_CACHE * c;
INTEGER64 ix,st;


	if ( get_mx_cache(p->c,p->dc) < 0 )
		return -1;
	c = p->c;
	m = c->m;
	ofs = mxc_alloc(c,sizeof(int)*m->p.dim);
	if ( p->ofs ) {
		for ( i = 0 ; i < m->p.dim ; i ++ ) {
			st = p->dc[i+1] >> (p->dc[0] * m->dim_divide[i]);
			ix = p->ofs[i] + st;
			st &= -(((INTEGER64)1)<<m->block_size[i]);
			ofs[i] = ix - st;
		}
	}
	else {
		for ( i = 0 ; i < m->p.dim ; i ++ )
			ofs[i] = (p->dc[i+1] >> (p->dc[0]
					* m->dim_divide[i])) &
					((((INTEGER64)1) 
						<< m->block_size[i])-1);
	}

	if ( mx_cache_boundary(p->c,ofs) < 0 ) {
		mxc_free(p->c,ofs);
		return -1;
	}
	for ( i = 0 ; i < c->ds_len ; i ++ ) {
		if ( p->data_ix[i].x == MXC_INVALID )
			continue;
		if ( c->ds[i].tp->parent ) {
			tp = c->ds[i].tp->parent;
			el_size = (*tp->get_size)(tp,0);
			p1 = ((char*)c->ds[i].offset)
				+ get_seq_from_ix(ofs,c->ds[i].ix,
					m->p.dim) *
					el_size;
			memcpy(p1,
				((char*)p->data_ptrs[p->data_ix[i].p]) + 
					el_size*p->data_ix[i].x,
				el_size);
		}
		else {
			tp = c->ds[i].tp;
			el_size = (*tp->get_size)(tp,0);
			memcpy(c->ds[i].offset,
				((char*)p->data_ptrs[p->data_ix[i].p]) + 
				p->data_ix[i].x*el_size,el_size);
		}
		c->dirty = NF_DIRTY;
	}
	mxc_free(c,ofs);
	return 0;
}

int
read_mx_cache(MX_CACHE_PARAM * p)
{
int i;
int * ofs;
MATRIX_DATA_TYPE * tp;
int el_size;
void * p1;
MATRIX * m;
MX_CACHE * c;
INTEGER64 st,ix;
	if ( get_mx_cache(p->c,p->dc) < 0 )
		return -1;
	c = p->c;
	m = c->m;
	ofs = mxc_alloc(c,sizeof(int)*m->p.dim);
	if ( p->ofs ) {
		for ( i = 0 ; i < m->p.dim ; i ++ ) {
			st = p->dc[i+1] >> (p->dc[0] * m->dim_divide[i]);
			ix = p->ofs[i] + st;
			st &= -(((INTEGER64)1)<<m->block_size[i]);
			ofs[i] = ix - st;
		}
	}
	else {
		for ( i = 0 ; i < m->p.dim ; i ++ )
			ofs[i] = (p->dc[i+1] >> (p->dc[0]
					* m->dim_divide[i])) &
					((((INTEGER64)1)
						<<m->block_size[i])-1);
	}
	if ( mx_cache_boundary(p->c,ofs) < 0 ) {
		mxc_free(p->c,ofs);
		return -1;
	}
	for ( i = 0 ; i < c->ds_len ; i ++ ) {
		if ( p->data_ix[i].x == MXC_INVALID )
			continue;
		if ( c->ds[i].tp->parent ) {
			tp = c->ds[i].tp->parent;
			el_size = (*tp->get_size)(tp,0);
			p1 = ((char*)c->ds[i].offset)
				+ get_seq_from_ix(ofs,c->ds[i].ix,
					m->p.dim) *
					el_size;
			memcpy(((char*)p->data_ptrs[p->data_ix[i].p]) +
				p->data_ix[i].x*el_size,
				p1,el_size);
		}
		else {
			tp = c->ds[i].tp;
			el_size = (*tp->get_size)(tp,0);
			memcpy(((char*)p->data_ptrs[p->data_ix[i].p]) +
				p->data_ix[i].x*el_size,
				c->ds[i].offset,el_size);
		}
	}
	mxc_free(c,ofs);
	return 0;
}

