#include <imageanalysis/ImageAnalysis/ImageRebinner.h>

namespace casa {

template<class T> ImageRebinner<T>::ImageRebinner(
	const SPCIIT image,
	const casacore::Record *const region,
	const casacore::String& maskInp,
	const casacore::String& outname, casacore::Bool overwrite
) : ImageTask<T>(
		image, "", region, "", "", "",
		maskInp, outname, overwrite
	),
	_factors(), _crop(false) {
        this->_construct();
    }

template<class T> void ImageRebinner<T>::setFactors(const casacore::Vector<casacore::Int>& f) {
	ThrowIf(
		f.empty(),
		"Rebinning factor vector cannot be empty"
	);
	ThrowIf(
		anyTrue(f <= 0),
		"All factors must be positive"
	);
	ThrowIf(
		allTrue(f == 1),
		"All rebinning factors are 1, which "
		"means rebinning cannot occur"
	);
	casacore::uInt ndim = this->_getImage()->ndim();
	ThrowIf(
		f.size() > ndim,
		"Factor vector length must be less than or "
		"equal to the number of input image axes"
	);
	casacore::Vector<casacore::Int> fcopy = f.copy();
	casacore::uInt mysize = f.size();
	if (mysize < ndim) {
		fcopy.resize(ndim, true);
		for (casacore::uInt i=mysize; i<ndim; i++) {
			fcopy[i] = 1;
		}
		*this->_getLog() << casacore::LogOrigin(getClass(), __func__)
			<< casacore::LogIO::NORMAL << "Not all rebinning factors were specified, "
			<< "filling in those not specified with factors of "
			<< "1, so will rebin according to factor=" << fcopy
			<< casacore::LogIO::POST;
	}
	const casacore::CoordinateSystem& csys = this->_getImage()->coordinates();
	ThrowIf(
		csys.hasPolarizationCoordinate() && fcopy[csys.polarizationAxisNumber()] > 1,
		"A polarization axis cannot be rebinned"
	);
	_factors = fcopy;
}

template<class T> SPIIT ImageRebinner<T>::rebin() const {
	ThrowIf(_factors.empty(), "Logic Error: factors have not been set");
    SPCIIT image = this->_getImage();
    casacore::uInt ndim = image->ndim();
    ThrowIf(
        ndim != _factors.size(),
        "You have provided " + casacore::String::toString(_factors.size())
        + " factors. You must provide exactly " + casacore::String::toString(ndim)
        + (
            this->_getDropDegen()
                ? ". If you wish to drop degenerate axes, specify binning factors of 1 for them"
                : ""
        )
    );
    casacore::IPosition myFactors;
    if (this->_getDropDegen()) {
        casacore::IPosition shape = image->shape();
        casacore::IPosition degenAxes;
        for (casacore::uInt i=0; i<ndim; i++) {
            if (shape[i] == 1) {
                degenAxes.append(casacore::IPosition(1, i));
            }
        }
        myFactors = _factors.removeAxes(degenAxes);
    }
    else { 
        myFactors = _factors;
    }
    SPIIT subImage(
		SubImageFactory<T>::createImage(
			*this->_getImage(), "", *this->_getRegion(),
			this->_getMask(), this->_getDropDegen(),
			false, false, this->_getStretch()
		)
	);
	if (_crop) {
        ndim = subImage->ndim();
		casacore::IPosition shape = subImage->shape();
		casacore::IPosition trc = shape - 1;
		casacore::Vector<casacore::Int> mods(ndim);
		for (casacore::uInt i=0; i<ndim; i++) {
			mods[i] = shape[i] % myFactors[i];
			if (mods[i] > 0) {
				trc[i] -= mods[i];
			}
		}
		if (anyTrue(mods > 0)) {
			casacore::LCBox box(casacore::IPosition(ndim, 0), trc, shape);
			subImage = 	SubImageFactory<T>::createImage(
				*subImage, "", box.toRecord(""),
				"", false, false, false, false
			);
		}
	}
	casacore::RebinImage<T> binIm(*subImage, myFactors);
	SPIIT outIm = this->_prepareOutputImage(binIm, this->_getDropDegen());
	/*
	// remove any axes that have been binned into a remaining degenerate axis,
	// CAS-5836
	if (this->_getDropDegen()) {
		casacore::IPosition outShape = outIm->shape();
		casacore::uInt outDim = outIm->ndim();
		for (casacore::uInt i=0; i<outDim; ++i) {
			if (outShape[i] == 1) {
				casacore::Record empty;
				outIm = SubImageFactory<T>::createImage(
					*outIm, this->_getOutname(), casacore::Record(),
					"", true, true, false, false
				);
				break;
			}
		}
	}
	*/
	return outIm;
}

}