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

using System;
using System.Net;
using MathWorks.MATLAB.ProductionServer.Client;
using MathWorks.MATLAB.ProductionServer.Client.REST;
using System.Threading;
using System.Collections.Generic;

namespace MagicSquareExample
{
    public class MagicAsync
    {
        static void Main(string[] args)
        {
            //Async request using RESTful API and protobuf.

            //URL of the MATLAB Production Server.
            String mpsBaseUrl = "http://localhost:9910";
            MATLABParams mlParams = MATLABParams.Create(1, new List<Type> { typeof(double[,]) }, 2);

            Console.WriteLine("Sending the initial HTTP POST request to the URL " + mpsBaseUrl + "/mymagic/mymagic?mode=async.");
            var response = MakeHTTPRequest(mpsBaseUrl + "/mymagic/mymagic?mode=async", "POST", mlParams);
            Console.WriteLine("The HTTP status code of the request is " + response.StatusCode + ".\n\n");

            // Parse the response body using the MATLABRequestHandle class
            // to retrieve the request URL, last modified sequence value and state of the request.
            MATLABRequestHandle matlabRequestHandle = MATLABRequestHandle.Create(response.GetResponseStream());
            Console.WriteLine("The response body of the initial POST request contains the following values after deserialization using MATLABRequestHandle class: ");
            Console.WriteLine(matlabRequestHandle.ToString() + ".\n\n");

            // Query for the state of the request.
            Console.WriteLine("Send GET status request every 1 second to look for state change of the request to either READY_STATE or ERROR_STATE.");
            Console.WriteLine("Sending GET request to the following URL to fetch the status of the request " + mpsBaseUrl + matlabRequestHandle.RequestURL + "/info?format=protobuf.");
            Console.WriteLine("The response body is parsed with MATLABRequest class.");
            MATLABRequest mlRequestStatus;
            do
            {
                var statusRequestResponse = MakeHTTPRequest(mpsBaseUrl + matlabRequestHandle.RequestURL + "/info?format=protobuf", "GET", null);
                mlRequestStatus = MATLABRequest.Create(statusRequestResponse.GetResponseStream());
                Console.WriteLine("State: " + mlRequestStatus.State + " Last modified sequence value: " + mlRequestStatus.LastModified + ".");
                Thread.Sleep(1000);
            } while (mlRequestStatus.State < MATLABRequestState.READY_STATE);

            // Once the state changes to READY_STATE or ERROR_STATE, make a GET call to /result. If the state is ERROR_STATE, 
            //use HTTPErrorInfo class, else use MATLABResult class to parse the response body.
            Console.Write("\n\n");
            Console.WriteLine("Now send a GET request to fetch the result using the URL " + mpsBaseUrl + matlabRequestHandle.RequestURL + "/result?format=protobuf.");
            response = MakeHTTPRequest(mpsBaseUrl + matlabRequestHandle.RequestURL + "/result?format=protobuf", "GET", null);

            if (mlRequestStatus.State == MATLABRequestState.READY_STATE)
            {
                // Parse the response body with the help of MATLABResult class. The Create function
                // takes as input the MATLABParams object that was initially created.
                // If there is any error in MATLAB, call to Result<>() throws MATLABException which contains the error message
                // displayed in MATLAB.

                MATLABResult mlResult;
                try
                {
                    mlResult = MATLABResult.Create(mlParams, response.GetResponseStream());
                    double[,] result = mlResult.Result<double[,]>();
                    Console.WriteLine("Printing the 2-D array...\n");
                    PrintMagic(result);
                }catch (MATLABException e)
                {
                    Console.WriteLine(e.Message);
                }
            }
            else if (mlRequestStatus.State == MATLABRequestState.ERROR_STATE)
            {
                HTTPErrorInfo httpErrorInfo = HTTPErrorInfo.Create(response.GetResponseStream());
                Console.WriteLine("Error:");
                Console.WriteLine(httpErrorInfo.HttpErrorCode);
                Console.WriteLine(httpErrorInfo.HttpErrorMessage);
            }
            Console.ReadKey();

        }

        static void PrintMagic(double[,] magic)
        {
            int numDims = magic.Rank;
            int[] dims = new int[numDims];

            for (int i = 0; i < numDims; i++)
            {
                dims[i] = magic.GetLength(i);
            }

            for (int j = 0; j < dims[0]; j++)
            {
                for (int k = 0; k < dims[1]; k++)
                {
                    Console.Write(magic[j, k]);
                    if (k < dims[1] - 1)
                    {
                        Console.Write(",");
                    }
                }
                Console.WriteLine();
            }
        }

        //This function takes URL and HTTP request type as input and sends the HTTP request.
        static HttpWebResponse MakeHTTPRequest(String url, String requestType, MATLABParams mlParams)
        {
            var httpRequest = (HttpWebRequest)WebRequest.Create(url);
            httpRequest.Method = requestType;
            httpRequest.ContentType = "application/x-google-protobuf";
            if (requestType.Equals("POST"))
            {
                mlParams.WriteTo(httpRequest.GetRequestStream());

            }
            return (HttpWebResponse)httpRequest.GetResponse();
        }

    }
}