static double CalculateTotalWaterVolume (BrewData data)
		{
			//Designing Great Beers by Ray Daniels pg. 65
			var runoffVolume = (data.FinishedBeerVolume + data.TrubLoss) / (1.0 - ShrinkageFactor) / CalculateEvaportation (data);
			var grainWaterRetention = GrainRetentionFactor * data.GrainBill;
			return runoffVolume + data.EquipmentLoss + grainWaterRetention;
		}
        static double CalculateTotalWaterVolume(BrewData data)
        {
            //Designing Great Beers by Ray Daniels pg. 65
            var runoffVolume        = (data.FinishedBeerVolume + data.TrubLoss) / (1.0 - ShrinkageFactor) / CalculateEvaportation(data);
            var grainWaterRetention = GrainRetentionFactor * data.GrainBill;

            return(runoffVolume + data.EquipmentLoss + grainWaterRetention);
        }
        static double CaluclateStrikeWaterTemperature(BrewData data)
        {
            //http://www.howtobrew.com/section3/chapter16-3.html
            //Initial infusion equation
            //Tw = (.2 / r) * (T2 - T1) + T2;
//			r = The ratio of water to grain in quarts per pound.
//			Wa = The amount of boiling water added (in quarts).
//			Wm = The total amount of water in the mash (in quarts).
//			T1 = The initial temperature (¡F) of the mash.
//			T2 = The target temperature (¡F) of the mash.
//			Tw = The actual temperature (¡F) of the infusion water.
//			G = The amount of grain in the mash (in pounds).

            return((.2 / data.MashThickness) * (data.MashTemperature - data.GrainTemperature) + data.MashTemperature);
        }
        public MashStatsViewModel(IScreen hostScreen, BrewData data)
        {
            HostScreen = hostScreen ?? Locator.Current.GetService <IScreen> ();

            SetMashTempTo = ReactiveCommand.Create();
            SetMashTempTo
            .StartWith(DefaultMashTemp)
            .Select(x => (double)x)
            .Where(x => x > 0.0)
            .Select(x => data.MashTemperature = x)
            .ToProperty(this, vm => vm.MashTemperature, out _MashTemperature);

            SetGrainTempTo = ReactiveCommand.Create();
            SetGrainTempTo
            .StartWith(DefaultGrainTemp)
            .Select(x => (double)x)
            .Where(x => x > 0.0)
            .Select(x => data.GrainTemperature = x)
            .ToProperty(this, vm => vm.GrainTemperature, out _GrainTemperature);

            SetMashThicknessTo = ReactiveCommand.Create();
            SetMashThicknessTo
            .StartWith(DefaultMashThickness)
            .Select(x => (double)x)
            .Where(x => x > 0.0)
            .Select(x => data.MashThickness = x)
            .ToProperty(this, vm => vm.MashThickness, out _MashThickness);

            SetMashLengthTo = ReactiveCommand.Create();
            SetMashLengthTo
            .StartWith(DefaultMashLength)
            .Select(x => data.MashTime = TimeSpan.FromMinutes((double)x))
            .Select(x => x.TotalMinutes)
            .ToProperty(this, vm => vm.MashLength, out _MashLength);

            SetBoilLengthTo = ReactiveCommand.Create();
            SetBoilLengthTo
            .StartWith(DefaultBoilLength)
            .Select(x => data.BoilTime = TimeSpan.FromMinutes((double)x))
            .Select(x => x.TotalMinutes)
            .ToProperty(this, vm => vm.BoilLength, out _BoilLength);

            NavigateToWaterProjections = ReactiveCommand.Create();
            NavigateToWaterProjections
            .Select(x => new WaterProjectionsViewModel(HostScreen, data))
            .Subscribe(HostScreen.Router.Navigate.Execute);
        }
		public MashStatsViewModel (IScreen hostScreen, BrewData data)
		{
			HostScreen = hostScreen ?? Locator.Current.GetService<IScreen> ();

			SetMashTempTo = ReactiveCommand.Create ();
			SetMashTempTo
				.StartWith (DefaultMashTemp)
				.Select (x => (double)x)
				.Where (x => x > 0.0)
				.Select (x => data.MashTemperature = x)
				.ToProperty (this, vm => vm.MashTemperature, out _MashTemperature);

			SetGrainTempTo = ReactiveCommand.Create ();
			SetGrainTempTo
				.StartWith (DefaultGrainTemp)
				.Select (x => (double)x)
				.Where (x => x > 0.0)
				.Select (x => data.GrainTemperature = x)
				.ToProperty (this, vm => vm.GrainTemperature, out _GrainTemperature);

			SetMashThicknessTo = ReactiveCommand.Create ();
			SetMashThicknessTo
				.StartWith (DefaultMashThickness)
				.Select (x => (double)x)
				.Where (x => x > 0.0)
				.Select (x => data.MashThickness = x)
				.ToProperty (this, vm => vm.MashThickness, out _MashThickness);

			SetMashLengthTo = ReactiveCommand.Create ();
			SetMashLengthTo
				.StartWith (DefaultMashLength)
				.Select (x => data.MashTime = TimeSpan.FromMinutes ((double)x))
				.Select (x => x.TotalMinutes)
				.ToProperty (this, vm => vm.MashLength, out _MashLength);

			SetBoilLengthTo = ReactiveCommand.Create ();
			SetBoilLengthTo
				.StartWith (DefaultBoilLength)
				.Select (x => data.BoilTime = TimeSpan.FromMinutes ((double)x))
				.Select (x => x.TotalMinutes)
				.ToProperty (this, vm => vm.BoilLength, out _BoilLength);

			NavigateToWaterProjections = ReactiveCommand.Create ();
			NavigateToWaterProjections
				.Select (x => new WaterProjectionsViewModel (HostScreen, data))
				.Subscribe (HostScreen.Router.Navigate.Execute);
		}
        public WaterProjectionsViewModel(IScreen hostScreen, BrewData data)
        {
            HostScreen = hostScreen ?? Locator.Current.GetService <IScreen> ();

            TotalWaterVolume       = Math.Round(CalculateTotalWaterVolume(data), 2);
            StrikeWaterVolume      = Math.Round(CalculateStrikeWaterVolume(data), 2);
            StrikeWaterTemperature = Math.Round(CaluclateStrikeWaterTemperature(data), 2);
            SpargeWaterVolume      = Math.Round(TotalWaterVolume - StrikeWaterVolume, 2);
            //TODO: Research sparge water temp. In a single infusion it seems to always be
            //170.  Not sure why. Is is like a second infusion or just empirically good
            //for the grain wash
            SpargeWaterTemperature = 170.0;

            NavigateToMashTimer = ReactiveCommand.Create();
            NavigateToMashTimer
            .ObserveOn(RxApp.MainThreadScheduler)
            .Subscribe(x => HostScreen.Router.Navigate.Execute(new MashTimerViewModel(HostScreen, data)));
        }
		public WaterProjectionsViewModel (IScreen hostScreen, BrewData data)
		{
			HostScreen = hostScreen ?? Locator.Current.GetService<IScreen> ();

			TotalWaterVolume = Math.Round (CalculateTotalWaterVolume (data), 2);
			StrikeWaterVolume = Math.Round (CalculateStrikeWaterVolume (data), 2);
			StrikeWaterTemperature = Math.Round (CaluclateStrikeWaterTemperature (data), 2);
			SpargeWaterVolume = Math.Round (TotalWaterVolume - StrikeWaterVolume, 2);
			//TODO: Research sparge water temp. In a single infusion it seems to always be 
			//170.  Not sure why. Is is like a second infusion or just empirically good
			//for the grain wash
			SpargeWaterTemperature = 170.0; 

			NavigateToMashTimer = ReactiveCommand.Create ();
			NavigateToMashTimer
				.ObserveOn (RxApp.MainThreadScheduler)
				.Subscribe (x => HostScreen.Router.Navigate.Execute (new MashTimerViewModel (HostScreen, data)));

		}
        public MashTimerViewModel(IScreen hostScreen, BrewData data)
        {
            HostScreen = hostScreen ?? Locator.Current.GetService <IScreen> ();

            CurrentTimeRemaining = (int)data.MashTime.TotalSeconds;

            var canStartOrReset = this.WhenAnyValue(x => x.IsTimerRunning).Where(x => !x);

            StartTimer = ReactiveCommand.Create(canStartOrReset);
            var start = StartTimer.Select(x => true);

            PauseTimer = ReactiveCommand.Create(this.WhenAnyValue(x => x.IsTimerRunning).Where(x => x));
            var pause = PauseTimer.Select(x => false);

            Observable.Merge(start, pause)
            .StartWith(false)
            .ToProperty(this, vm => vm.IsTimerRunning, out _IsTimerRunning);

            ResetTimer = ReactiveCommand.Create(canStartOrReset);
            ResetTimer.Subscribe(_ => {
                CurrentTimeRemaining = (int)data.MashTime.TotalSeconds;
            });


            this.WhenAnyValue(x => x.IsTimerRunning)
            .Where(x => x == true)
            .Subscribe(_ => Device.StartTimer(TimeSpan.FromSeconds(1), () => {
                if (CurrentTimeRemaining > 0)
                {
                    CurrentTimeRemaining -= 1;
                }
                return(IsTimerRunning);
            }));



            this.WhenAnyValue(x => x.CurrentTimeRemaining)
            .Select(x => TimeSpan.FromSeconds(x).ToString())
            .ToProperty(this, vm => vm.ClockText, out _ClockText);
        }
		public MashTimerViewModel (IScreen hostScreen, BrewData data)
		{
			HostScreen = hostScreen ?? Locator.Current.GetService<IScreen> ();

			CurrentTimeRemaining = (int)data.MashTime.TotalSeconds;

			var canStartOrReset = this.WhenAnyValue (x => x.IsTimerRunning).Where (x => !x);

			StartTimer = ReactiveCommand.Create (canStartOrReset);
			var start = StartTimer.Select (x => true);

			PauseTimer = ReactiveCommand.Create (this.WhenAnyValue (x => x.IsTimerRunning).Where (x => x));
			var pause = PauseTimer.Select (x => false);

			Observable.Merge (start, pause)
				.StartWith (false)
				.ToProperty (this, vm => vm.IsTimerRunning, out _IsTimerRunning);

			ResetTimer = ReactiveCommand.Create (canStartOrReset);
			ResetTimer.Subscribe (_ => {
				CurrentTimeRemaining = (int)data.MashTime.TotalSeconds;
			});


			this.WhenAnyValue (x => x.IsTimerRunning)
				.Where (x => x == true)
				.Subscribe (_ => Device.StartTimer (TimeSpan.FromSeconds (1), () => {
				if (CurrentTimeRemaining > 0)
					CurrentTimeRemaining -= 1;
				return IsTimerRunning;
			}));




			this.WhenAnyValue (x => x.CurrentTimeRemaining)
				.Select (x => TimeSpan.FromSeconds (x).ToString ())
				.ToProperty (this, vm => vm.ClockText, out _ClockText);
		}
		static double CalculateStrikeWaterVolume (BrewData data)
		{
			return ((data.GrainBill * data.MashThickness) / QuartsPerGallon) + data.EquipmentLoss;
		}
		static double CalculateEvaportation (BrewData data)
		{
			return 1 - ((EvaporatationFactor / MinutesPerHour) * data.BoilTime.TotalMinutes);
		}
		static double CaluclateStrikeWaterTemperature (BrewData data)
		{
			//http://www.howtobrew.com/section3/chapter16-3.html
			//Initial infusion equation
			//Tw = (.2 / r) * (T2 - T1) + T2;
//			r = The ratio of water to grain in quarts per pound.
//			Wa = The amount of boiling water added (in quarts).
//			Wm = The total amount of water in the mash (in quarts).
//			T1 = The initial temperature (¡F) of the mash.
//			T2 = The target temperature (¡F) of the mash.
//			Tw = The actual temperature (¡F) of the infusion water.
//			G = The amount of grain in the mash (in pounds).

			return (.2 / data.MashThickness) * (data.MashTemperature - data.GrainTemperature) + data.MashTemperature;
		}
 static double CalculateEvaportation(BrewData data)
 {
     return(1 - ((EvaporatationFactor / MinutesPerHour) * data.BoilTime.TotalMinutes));
 }
 static double CalculateStrikeWaterVolume(BrewData data)
 {
     return(((data.GrainBill * data.MashThickness) / QuartsPerGallon) + data.EquipmentLoss);
 }