#include <spdlog/spdlog.h>
#include <maque_type.h>
#include <maque_system.h>
#include <maque_jpeg_enc.h>
#include <zxing/common/Counted.h>
#include <zxing/Binarizer.h>
#include <zxing/MultiFormatReader.h>
#include <zxing/Result.h>
#include <zxing/ReaderException.h>
#include <zxing/common/GlobalHistogramBinarizer.h>
#include <zxing/common/HybridBinarizer.h>
#include <exception>
#include <zxing/Exception.h>
#include <zxing/common/IllegalArgumentException.h>
#include <zxing/BinaryBitmap.h>
#include <zxing/DecodeHints.h>
#include <zxing/qrcode/QRCodeReader.h>
#include <zxing/multi/qrcode/QRCodeMultiReader.h>
#include <zxing/multi/ByQuadrantReader.h>
#include <zxing/multi/MultipleBarcodeReader.h>
#include <zxing/multi/GenericMultipleBarcodeReader.h>
#include "jpgd.h"
#include "ImageReaderSource.h"

using namespace std;
using namespace zxing;
using namespace zxing::multi;
using namespace zxing::qrcode;

/// TODO: refinment of extern
extern "C" {
    XM_S32 MaQue_Demo_Mem_release(XM_HANDLE handle);
}


vector<Ref<Result> > decode(Ref<BinaryBitmap> image, DecodeHints hints)
{
    Ref<Reader> reader(new MultiFormatReader);
    return vector<Ref<Result> >(1, reader->decode(image, hints));
}

string qr_analysis()
{
    // capture image
    string ret;
    static bool created = false;
    MaQueJpegEncCreate_s cPara;
    cPara.nHeight = 1080;
    cPara.nWidth = 1920;
    if (!created && LibXmMaQue_JpegEnc_create(0, &cPara)==0) { /* pstCreate参数暂时没有启用，可传NULL。*/
        created = true;
    }

    if(!created) {
        ret = "qr: LibXmMaQue_JpegEnc_create err!\n";
        spdlog::error(ret);
        return ret;
    }

    MaQueJpegFrame_s jpgFrame;
    memset(&jpgFrame, 0x0, sizeof(jpgFrame));
    jpgFrame.nWidth = 640;
    jpgFrame.nHeight = 360;

    if (LibXmMaQue_JpegEnc_getFrame(0, &jpgFrame) == XM_SUCCESS) {
        int width, height, comps;
        char *buffer = reinterpret_cast<char*>(jpgd::decompress_jpeg_image_from_memory(jpgFrame.pBuffer,
                                               jpgFrame.nDataLen, &width, &height, &comps, 4));
        auto image = zxing::ArrayRef<char>(buffer, 4 * width * height);
        MaQue_Demo_Mem_release(jpgFrame.handleMem);
        if (!image) {
            ret = "qr: failed to decode jpg";
            spdlog::error(ret);
            free(buffer);
            return ret;
        }
        else {
            Ref<LuminanceSource> source(new ImageReaderSource(image, width, height, comps));
            vector<Ref<Result> > results;
            try {
                Ref<Binarizer> binarizer;
                binarizer = new GlobalHistogramBinarizer(source);
                DecodeHints hints(DecodeHints::QR_CODE_HINT);
                hints.setTryHarder(true);
                Ref<BinaryBitmap> binary(new BinaryBitmap(binarizer));
                results = decode(binary, hints);
            }
            catch (const ReaderException& e) {
                ret = "zxing::ReaderException: " + string(e.what());
            }
            catch (const zxing::IllegalArgumentException& e) {
                ret = "zxing::IllegalArgumentException: " + string(e.what());
            }
            catch (const zxing::Exception& e) {
                ret = "zxing::Exception: " + string(e.what());
            }
            catch (const std::exception& e) {
                ret = "std::exception: " + string(e.what());
            }

            free(buffer);
            if(!ret.empty()) {
                spdlog::error(ret);
                return ret;
            }

            for (size_t i = 0; i < results.size(); i++) {
                spdlog::info("Format: {}", BarcodeFormat::barcodeFormatNames[results[i]->getBarcodeFormat()]);
                for (int j = 0; j < results[i]->getResultPoints()->size(); j++) {
                    spdlog::info("  Point[{}]: {} {}", j, results[i]->getResultPoints()[j]->getX(),
                                 results[i]->getResultPoints()[j]->getY());
                }

                spdlog::info(results[i]->getText()->getText());
            }
        }
    }
    else {
        ret = "qr: failed to get jpg frame";
        spdlog::error(ret);
    }

    return ret;
}