/* CLIPCODE EVAL ADDED TEXT (BEGINNING)
 * 
 * This Coding Challenge is based on changing the code in Microsoft's ML.NET Tutorial to 
 * behave slightly differently. 
 * 
 * Licensing for Microsoft's ML.NET Tutorial: Creative Commons Attribution 4.0 International. 
 * https://github.com/dotnet/docs/blob/master/LICENSE 
 *
 * It will take you a few minutes to run the ML.NET Tutorial:
 * https://dotnet.microsoft.com/learn/machinelearning-ai/ml-dotnet-get-started-tutorial/intro
 * After that, you are ready to try out this coding challenge. 
 * 
 * For this coding challenge, you are asked to make some changes to this tutorial code:
 * 
 * 1) Carefully managing the input data supplied is of major importance for all ML.NET projects, so: 
 *    Remove the use of the 'SepalWidth' field and modify the rest of the code to keep it running correctly.
 *    
 * 2) The pipeline is the most critical part of a ML.NET app. Change the IrisPrediction class replacing
 *    PredictedLabel(s) for ResultLabel(s) and modify the pipeline to use this changed prediction class. 
 *    Important: You will need to change the last line of the pipeline:
 *      .Append(mlContext.Transforms.Conversion.MapKeyToValue(??)
 *    but the change is a bit more than just replacing the name - what is it?
 *    
 *  3) Imagine we are particularly interested in working with very large datasets, so we decide we 
 *     need to make a change to the pipeline code so that training does not occur against cached data - how? 
 *  
 *  CLIPCODE EVAL ADDED TEXT (END)
 *  =====================================================================================================
 */

using Microsoft.ML;
using Microsoft.ML.Data;
using System;

// CS0649 compiler warning is disabled because some fields are only
// assigned to dynamically by ML.NET at runtime
#pragma warning disable CS0649

namespace myMLApp
{
    class Program
    {
        // STEP 1: Define your data structures
        // IrisData is used to provide training data, and as
        // input for prediction operations
        // - First 4 properties are inputs/features used to predict the label
        // - Label is what you are predicting, and is only set when training
        public class IrisData
        {
            [LoadColumn(0)]
            public float SepalLength;

            [LoadColumn(1)]
            public float SepalWidth;

            [LoadColumn(2)]
            public float PetalLength;

            [LoadColumn(3)]
            public float PetalWidth;

            [LoadColumn(4)]
            public string Label;
        }

        // IrisPrediction is the result returned from prediction operations
        public class IrisPrediction
        {
            [ColumnName("PredictedLabel")]
            public string PredictedLabels;
        }

        static void Main(string[] args)
        {
            // STEP 2: Create a ML.NET environment
            MLContext mlContext = new MLContext();

            // If working in Visual Studio, make sure the 'Copy to Output Directory'
            // property of iris-data.txt is set to 'Copy always'
            IDataView trainingDataView = mlContext.Data.LoadFromTextFile<IrisData>(path: "iris-data.txt", hasHeader: false, separatorChar: ',');

            // STEP 3: Transform your data and add a learner
            // Assign numeric values to text in the "Label" column, because only
            // numbers can be processed during model training.
            // Add a learning algorithm to the pipeline. e.g.(What type of iris is this?)
            // Convert the Label back into original text (after converting to number in step 3)
            var pipeline = mlContext.Transforms.Conversion.MapValueToKey("Label")
                .Append(mlContext.Transforms.Concatenate("Features", "SepalLength", "SepalWidth", "PetalLength", "PetalWidth"))
                .AppendCacheCheckpoint(mlContext)
                .Append(mlContext.MulticlassClassification.Trainers.SdcaMaximumEntropy(labelColumnName: "Label", featureColumnName: "Features"))
                .Append(mlContext.Transforms.Conversion.MapKeyToValue("PredictedLabel"));

            // STEP 4: Train your model based on the data set
            var model = pipeline.Fit(trainingDataView);

            // STEP 5: Use your model to make a prediction
            // You can change these numbers to test different predictions
            var prediction = mlContext.Model.CreatePredictionEngine<IrisData, IrisPrediction>(model).Predict(
                new IrisData()
                {
                    SepalLength = 3.3f,
                    SepalWidth = 1.6f,
                    PetalLength = 0.2f,
                    PetalWidth = 5.1f,
                });

            Console.WriteLine($"Predicted flower type is: {prediction.PredictedLabels}");

            Console.WriteLine("Press any key to exit....");
            Console.ReadLine();
        }
    }
}