#include <spdlog/spdlog.h>
#include <maque_type.h>
#include <maque_system.h>
#include <maque_yuv.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 "PlanarYUVLuminanceSource.h" // yuv

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

/// 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));
}

int get_yuv_buflen(int width, int height)
{
    return (((width + YUV_WIDTH_ALIGN_MASK) & ~YUV_WIDTH_ALIGN_MASK) * ((height
            +  YUV_HEIGHT_ALIGN_MASK) & ~YUV_HEIGHT_ALIGN_MASK) * 3) >> 1;
}

string qr_analysis()
{
    // capture image
    string ret;
    static bool created = false;
    MaQueYuvCreate_s stYuvCreate;
    memset(&stYuvCreate, 0, sizeof(MaQueYuvCreate_s));
    stYuvCreate.nHeight = 1080;
    stYuvCreate.nWidth = 1920;

    if (!created && LibXmMaQue_YUV_create(0, &stYuvCreate) == XM_SUCCESS) {
        created = true;
    }

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

    MaQueYuvParam_s stYuvParam;
    memset(&stYuvParam, 0, sizeof(MaQueYuvParam_s));
    stYuvParam.nWidth = 1920;
    stYuvParam.nHeight = 1080;
    LibXmMaQue_YUV_setParam(0, &stYuvParam);
    int yuvSize = get_yuv_buflen(1920, 1080);

    /// frame
    MaQueYuvFrame_s stYuvFrame;
    memset(&stYuvFrame, 0, sizeof(MaQueYuvFrame_s));
    stYuvFrame.nBufLen = yuvSize;
    stYuvFrame.pBuffer = (uint8_t *)malloc(yuvSize);
    if (LibXmMaQue_YUV_getFrame(0, &stYuvFrame) == XM_SUCCESS) {
        ArrayRef<char> data((char*)stYuvFrame.pBuffer, stYuvFrame.nBufLen);
        free(stYuvFrame.pBuffer);
        Ref<LuminanceSource> source(new PlanarYUVLuminanceSource(data, stYuvParam.nWidth, stYuvParam.nHeight, 0, 0, stYuvParam.nWidth, stYuvParam.nHeight,false));
        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);
            //MaQue_Demo_Mem_release(stYuvFrame.handleMem);
        }
        catch (const ReaderException& e) {
            ret = "qr: " + string(e.what());
        }
        catch (const zxing::IllegalArgumentException& e) {
            ret = "qr: " + string(e.what());
        }
        catch (const zxing::Exception& e) {
            ret = "qr: " + string(e.what());
        }
        catch (const std::exception& e) {
            ret = "qr: " + string(e.what());
        }

        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: LibXmMaQue_YUV_getFrame error";
        spdlog::error(ret);
    }

    return ret;
}