using System;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using System.Web.Http;
using ImageProcessing;
using log4net;
using OpenCvSharp;
using OpenCvSharp.CPlusPlus;

namespace Control.Controllers
{
    [RoutePrefix("api/v1/improc")]
    public class ImageProcessingController : ApiController
    {
        private static readonly ILog Log = LogManager.GetLogger(typeof(ImageProcessingController));

        /// <summary>
        /// This methods provides evaluating methods according to selected mode and type, can also check if or which jobs are selected
        /// </summary>
        /// <param name="mode">Mode of evaluation</param>
        /// <param name="type">If you expect value or want to detect one</param>
        /// <param name="desired">name of desired screen</param>
        /// <param name="port">Robot's API port</param>
        /// <param name="jobSelection">Selection of job selection mode</param>
        /// <returns>action result + result message</returns>
        [HttpPost, Route("evaluate/{mode?}/{type?}/{desired?}/{port?}/{jobSelection=None}")]
        public async Task<IHttpActionResult> EvaluateImage(EvaluationMode mode, EvaluationType type, string desired, string port, JobSelection jobSelection)
        {
            var userIp = Request.GetOwinContext().Request.RemoteIpAddress+":"+port;
            if (!RqaImProc.CalibrationPoints.ContainsKey(userIp)){
                return BadRequest("Camera for IP: " + userIp + " is not calibrated");
            }
            var bytes = await Request.Content.ReadAsByteArrayAsync();
            Mat mat = Mat.FromImageData(bytes);
            Log.Info("Serving request from " + userIp);
            var imp = new IpMethods { PrinterId = RqaImProc.CalibrationPoints.First(x => x.Key == userIp).Value.Mfd };
            ImageEvaluation ev = new ImageEvaluation();
            switch (mode){
                case EvaluationMode.Complex:
                    ev = imp.EvaluateImage(RqaImProc.CalibrationPoints.First(o => o.Key == userIp).Value.Points, mat,
                        desired);
                    if ((!ev.Corresponds && type == EvaluationType.Verification) ||
                        (ev.Identified.Equals("none") && type == EvaluationType.Detection)){
                        try{
                            string imagePath = @"C:\RQA\imgs\" + desired + System.IO.Path.GetRandomFileName() + ".png";
                            mat.SaveImage(imagePath, new PngEncodingParam(100));
                        }
                        catch (Exception ex){
                            Log.Error("Error writing a file", ex);
                        }
                    }
                    Log.Info(desired + " : " + ev.Identified + " - " +
                             (ev.Corresponds ? "corresponds" : "does not corresponds."));
                    break;
                case EvaluationMode.Fast:
                    ev = imp.FastEvaluation(RqaImProc.CalibrationPoints.First(o => o.Key == userIp).Value.Points, mat,
                        desired);
                    Log.Info("With fast " + desired + " - " + (ev.Corresponds ? "corresponds" : "does not corresponds."));
                    break;
                case EvaluationMode.Pin:
                    ev = imp.CheckPinCount(RqaImProc.CalibrationPoints.First(o => o.Key == userIp).Value.Points, mat);
                    break;
                case EvaluationMode.JobSelection:
                    switch (type){
                        case EvaluationType.Verification:
                            if (jobSelection == JobSelection.None){
                                const string message =
                                    "Was selected mode JobSelection however JobSelection enumeration was set to None, cannot proceed";
                                Log.Error(message);
                                return BadRequest(message);
                            }
                            ev =
                                imp.CheckSelectedJob(
                                    RqaImProc.CalibrationPoints.First(o => o.Key == userIp).Value.Points,
                                    mat,
                                    jobSelection);
                            if (ev.Evaluated == false)
                                return BadRequest("Some feature region for checking selected job was not found");
                            break;
                        case EvaluationType.Detection:
                            ev =
                                imp.FindSelectedJobs(
                                    RqaImProc.CalibrationPoints.First(o => o.Key == userIp).Value.Points, mat);
                            break;
                        default:
                            return BadRequest("Unknown Evaluation type");
                    }
                    break;
                default:
                    return BadRequest("no such evaluation mode");
            }
            return Ok(ev);
        }
    }
}
