/// <summary>
        /// Creates a base AusFarm simulation spec from the Farm4Prophet spec.
        /// </summary>
        /// <param name="paddock">The farm paddock</param>
        /// <returns>The created AusFarm spec</returns>
        private static AusFarmSpec CreateBaseSimulation(FarmSystem farm)
        {
            FarmSystem copyOfFarm = farm; 

            AusFarmSpec runnableSim = new AusFarmSpec();
            runnableSim.Name = "BaseSim";

            runnableSim.StartDate = DateTime.MaxValue;

            //===========================================================
            // May be appropriate here to decide which simulation template
            // will be used based on the requirement for crops and animals.
            //===========================================================
            runnableSim.SimTemplateType = farm.SimTemplateType;

            /* sample dates used to initialise the run times?
            if (farm.SoilWaterSampleDate < runnableSim.StartDate)
                runnableSim.StartDate = farm.SoilWaterSampleDate;
            if (farm.SoilNitrogenSampleDate != DateTime.MinValue &&
                farm.SoilNitrogenSampleDate < runnableSim.StartDate)
                runnableSim.StartDate = farm.SoilNitrogenSampleDate; */
            //if (sow != null && sow.Date < shortSimulation.StartDate)
            //    shortSimulation.StartDate = sow.Date;

            if (farm.StartSeasonDate < runnableSim.StartDate)
                runnableSim.StartDate = farm.StartSeasonDate;

            runnableSim.OnFarmSoilTypes.AddRange(copyOfFarm.OnFarmSoilTypes);
            runnableSim.OnFarmPaddocks.AddRange(copyOfFarm.OnFarmPaddocks);
            runnableSim.LiveStock = copyOfFarm.LiveStock;

            runnableSim.StationNumber = copyOfFarm.StationNumber;
            runnableSim.CroppingRegion = copyOfFarm.CroppingRegion;
            return runnableSim;
        }
        /// <summary>
        /// Create simulation specifications.
        /// </summary>
        /// <param name="f4Prophet">The farm 4 prophet specification</param>
        /// <returns>A list of AusFarm specs. </returns>
        private static List <AusFarmSpec> SimulationRuns(Farm4Prophet f4Prophet)
        {
            List <AusFarmSpec> ausfarmSpecs = new List <AusFarmSpec>();

            foreach (FarmSystem farm in f4Prophet.FarmList)
            {
                AusFarmSpec simulation = CreateBaseSimulation(farm);
                simulation.Name       = farm.Name;
                simulation.Area       = farm.Area;
                simulation.StartDate  = farm.StartSeasonDate;
                simulation.EndDate    = simulation.StartDate.AddMonths(farm.RunLength);
                simulation.ReportName = farm.ReportName;
                ausfarmSpecs.Add(simulation);
            }
            return(ausfarmSpecs);
        }
        /// <summary>
        /// Creates a base AusFarm simulation spec from the Farm4Prophet spec.
        /// </summary>
        /// <param name="paddock">The farm paddock</param>
        /// <returns>The created AusFarm spec</returns>
        private static AusFarmSpec CreateBaseSimulation(FarmSystem farm)
        {
            FarmSystem copyOfFarm = farm;

            AusFarmSpec runnableSim = new AusFarmSpec();

            runnableSim.Name = "BaseSim";

            runnableSim.StartDate = DateTime.MaxValue;

            //===========================================================
            // May be appropriate here to decide which simulation template
            // will be used based on the requirement for crops and animals.
            //===========================================================
            runnableSim.SimTemplateType = farm.SimTemplateType;

            /* sample dates used to initialise the run times?
             * if (farm.SoilWaterSampleDate < runnableSim.StartDate)
             *  runnableSim.StartDate = farm.SoilWaterSampleDate;
             * if (farm.SoilNitrogenSampleDate != DateTime.MinValue &&
             *  farm.SoilNitrogenSampleDate < runnableSim.StartDate)
             *  runnableSim.StartDate = farm.SoilNitrogenSampleDate; */
            //if (sow != null && sow.Date < shortSimulation.StartDate)
            //    shortSimulation.StartDate = sow.Date;

            if (farm.StartSeasonDate < runnableSim.StartDate)
            {
                runnableSim.StartDate = farm.StartSeasonDate;
            }

            runnableSim.OnFarmSoilTypes.AddRange(copyOfFarm.OnFarmSoilTypes);
            runnableSim.OnFarmPaddocks.AddRange(copyOfFarm.OnFarmPaddocks);
            runnableSim.LiveStock = copyOfFarm.LiveStock;

            runnableSim.StationNumber  = copyOfFarm.StationNumber;
            runnableSim.CroppingRegion = copyOfFarm.CroppingRegion;
            return(runnableSim);
        }
        /// <summary>Creates the weather files for all simulations.</summary>
        /// <param name="simulations">The simulations.</param>
        /// <param name="workingFolder">The working folder to create the files in.</param>
        /// <param name="ausfarmNode"></param>
        private static void CreateWeatherFileForSimulation(AusFarmSpec simulation, string workingFolder)
        {
            // Write the .met file for the simulation
            DateTime earliestStartDate = DateTime.MaxValue;
            DateTime latestEndDate = DateTime.MinValue;
            DateTime nowDate = DateTime.MaxValue;
            DataTable observedData = null;
            int stationNumber = 0;

            stationNumber = simulation.StationNumber;
            if (simulation.StartDate < earliestStartDate)
                earliestStartDate = simulation.StartDate;
            if (simulation.EndDate > latestEndDate)
                latestEndDate = simulation.EndDate;


            // Create the weather files.
            FMetFile = Path.Combine(workingFolder, stationNumber.ToString()) + ".met";

            // Create a weather file.
            Weather.Data weatherFile = Weather.ExtractDataFromSILO(stationNumber, earliestStartDate, nowDate);
            Weather.OverlayData(observedData, weatherFile.Table);
            Weather.WriteWeatherFile(weatherFile.Table, FMetFile, weatherFile.Latitude, weatherFile.Longitude,
                                         weatherFile.TAV, weatherFile.AMP);

            // ensure that the run period doesn't exceed the data retrieved
            if (simulation.EndDate > weatherFile.LastDate)
                simulation.EndDate = weatherFile.LastDate;

            // calculate the rain deciles from April from the year of the start of the simulation. 
            // this could be improved to use more than a few decades of weather.
            simulation.RainDeciles = new double[12, 10]; // 12 months, 10 deciles
            DateTime accumStartDate = new DateTime(simulation.StartDate.Year, 4, 1);
            if (simulation.StartDate > accumStartDate)              // ensure that the start date for decile accum exists in the weather
                accumStartDate.AddYears(1);
            simulation.RainDeciles = Weather.CalculateRainDeciles(stationNumber, accumStartDate, simulation.EndDate);
        }
        /// <summary>
        /// Retrieves soil types from ApSoil
        /// Configures any missing crop ll, kl values
        /// </summary>
        /// <param name="simulation"></param>
        /// <param name="apsoilService"></param>
        /// <returns></returns>
        public static void DoSoils(AusFarmSpec simulation)
        {
            APSOIL.Service apsoilService = null; 
            Soil soil;
            for (int i = 0; i < simulation.OnFarmSoilTypes.Count; i++)
            {
                FarmSoilType soilType = simulation.OnFarmSoilTypes[i];
                soil = soilType.SoilDescr;
                if (soil == null)
                {
                    // Look for a <SoilName> and if found go get the soil from the Apsoil web service.
                    if (apsoilService == null)
                        apsoilService = new APSOIL.Service();
                    string soilXml = apsoilService.SoilXML(soilType.SoilPath);
                    if (soilXml == string.Empty)
                        throw new Exception("Cannot find soil: " + soilType.SoilPath);

                    soil = SoilUtilities.FromXML(soilXml);
                }
                
                // Other crop types not listed here will have their ll, kll, xf values calculated later

                // Remove any initwater nodes.
                soil.InitialWater = null;

                foreach (Sample sample in soil.Samples)
                    CheckSample(soil, sample);

                // get rid of <soiltype> from the soil
                // this is necessary because NPD uses this field and puts in really long
                // descriptive classifications. Soiln2 bombs with an FString internal error.
                soil.SoilType = "";

                // Set the soil name to 'soil'
                //soil.Name = "Soil";
                soilType.SoilDescr = soil;  //store the changed description
            }
        }
        /// <summary>
        /// Create a one year APSIM simulation for the specified yield prophet specification
        /// and paddock
        /// </summary>
        /// <param name="paddock">The paddock.</param>
        /// <param name="todayDate">The today date.</param>
        /// <param name="apsoilService">The apsoil service.</param>
        /// <returns>The XML node of the APSIM simulation.</returns>
        private static XmlNode CreateSimulationXML(AusFarmSpec simulation, string workingFolder)
        {
            AusFarmFileWriter ausfarmWriter;

            // determine which type of simulation this is based on stock, paddocks, crops
            if ((simulation.LiveStock.Flocks.Count == 0) && (simulation.LiveStock.TradeLambCount == 0))
            {
                ausfarmWriter = new AusFarmFileWriter(SimulationType.stCropOnly);
                simulation.SimTemplateType = SimulationType.stCropOnly; // ensure that the simulation type is alway correct
            }
            else
            {
                if (simulation.LiveStock.Flocks.Count == 1)
                {
                    ausfarmWriter = new AusFarmFileWriter(SimulationType.stSingleFlock);
                    simulation.SimTemplateType = SimulationType.stSingleFlock; // ensure that the simulation type is alway correct
                }
                else if (simulation.LiveStock.Flocks.Count == 2)
                {
                    ausfarmWriter = new AusFarmFileWriter(SimulationType.stDualFlock);
                    simulation.SimTemplateType = SimulationType.stDualFlock; // ensure that the simulation type is alway correct
                }
                else
                    throw new Exception();
            }

            // Name the simulation
            ausfarmWriter.NameSimulation(simulation.Name);

            // Set the clock start and end dates.
            ausfarmWriter.SetStartEndDate(simulation.StartDate, simulation.EndDate);

            //set the path for output files
            ausfarmWriter.OutputPath(workingFolder);
            ausfarmWriter.ReportName(simulation.ReportName);
            
            // Set the weather file
            ausfarmWriter.SetWeatherFile(FMetFile);
            ausfarmWriter.SetRainDeciles(simulation.RainDeciles);
            ausfarmWriter.SetCroppingRegion(simulation.CroppingRegion);
            ausfarmWriter.SetArea(simulation.Area);
            for (int i = 0; i < simulation.OnFarmSoilTypes.Count; i++)
            {
                ausfarmWriter.SetCropRotation(i + 1, simulation.OnFarmSoilTypes[i].CropRotationList);
            }
            
            // Do soil stuff.
            DoSoils(simulation);
            ausfarmWriter.SetSoils(simulation);

            if (simulation.SimTemplateType != SimulationType.stCropOnly)
            {
                // Set the Livestock data
                ausfarmWriter.WriteStockEnterprises(simulation.LiveStock);
            }

            return ausfarmWriter.ToXML();
        }
        /// <summary>Create a .sdml file for the job</summary>
        /// <param name="AusFarmSpec">The specification to use</param>
        /// <returns>The root XML node for the file</returns>
        private static XmlNode CreateAusFarmFile(AusFarmSpec simulation, string workingFolder)
        {
            
            XmlDocument doc = new XmlDocument();
            try
            {
                XmlNode simulationXML = CreateSimulationXML(simulation, workingFolder);
                if (simulationXML != null)
                    doc.AppendChild(doc.ImportNode(simulationXML, true));
            }
            catch (Exception err)
            {
                throw new Exception(err.Message + "\r\nSimulation name: " + simulation.Name);
            }

            return doc.DocumentElement;
        }