/*
 * ImageReorderer.cc
 *
 *  Created on: May 7, 2010
 *      Author: dmehring
 */

#include <imageanalysis/ImageAnalysis/ImageTransposer.h>

#include <casacore/images/Images/ImageUtilities.h>
#include <casacore/images/Images/PagedImage.h>
#include <casacore/images/Images/TempImage.h>

#include <imageanalysis/ImageAnalysis/SubImageFactory.h>

using namespace casacore;
namespace casa {

const String ImageTransposer::_class = "ImageTransposer";

ImageTransposer::ImageTransposer(
        const SPCIIF image, const String& order, const String& outputImage
)
    : ImageTask<Float>(
        image, "", 0, "", "", "",
        "", outputImage, false
    ),
    _order(Vector<Int>(0)), _reverse(IPosition(0)) {
    LogOrigin origin(_class, String(__FUNCTION__) + "_1");
    *_getLog() << origin;
    //_construct();
    *_getLog() << origin;
    Regex intRegex("^(-?[0-9])+$");
    if (order.matches(intRegex)) {
        _order = _getOrder(order);
    }
    else {
        *_getLog() << "Incorrect order specification " << order
            << ". All characters must be digits." << LogIO::EXCEPTION;
    }
}

ImageTransposer::ImageTransposer(
        const SPCIIF image, const Vector<String> order,
    const String& outputImage
)
:  ImageTask<Float>(
        image, "", 0, "", "", "",
        "", outputImage, false
    ), _order(Vector<Int>()), _reverse(IPosition(0)) {
    LogOrigin origin(_class, String(__FUNCTION__) + "_2");
    *_getLog() << origin;
    //_construct();
    *_getLog() << origin;
    Vector<String> orderCopy = order;
    std::vector<Bool> rev(orderCopy.size());
    uInt nRev = 0;

    for (uInt i=0; i<orderCopy.size(); i++) {
        if (orderCopy[i].startsWith("-")) {
            orderCopy[i] = orderCopy[i].substr(1);
            rev[i] = true;
            nRev++;
        }
        else {
            rev[i] = false;
        }
    }
    _order = _getImage()->coordinates().getWorldAxesOrder(orderCopy, true);
    uInt n = 0;
    if (nRev > 0) {
        _reverse.resize(nRev);
        for (uInt i=0; i<orderCopy.size(); i++) {
            if (rev[i]) {
                _reverse[n] = _order[i];
                n++;
            }
        }
    }
    *_getLog() << "Old to new axis mapping is " << _order << LogIO::NORMAL;
}

ImageTransposer::ImageTransposer(
        const SPCIIF image, uInt order,
    const String& outputImage
)
:  ImageTask<Float>(
        image, "", 0, "", "", "",
        "", outputImage, false
    ), _order(Vector<Int>()), _reverse(IPosition(0)) {
    LogOrigin origin(_class, String(__FUNCTION__) + "_3");
    *_getLog() << origin;
    //_construct();
    *_getLog() << origin;
    _order = _getOrder(order);
}

SPIIF ImageTransposer::transpose() const {
    *_getLog() << LogOrigin(_class, __FUNCTION__);
    // get the image data
    Array<Float> dataCopy = _getImage()->get();
    CoordinateSystem newCsys = _getImage()->coordinates();
    IPosition shape = _getImage()->shape();
    if (_reverse.size() > 0) {
        Vector<Double> refPix = newCsys.referencePixel();
        Vector<Double> inc = newCsys.increment();
        for (
            IPosition::const_iterator iter=_reverse.begin();
            iter!=_reverse.end(); iter++
        ) {
            refPix[*iter] = shape[*iter] - 1 - refPix[*iter];
            inc[*iter] *= -1;
        }
        newCsys.setReferencePixel(refPix);
        newCsys.setIncrement(inc);
    }
    newCsys.transpose(_order, _order);
    IPosition newShape(_order.size());
    for (uInt i=0; i<newShape.size(); ++i) {
        newShape[i] = shape[_order[i]];
    }
    SPIIF output(
        new TempImage<Float>(TiledShape(newShape), newCsys)
    );
    if (_reverse.size() > 0) {
        dataCopy = reverseArray(dataCopy, _reverse);
    }
    output->put(reorderArray(dataCopy, _order));
    if (_getImage()->hasPixelMask()) {
        std::unique_ptr<Lattice<Bool> > maskLattice(
            _getImage()->pixelMask().clone()
        );
        Array<Bool> maskCopy = maskLattice->get();
        if (_reverse.size() > 0) {
            maskCopy = reverseArray(maskCopy, _reverse);
        }
        dynamic_cast<TempImage<Float> *>(output.get())->attachMask(
            ArrayLattice<Bool>(reorderArray(maskCopy, _order))
        );
    }
    ImageUtilities::copyMiscellaneous(*output, *this->_getImage());
    return this->_prepareOutputImage(*output);
}

ImageTransposer::~ImageTransposer() {}

Vector<Int> ImageTransposer::_getOrder(uInt order) const {
    *_getLog() << LogOrigin(_class, String(__func__));
    uInt naxes = _getImage()->ndim();
    uInt raxes = uInt(log10(order)) + 1;
    if (naxes != raxes) {
        istringstream is;
        is >> order;
        if (! String(is.str()).contains("0")) {
            raxes++;
        }
    }
    if (raxes != naxes) {
        *_getLog() << "Image has " << naxes << " axes but " << raxes
                << " were given for reordering. Number of axes to reorder must match the number of image axes"
                << LogIO::EXCEPTION;
    }
    if (raxes > 10) {
        *_getLog() << "Only images with less than 10 axes can currently be reordered. This image has "
                << naxes << " axes" << LogIO::EXCEPTION;
    }
    Vector<Int> myorder(naxes);
    uInt mag = 1;
    for (uInt i=1; i<myorder.size(); i++) {
        mag *= 10;
    }
    uInt scratchOrder = order;
    for (uInt i=0; i<myorder.size(); i++) {
        uInt index = scratchOrder/mag;
        if (index >= naxes) {
            *_getLog() << "Image does not contain zero-based axis number " << index
                    << " but this was incorrectly specified in order parameter. "
                    << order << " All digits in the order parameter must be greater "
                    << "than or equal to zero and less than the number of image axes."
                    << LogIO::EXCEPTION;
        }
        for (uInt j=0; j<i; j++) {
            if ((Int)index == myorder[j]) {
                *_getLog() << "Axis number " << index
                        << " specified multiple times in order parameter "
                        << order << " . It can only be specified once."
                        << LogIO::EXCEPTION;
            }
        }
        myorder[i] = index;
        scratchOrder -= index*mag;
        mag /= 10;
    }
    return myorder;
}

Vector<Int> ImageTransposer::_getOrder(const String& order) {
    String orderCopy = order;
    if (orderCopy.contains('-', 0)) {
        uInt maxn = orderCopy.freq('-') + 1;
        String *parts = new String[maxn];
        split(order, parts, maxn, '-');
        // disregard the first element because that won't have a -
        _reverse.resize(maxn - 1);
        orderCopy = parts[0];
        for (uInt i=1; i<maxn; i++) {
            _reverse[i-1] = String::toInt(parts[i].substr(0, 1));
            orderCopy += parts[i];
        }
        delete [] parts;

    }
    return _getOrder(String::toInt(orderCopy));
}

}