// Emacs: -*- C++ -*-

//
//	Copyright 1993, Center for Computer Vision and Visualization,
//	University of Florida.  All rights reserved.
//


//
// $Log: neighborhood-reduction,v $
// Revision 1.1  1994/01/31  16:50:30  thoth
// Initial revision
//

#ifndef zero_extend_scan_IBTYPE
#define zero_extend_scan_IBTYPE

static void zero_extend_vec_scan(const IA_SetStructure &ss,
				 const IBTYPE **src,
				 IBTYPE **dest,
				 IBTYPE zero)
{
    for (unsigned i=0; i<ss.nintervals(); i++) {
	IA_ss_interval	temp(ss.retrieve_interval(i));
	if (temp.substructure == IA_SetStructure::FIRST_ONLY) {
	    (*src) += temp.count;
	} else if (temp.substructure == IA_SetStructure::BOTH) {
	    for (unsigned j=0; j<temp.count; j++) {
		*((*dest)++) = *((*src)++);
	    }
	} else if (temp.substructure == IA_SetStructure::SECOND_ONLY) {
	    for (unsigned j=0; j<temp.count; j++)
		*((*dest)++) = zero;
	} else {
	    for (unsigned j=0; j<temp.count; j++)
		zero_extend_vec_scan(temp.substructure, src, dest, zero);
	}
    }
}

static void zero_extend_iter_scan(const IA_SetStructure &ss,
				  IA_IVIter<IA_IntPoint,IBTYPE> *srciter,
				  IBTYPE **dest,
				  IBTYPE zero)
{
    for (unsigned i=0; i<ss.nintervals(); i++) {
	IA_ss_interval	temp(ss.retrieve_interval(i));
	if (temp.substructure == IA_SetStructure::FIRST_ONLY) {
	    IBTYPE	blah;
	    for (unsigned j=0; j<temp.count; j++) 
		(*srciter)(blah);
	} else if (temp.substructure == IA_SetStructure::BOTH) {
	    IBTYPE	blah;
	    for (unsigned j=0; j<temp.count; j++) {
		(*srciter)(blah);
		*((*dest)++) = blah;
	    }
	} else if (temp.substructure == IA_SetStructure::SECOND_ONLY) {
	    for (unsigned j=0; j<temp.count; j++)
		*((*dest)++) = zero;
	} else {
	    for (unsigned j=0; j<temp.count; j++)
		zero_extend_iter_scan(temp.substructure, srciter, dest, zero);
	}
    }
}

static void zero_extend(const IA_CoreImage<IA_IntPoint,IBTYPE> &srcimg,
			const IA_IntPointSet &dest_ps, IBTYPE *dest_data,
			IBTYPE zero)
{
    IA_SetStructure	ss;
    intersect_with_dualstruct(srcimg.domain(), dest_ps, &ss);

    if (srcimg.type() == IA_VectorI<IA_IntPoint,IBTYPE>::s_type()) {
	IBTYPE	*srcdata = ((IA_VectorI<IA_IntPoint,IBTYPE>*)srcimg.bip)->vec;
	zero_extend_vec_scan(ss, &srcdata, &dest_data, zero);
    } else {
	IA_IVIter<IA_IntPoint,IBTYPE>	iter(srcimg);
	zero_extend_iter_scan(ss, &iter, &dest_data, zero);
    }
}

#endif

//
// backward neighborhood reductions
//

static IA_Image<IA_IntPoint,RTYPE>
backw_CONV_inv_core(IA_IntPoint src_infimum,
	      IA_IntPoint src_width,
	      const IBTYPE *src_data, // length is prod(src_width)
	      const IA_IntPointSet &nbh,
	      IA_IntPointSet dest_ps)
{
    const int	dimen = src_width.dim();

    int		nbh_sz = nbh.card();
    int	*const nbh_offsets = new int[nbh_sz];

    {
	int	*o_scan = nbh_offsets;
	IA_PSIter<IA_IntPoint>	iter(nbh);
	IA_IntPoint	ip;
	while (iter(ip)) {
	    *o_scan = ip[0];
	    for (unsigned i=1; i<dimen; i++) {
		*o_scan *= src_width[i];
		*o_scan += ip[i];
	    }
	    o_scan++;
	}
    }

    IA_PSIter<IA_IntPoint>	iter(dest_ps);
    IA_IntPoint	ip;
    RTYPE *const	dest_data = new RTYPE[dest_ps.card()];
    RTYPE *	valp = dest_data;
    while (iter(ip)) {
	int	offset= ip[0] - src_infimum[0];
	unsigned i;
	for (i=1; i<dimen; i++) {
	    offset *= src_width[i];
	    offset += ip[i] - src_infimum[i];
	}
	const IBTYPE *const base = src_data + offset;

	// _IVAL_=(base[nbh_offsets[i]])
	// _IRESULT_=(*valp)

	INITIALIZE ;

	for (i=0; i<nbh_sz; i++) {
	    ACCUMULATE ;
	}
	{
	    RESULT ;
	}
	valp++;
    }
    delete[] nbh_offsets;

    return IA_Image<IA_IntPoint,RTYPE>(dest_ps, dest_data, dest_ps.card(), 1);
}

static IA_Image<IA_IntPoint,RTYPE>
backw_CONV_inv(const IA_Image<IA_IntPoint,IBTYPE> &img,
	 const IA_IntPointSet &invnbh,
	 IA_IntPointSet dest_ps)
{
    IA_IntPoint	inf_ = dest_ps.inf()+invnbh.inf();
    IA_IntPoint	sup_ = dest_ps.sup()+invnbh.sup();
    IA_IntPointSet	src_ps = IA_IntPointSet(inf_,sup_);

    IBTYPE	*const src_data = new IBTYPE[src_ps.card()];

    zero_extend(img, src_ps, src_data, ZERO);
    IA_Image<IA_IntPoint,RTYPE>	rval =
	backw_CONV_inv_core(inf_, sup_-inf_ + 1, src_data, invnbh, dest_ps);

    delete[] src_data;

    return rval;
}

IA_Image<IA_IntPoint,RTYPE>
CONV(const IA_CoreImage<IA_IntPoint,IBTYPE> &img_,
     const IA_Neighborhood<IA_IntPoint,IA_IntPoint> &nbh,
     IA_IntPointSet dest_ps)
{
    IA_Image<IA_IntPoint,IBTYPE>	img(img_);

    if (nbh.type() == IA_InvariantNbh<IA_IntPoint,IA_IntPoint>::s_type()) {
	return backw_CONV_inv
	    (img,
	     ((IA_InvariantNbh<IA_IntPoint,IA_IntPoint>*) nbh.bnbh)->value,
	     dest_ps);
    } else {
	RTYPE *const	dest_data = new RTYPE[dest_ps.card()];
	RTYPE	*valp = dest_data;
	IA_PSIter<IA_IntPoint>	dest_iter(dest_ps);
	IA_IntPoint	base_ip;
	while (dest_iter(base_ip)) {
	    IA_IntPointSet	ps = nbh(base_ip);

	    IA_PSIter<IA_IntPoint>	nbh_iter(ps);
	    IA_IntPoint	nbh_ip;

	    // _IVAL_=img(nbh_ip)
	    // _IRESULT_=(*valp)

	    INITIALIZE ;

	    while ( nbh_iter(nbh_ip) ) {
		if (! img.domain().contains(nbh_ip))
		    continue;
		ACCUMULATEVAR ;
	    }
	    {
		RESULT ;
	    }
	    valp++;
	}
	return IA_Image<IA_IntPoint,RTYPE>(dest_ps, dest_data,
					   dest_ps.card(), 1);
    }
}

//
// foward neighborhood reductions
//

static IA_Image<IA_IntPoint,RTYPE>
forw_CONV_inv_core(IA_IntPoint src_infimum,
	      IA_IntPoint src_width,
	      const IBTYPE *src_data, // length is prod(src_width)
	      const IA_IntPointSet &nbh,
	      IA_IntPointSet dest_ps)
{
    const int	dimen = src_width.dim();

    int		nbh_sz = nbh.card();
    int	*const nbh_offsets = new int[nbh_sz];

    {
	int	*o_scan = nbh_offsets;
	IA_PSIter<IA_IntPoint>	iter(nbh);
	IA_IntPoint	ip;
	while (iter(ip)) {
	    *o_scan = -ip[0];
	    for (unsigned i=1; i<dimen; i++) {
		*o_scan *= src_width[i];
		*o_scan -= ip[i];
	    }
	    o_scan++;
	}
    }

    IA_PSIter<IA_IntPoint>	iter(dest_ps);
    IA_IntPoint	ip;
    RTYPE *const	dest_data = new RTYPE[dest_ps.card()];
    RTYPE *	valp = dest_data;
    while (iter(ip)) {
	int	offset= ip[0] - src_infimum[0];
	unsigned i;
	for (i=1; i<dimen; i++) {
	    offset *= src_width[i];
	    offset += ip[i] - src_infimum[i];
	}
	const IBTYPE *const base = src_data + offset;

	// _IVAL_=(base[nbh_offsets[i]])
	// _IRESULT_=(*valp)

	INITIALIZE ;

	for (i=0; i<nbh_sz; i++) {
	    ACCUMULATE ;
	}
	{
	    RESULT ;
	}
	valp++;
    }
    delete[] nbh_offsets;

    return IA_Image<IA_IntPoint,RTYPE>(dest_ps, dest_data, dest_ps.card(), 1);
}

static
IA_Image<IA_IntPoint,RTYPE>
forw_CONV_inv(const IA_Image<IA_IntPoint,IBTYPE> &img,
	const IA_IntPointSet &invnbh,
	IA_IntPointSet dest_ps)
{
    IA_IntPoint	inf_ = dest_ps.inf()-invnbh.sup();
    IA_IntPoint	sup_ = dest_ps.sup()-invnbh.inf();
    IA_IntPointSet	src_ps = IA_IntPointSet(inf_,sup_);

    IBTYPE	*const src_data = new IBTYPE[src_ps.card()];

    zero_extend(img, src_ps, src_data, ZERO);
    IA_Image<IA_IntPoint,RTYPE>	rval =
	forw_CONV_inv_core(inf_, sup_-inf_ + 1, src_data, invnbh, dest_ps);

    delete[] src_data;

    return rval;
}

IA_Image<IA_IntPoint,RTYPE>
CONV(const IA_Neighborhood<IA_IntPoint,IA_IntPoint> &nbh,
     const IA_CoreImage<IA_IntPoint,IBTYPE> &img_,
     IA_IntPointSet dest_ps)
{
    IA_Image<IA_IntPoint,IBTYPE>	img(img_);

    if (nbh.type() == IA_InvariantNbh<IA_IntPoint,IA_IntPoint>::s_type()) {
	return forw_CONV_inv
	    (img,
	     ((IA_InvariantNbh<IA_IntPoint,IA_IntPoint>*)nbh.bnbh)->value,
	     dest_ps);
    } else {
	unsigned	size = dest_ps.card();
	RTYPE *const	dest_data = new RTYPE[size];

	for (int i=0; i<size; i++) {
	    // _IRESULT_=dest_data[i]
	    FORW_INITIALIZE ;
	}

	IA_PSIter<IA_IntPoint>	src_iter(img.domain());
	IA_IntPoint	base_ip;
	while (src_iter(base_ip)) {
	    IA_IntPointSet	ps = nbh(base_ip);

	    IA_PSIter<IA_IntPoint>	nbh_iter(ps);
	    IA_IntPoint	nbh_ip;
	    IBTYPE	ival = img(base_ip);

	    // _IVAL_=ival
	    // _IRESULT_=dest_data[offset]

	    while ( nbh_iter(nbh_ip) ) {
		if (! dest_ps.contains(nbh_ip))
		    continue;
		unsigned	offset = dest_ps.index(nbh_ip);
//		cout << "nbh(" << base_ip << ") =  " << tv << " and nbh()("
//		     << nbh_ip <<") = " << nbh_val << endl;
		FORW_ACCUMULATEVAR ;
	    }
	}
	return IA_Image<IA_IntPoint,RTYPE>(dest_ps, dest_data, size, 1);
    }
}
