#include "libxisf.h"
#include "../src/rawimage.h"
#ifdef WIN32
#include <windows.h>
#endif
#include <fitsio2.h>

bool OpenGLES = false;

bool loadXISF(const LibXISF::ByteArray &data, std::shared_ptr<RawImage> &rawImage)
{
    try
    {
        LibXISF::XISFReader xisf;
        xisf.open(data);

        const LibXISF::Image &xisfImage = xisf.getImage(0);

        RawImage::DataType type;
        switch(xisfImage.sampleFormat())
        {
        case LibXISF::Image::UInt8: type = RawImage::UINT8; break;
        case LibXISF::Image::UInt16: type = RawImage::UINT16; break;
        case LibXISF::Image::UInt32: type = RawImage::UINT32; break;
        case LibXISF::Image::Float32: type = RawImage::FLOAT32; break;
        case LibXISF::Image::Float64: type = RawImage::FLOAT64; break;
        default: return false;
        }

        LibXISF::Image tmpImage = xisfImage;
        tmpImage.convertPixelStorageTo(LibXISF::Image::Planar);

        if(tmpImage.colorSpace() == LibXISF::Image::ColorSpace::Gray)
        {
            rawImage = std::make_shared<RawImage>(tmpImage.width(), tmpImage.height(), 1, type);
            std::memcpy(rawImage->data(), tmpImage.imageData(), tmpImage.imageDataSize() / tmpImage.channelCount());
        }
        else if(tmpImage.channelCount() == 3 || tmpImage.channelCount() == 4)
        {
            rawImage = RawImage::fromPlanar(tmpImage.imageData(), tmpImage.width(), tmpImage.height(), tmpImage.channelCount(), type);
        }
        return true;
    }
    catch (LibXISF::Error &err)
    {
#ifdef WIN32
        char text[1024];
        sprintf_s(text, 1000, "Failed to open XISF image %s", err.what());
        OutputDebugStringA(text);
#endif
        return false;
    }
    return false;
}

bool loadFITS(const LibXISF::ByteArray &data, std::shared_ptr<RawImage> &rawImage)
{
    fitsfile *file;

    int status = 0;
    int hdutype = -1;
    int num = 0;
    long naxes[3] = {0};

    auto checkError = [&status]()
    {
        char err[100];
        fits_get_errstatus(status, err);
#ifdef WIN32
        char text[1000];
        sprintf_s(text, 1000, "Failed to load FITS file %s", err);
        OutputDebugStringA(text);
#endif
        return false;
    };

    const void *dataPtr = data.data();
    size_t size = data.size();
    fits_open_memfile(&file, "file.fits", READONLY, (void**)&dataPtr, &size, 0, nullptr, &status);
    if(status)return checkError();
    fits_get_num_hdus(file, &num, &status);
    if(status)return checkError();

    int imgtype;
    int naxis;
    for(int i=1; i <= num; i++)
    {
        fits_movabs_hdu(file, i, &hdutype, &status);if(status)return checkError();
        if(hdutype == IMAGE_HDU)
        {
            naxes[0] = naxes[1] = naxes[2] = 0;
            fits_get_img_param(file, 3, &imgtype, &naxis, naxes, &status);if(status)return checkError();
            fits_get_img_equivtype(file, &imgtype, &status);if(status)return checkError();

            if(hdutype == IMAGE_HDU && naxis >= 2 && naxis <= 3 && status == 0)
            {
                RawImage::DataType type;
                int fitstype;
                long fpixel[3] = {1,1,1};
                switch(imgtype)
                {
                case BYTE_IMG:
                    type = RawImage::UINT8;
                    fitstype = TBYTE;
                    break;
                case SHORT_IMG:
                    type = RawImage::UINT16;
                    fitstype = TSHORT;
                    break;
                case USHORT_IMG:
                    type = RawImage::UINT16;
                    fitstype = TUSHORT;
                    break;
                case ULONG_IMG:
                    type = RawImage::UINT32;
                    fitstype = TUINT;
                    break;
                case FLOAT_IMG:
                    type = RawImage::FLOAT32;
                    fitstype = TFLOAT;
                    break;
                case DOUBLE_IMG:
                    type = RawImage::FLOAT64;
                    fitstype = TDOUBLE;
                    break;
                default:
                    return false;
                    break;
                }

                size_t size = naxes[0]*naxes[1];
                size_t w = naxes[0];
                size_t h = naxes[1];

                RawImage img(w, h, naxis == 2 ? 1 : naxes[2], type);
                uint8_t *data = static_cast<uint8_t*>(img.data());
                for (int i=1; i==1 || i<=naxes[2]; i++)
                {
                    fpixel[2] = i;
                    fits_read_pix(file, fitstype, fpixel, size, NULL, data + img.size() * RawImage::typeSize(type) * (i-1), NULL, &status);
                    if(status)return checkError();
                }
                if(fitstype == TSHORT)
                {
                    uint16_t *s = static_cast<uint16_t*>(img.data());
                    size_t size = img.size() * img.channels();
                    for(size_t i=0; i<size; i++)
                        s[i] -= INT16_MIN;
                }

                if(img.channels() == 1)
                    rawImage = std::make_shared<RawImage>(std::move(img));
                else
                    rawImage = RawImage::fromPlanar(img);

                return true;
            }
        }
    }

    return false;
}

