// Copyright 1984-2019 The MathWorks, Inc.
// All Rights Reserved.

#include <iostream>
#include "mps/client.h"

void handleErrors(mpsClientRuntime* mpsruntime, mpsClientContext* client)
{

    mpsErrorInfo err;
    mpsruntime->getLastErrorInfo(client, &err);
    std::cout << "Error message : " << err.message << std::endl;

    switch (err.type) {

    case MPS_HTTP_ERROR_INFO:
        std::cout << "HTTP response code : " << err.details.http.responseCode << std::endl;
        std::cout << "HTTP response message : " << err.details.http.responseMessage << std::endl;
        break;

    case MPS_MATLAB_ERROR_INFO: {
        std::cout << "MATLAB error message : " << err.details.matlab.message << std::endl;
        std::cout << "MATLAB identifier : " << err.details.matlab.identifier << std::endl;

        size_t stackDepth = err.details.matlab.matlabStackDepth;
        std::cout << "MATLAB stack depth : " << stackDepth << std::endl;

        for (size_t i = 0; i < stackDepth; i++) {
            std::cout << "MATLAB function : " << err.details.matlab.matlabStack[i].function
                      << std::endl;
            std::cout << "MATLAB file : " << err.details.matlab.matlabStack[i].file << std::endl;
            std::cout << "MATLAB line in the function : " << err.details.matlab.matlabStack[i].line
                      << std::endl;
            std::cout << std::endl;
        }
    } break;

    case MPS_GENERIC_ERROR_INFO:
        std::cout << "General error thrown : " << err.details.general.genericErrorMsg << std::endl;
        break;
    }
    mpsruntime->destroyLastErrorInfo(&err);
}

double getMagicSquareSize()
{

    double size;
    std::cout << "Enter the size of magic square : ";
    std::cin >> size;
    std::cout << std::endl;
    return size;
}

void magicSq(mpsClientRuntime* mpsruntime, mpsClientContext* client)
{

    int nrhs = 1, nlhs = 1;
    const mpsArray** prhs = new const mpsArray*[nrhs];
    mpsArray** plhs = new mpsArray*[nlhs];

    double size = getMagicSquareSize();
    prhs[0] = mpsCreateDoubleScalar(size);

    mpsStatus status =
        mpsruntime->feval(client, "http://localhost:9910/magic/mymagic", nlhs, plhs, nrhs, prhs);

    if (status == MPS_OK) {

        double* magicData = mpsGetPr(plhs[0]);
        for (mpsIndex i = 0; i < size; i++) {
            for (mpsIndex j = 0; j < size; j++) {
                mpsIndex subs[] = {i, j};
                mpsIndex id = mpsCalcSingleSubscript(plhs[0], 2, subs);
                std::cout << magicData[id] << "\t";
            }
            std::cout << std::endl;
        }
        mpsDestroyArray(plhs[0]);

    } else {
        handleErrors(mpsruntime, client);
    }

    mpsDestroyArray(const_cast<mpsArray*>(prhs[0]));
    delete[] prhs;
    delete[] plhs;
}

int main(void)
{
    /**
    Following call needs to be made only once per application. It performs following
    two important tasks:
    1. Initialization of underlying HTTP framework
    2. Create a function table of MPS client API
*/
    mpsClientRuntime* mpsruntime = mpsInitialize();

    /**
    Create client configuration. The default configuration uses following
    parameters:
    1. Response timeout of 120 sec (2 mins)
    2. Response size limit of 64 Mb
*/
    mpsClientConfig* config;
    mpsruntime->createConfig(&config);

    /**
    Initialize client context using configuration. The client
    context gets used for invoking the MATLAB function
*/
    mpsClientContext* client;
    mpsStatus status = mpsruntime->createContext(&client, config);

    if (status == MPS_OK) {

        /**
            Invoke MATLAB function
        */
        magicSq(mpsruntime, client);

        /**
    Destroy mpsClientContext and mpsClientConfig
*/
        mpsruntime->destroyConfig(config);
        mpsruntime->destroyContext(client);

    } else {
        std::cout << "Error initializing mpsClientContext" << std::endl;
    }

    /**
        Termination of underlying HTTP framework
    */
    mpsTerminate();

    return 0;
}