using System;
using System.IO;
using System.Net.Http;
using System.Threading.Tasks;

namespace MarketData.CNNProcessing
{
  public class CNNClient
	{
	  public enum Model{resnet50,resnet50B,resnet50_20241024_270,inception,vgg16,lenet5,ping};
		private static readonly string Alive="Alive";
    private readonly HttpClient client = new HttpClient();
		private string baseUrl;
    private static int REQUEST_TIMEOUT=15000;

		public CNNClient(String baseUrl)
		{
      client.Timeout=new TimeSpan(0,0,0,0,REQUEST_TIMEOUT);
      if(!baseUrl.StartsWith("http://") && !baseUrl.StartsWith("HTTP://"))baseUrl="http://"+baseUrl;
		  this.baseUrl=baseUrl;
		}

		private String GetModelUrl(CNNClient.Model model)
		{
		  return baseUrl+"/predict_"+model.ToString();
		}

    public String Predict(CNNClient.Model model,Stream stream)
    {
		  try
			{
        string responsePayload = Upload(GetModelUrl(model),stream).GetAwaiter().GetResult();
				return responsePayload;
			}
			catch(Exception exception)
			{
			  return exception.ToString();
			}
    }

// This method is used to process an image through PIL.  All images that are being used in training should ultimately be processed through
// PIL (Python Image Library) so that images can be normalized by python prior to training.  We do this because we use PIL during the 
// prediction process and we want all images used in training/validation and all images used for prediction to be touched by and processed
// by PIL
    public Stream ProcessImage(Stream stream)
    {
      return UploadImage(baseUrl+"/process_image", stream);
    }

    public bool Ping()
    {
		  try
			{
        string responsePayload = Upload(baseUrl+"/ping").GetAwaiter().GetResult();
        if(null==responsePayload)return false;
				return responsePayload.Equals(Alive);
			}
			catch(Exception exception)
			{
        MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Exception encountered: {0}",exception.ToString()));
			  return false;
			}
    }

    private async Task<String> Upload(String url,Stream stream)
    {
      try
      {
        using (HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, url))
        {
				  int streamEnd = Convert.ToInt32(stream.Length);
				  byte[] byteArray = new byte[streamEnd];
				  stream.Read(byteArray, 0, streamEnd);
          request.Content=new ByteArrayContent(byteArray);
				  using (HttpResponseMessage response = await client.SendAsync(request))
          {
            return await response.Content.ReadAsStringAsync();
          }
        }
      }
      catch(Exception exception)
      {
        MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Exception encountered: {0}",exception.ToString()));
        return null;
      }
    }
    /// <summary>
    /// This method uploads an image stream to the specified url and receives the processed image back.
    /// The processed image simply processes the image through PIL (Python Image Library)
    /// </summary>
    private Stream UploadImage(String url,Stream stream)
    {
      try
      {
        using (HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, url))
        {
				  int streamEnd = Convert.ToInt32(stream.Length);
				  byte[] byteArray = new byte[streamEnd];
				  stream.Read(byteArray, 0, streamEnd);
          request.Content=new ByteArrayContent(byteArray);
          HttpResponseMessage response = client.SendAsync(request).GetAwaiter().GetResult();
          return response.Content.ReadAsStreamAsync().GetAwaiter().GetResult();
        }
      }
      catch(Exception exception)
      {
        MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Exception encountered: {0}",exception.ToString()));
        return null;
      }
    }

    private async Task<String> Upload(String url)
    {
      try
      {
        using (HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, url))
        {
				  HttpResponseMessage response = await client.SendAsync(request);
          return await response.Content.ReadAsStringAsync();
        }
      }
      catch(Exception exception)
      {
        MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Exception encountered: {0}",exception.ToString()));
        return null;
      }
    }
	}
}
