/// <summary>
        /// Parses the specified text into a schedule object.
        /// </summary>
        /// <param name="text">The text.</param>
        /// <returns>
        /// Returns null if the text is invalid.
        /// </returns>
        public TextToScheduleResults Parse(string text)
        {
            text = GrammarHelper.Normalize(text);

            TextToScheduleResults results = new TextToScheduleResults();
            bool matched = false;

            if (ExecuteMatch(Grammar.Expression1, text, results, Expression1Handler))
            {
                matched = true;
            }
            else if (ExecuteMatch(Grammar.Expression2, text, results, Expression2Handler))
            {
                matched = true;
            }
            else if (ExecuteMatch(Grammar.Expression3, text, results, Expression3Handler))
            {
                matched = true;
            }


            if (matched)
                return results;   
            else
                return null;
        }
        private static void InputChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            var m = (sender) as Model;
            m.Triggers.Clear();

            TextToScheduleResults results = null;

            try
            {
                CronExpression cron = new CronExpression(m.Input);
                results = new TextToScheduleResults();
                results.Add(TriggerBuilder.Create()
                    .WithCronSchedule(m.Input));
            }
            catch
            {
                var english = TextToScheduleFactory.CreateEnglishParser();
                results = english.Parse(m.Input);
            }

            if (results == null)
            {
                try
                {
                    //try german verion
                    var german = new TextToSchedule(new AndrewSmith.Quartz.TextToSchedule.Grammars.GermanGrammar(), new AndrewSmith.Quartz.TextToSchedule.Grammars.GermanGrammarHelper());
                    results = german.Parse(m.Input);
                }
                catch
                {

                }
            }
            

            if (results != null)
            {            
                List<DateTime> list = new List<DateTime>();

                foreach (var groups in results.RegisterGroups)
	            {
                    var trigger = (IOperableTrigger)groups.TriggerBuilder.Build();
                    var dates = TriggerUtils.ComputeFireTimes(trigger, groups.Calendar, MaxResultsToReturn);

                    foreach (var d in dates)
                    {
                        list.Add(d.ToLocalTime().DateTime);
                    }
	            }

                //sort the list
                list.Sort();

                foreach (var item in list.Take(MaxResultsToReturn))
                {
                    m.Triggers.Add(item.ToString("ddd, MM/dd/yyyy hh:mm:ss tt"));
                }
            }
        }
        /// <summary>
        /// Method that matches an expression, and if it does will run the associated method
        /// </summary>
        /// <param name="expression">The expression.</param>
        /// <param name="input">The input.</param>
        /// <param name="results">The results.</param>
        /// <param name="matchFunction">The match function to run if it matches.</param>
        /// <returns></returns>
        private static bool ExecuteMatch(string expression, string input, TextToScheduleResults results, Action<NameValueCollection, TextToScheduleResults> matchFunction)
        {
            expression = "^" + expression + "$";

            var dic = RegexHelper.GetNamedMatches(input, expression);

            if (dic != null)
            {
                matchFunction(dic, results);
                return true;
            }

            return false;                
        }
        /// <summary>
        /// Handles with a given text matches the Expression2 field.
        /// </summary>
        /// <param name="nameValueCollection">A collection of values from the named capture groups.</param>
        /// <param name="results">The results.</param>
        private void Expression3Handler(NameValueCollection nameValueCollection, TextToScheduleResults results)
        {
            var time = nameValueCollection["TIME"];

            var dateSpec = nameValueCollection["DATESPEC"];
            var month = nameValueCollection["MONTH"];
            var day = nameValueCollection["DAY"];
            var year = nameValueCollection["YEAR"];

            DateTime date = DateTime.Today; //default date to today

            if (time != null)
            {
                date = GrammarHelper.GetTimeFromTimeString(time).Value;
            }

            //init cron values
            List<string> cronExpressions = new List<string>();

            string cron_sec = date.Second.ToString();
            string cron_min = date.Minute.ToString();
            string cron_hour = date.Hour.ToString();
            string cron_day = "*";
            string cron_month = "*";
            string cron_dayofWeek = "?";
            string cron_year = null;

            cron_month = GetMonthCronValue(GrammarHelper.GetMonthValue(month));
            cron_day = day;

            if (year != null)
                cron_year = GetYearCronValue(GrammarHelper.GetYearValue(year));


            //build cron string
            string cronString =  null;

            if (cron_year != null)
                cronString = string.Format(string.Join(" ", new string[] { cron_sec, cron_min, cron_hour, cron_day, cron_month, cron_dayofWeek, cron_year }));
            else
                cronString = string.Format(string.Join(" ", new string[] { cron_sec, cron_min, cron_hour, cron_day, cron_month, cron_dayofWeek }));

            //add cron string
            cronExpressions.Add(cronString);



            foreach (var item in cronExpressions)
            {
                var triggerBuilder = TriggerBuilder.Create();

                CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.CronSchedule(item);
                triggerBuilder.WithSchedule(cronScheduleBuilder);

                ITrigger trigger = triggerBuilder.Build();
                results.Add(triggerBuilder);
            }
        }
        /// <summary>
        /// Handles with a given text matches the Expression2 field.
        /// </summary>
        /// <param name="nameValueCollection">A collection of values from the named capture groups.</param>
        /// <param name="results">The results.</param>
        private void Expression2Handler(NameValueCollection nameValueCollection, TextToScheduleResults results)
        {
            var time = nameValueCollection["TIME"];
            var fromTime = nameValueCollection["FROMTIME"];
            var toTime = nameValueCollection["TOTIME"];

            var dayOfWeekSpecs = nameValueCollection.GetValues("DAYOFWEEK");
            var monthSpecs = nameValueCollection.GetValues("MONTH");

            var ordinals = nameValueCollection.GetValues("ORDINAL");

            DateTime date = DateTime.Today; //default date to today

            if (time != null)
            {
                date = GrammarHelper.GetTimeFromTimeString(time).Value;
            }

            //init cron values
            List<string> cronExpressions = new List<string>();

            string cron_sec = date.Second.ToString();
            string cron_min = date.Minute.ToString();
            string cron_hour = date.Hour.ToString();
            string cron_day = "?";
            string cron_month = "*";
            string cron_dayofWeek = "*";

            if (monthSpecs != null)
            {
                var months = GrammarHelper.GetMonthValues(monthSpecs);
                cron_month = string.Join(",", months.Select(mon => GetMonthCronValue(mon)));
            }

            if (dayOfWeekSpecs != null)
            {
                var dows = GrammarHelper.GetDayOfWeekValues(dayOfWeekSpecs);
                cron_dayofWeek = string.Join(",", dows.Select(x => GetDayOfWeekCronValue(x)));
            }

            if (ordinals != null)
            {
                if (dayOfWeekSpecs != null)
                {
                    //combine ordinals and dayOfWeeks
                    var combined =
                        from a in GrammarHelper.GetOrdinalValues(ordinals)
                        from b in GrammarHelper.GetDayOfWeekValues(dayOfWeekSpecs)
                        select new { Ordinal = a, DayOfWeek = b };

                    foreach (var item in combined)
                    {
                        cron_dayofWeek = GetDayOfWeekCronValue(item.DayOfWeek);
                        cron_dayofWeek += GetOrdinalCronValue(item.Ordinal);

                        string cronString = string.Format(string.Join(" ", new string[] { cron_sec, cron_min, cron_hour, cron_day, cron_month, cron_dayofWeek }));
                        cronExpressions.Add(cronString);
                    }
                }

                if (dayOfWeekSpecs == null) //day was specified, handle special case
                {
                    //handle special cases
                    cron_dayofWeek = "?";

                    foreach (var o in GrammarHelper.GetOrdinalValues(ordinals))
	                {
                        cron_day = GetOrdinalCronValue(o).Replace("#", "");
                        string cronString = string.Format(string.Join(" ", new string[] { cron_sec, cron_min, cron_hour, cron_day, cron_month, cron_dayofWeek }));
                        cronExpressions.Add(cronString);
	                }
                }
            }
            else //no ordinal was specified
            {
                string cronString = string.Format(string.Join(" ", new string[] { cron_sec, cron_min, cron_hour, cron_day, cron_month, cron_dayofWeek }));
                cronExpressions.Add(cronString);
            }
        
            foreach (var item in cronExpressions)
            {
                var triggerBuilder = TriggerBuilder.Create();

                CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.CronSchedule(item);
                triggerBuilder.WithSchedule(cronScheduleBuilder);

                ITrigger trigger = triggerBuilder.Build();
                results.Add(triggerBuilder);
            }
        }
        /// <summary>
        /// Handles with a given text matches the Expression1 field.
        /// </summary>
        /// <param name="nameValueCollection">A collection of values from the named capture groups.</param>
        /// <param name="results">The results.</param>
        private void Expression1Handler(NameValueCollection nameValueCollection, TextToScheduleResults results)
        {
            var amount = nameValueCollection["AMOUNT"];
            var timeValue = nameValueCollection["TIMEVALUE"];
            var startDate = nameValueCollection["DATESPEC"];
            
            var time = nameValueCollection["TIME"];
            var fromTime = nameValueCollection["FROMTIME"];
            var toTime = nameValueCollection["TOTIME"];

            var dayOfWeekSpecs = nameValueCollection.GetValues("DAYOFWEEK");
            var monthSpecs = nameValueCollection.GetValues("MONTH");


            TimeSpan interval = BuildTimeInterval(timeValue, amount);
            DateTime? triggerStartTime = null;

            ICalendar calendar = null;

            //DAY OF WEEK SPECS
            if (dayOfWeekSpecs != null)
            {
                calendar = BuildCalendarOnDayOfWeek(calendar, dayOfWeekSpecs);
            }

            //MONTH SPECS (I had to put this after the day of week, because chaining the calendar's didn't seem to compute properly)
            if (monthSpecs != null)
            {
                calendar = BuildCalendarOnMonths(calendar, monthSpecs);
            }


            //TIME (single or range)

            //check for ranged time
            if (fromTime != null && toTime != null)
            {
                calendar = BuildCalendarOnTimeRange(calendar, fromTime, toTime);

                //set the start date as the from time
                DateTime? fromTimeStartDate = GrammarHelper.GetTimeFromTimeString(fromTime);
                triggerStartTime = fromTimeStartDate;
            }
            //is regualr time, process as single time provided
            else if (time != null)
            {
                DateTime? timeStartDate = GrammarHelper.GetTimeFromTimeString(time);
                triggerStartTime = timeStartDate;
            }
            
            //BUILD TRIGGER
            TriggerBuilder triggerBuilder = TriggerBuilder.Create();

            SimpleScheduleBuilder simpleBuilder = SimpleScheduleBuilder.Create();
            simpleBuilder.WithInterval(interval);
            simpleBuilder.RepeatForever();
            triggerBuilder.WithSchedule(simpleBuilder);
            
            //start on from time
            if (triggerStartTime != null)
                triggerBuilder.StartAt(new DateTimeOffset(triggerStartTime.Value));

            results.Add(triggerBuilder, calendar);
        }