// we should read and apply all destinations at once,
// or at least make sure we apply them to the global datastruct at once,
// otherwise we can destabilize things / send wrong traffic, etc
func readDestinations(specs []string, table *Table, allowMatcher bool) (destinations []*Destination, err error) {
	s := toki.NewScanner(tokenDefDest)
	for _, spec := range specs {
		//fmt.Println("spec" + spec)
		var prefix, sub, regex, addr, spoolDir string
		var spool, pickle bool
		flush := 1000
		reconn := 10000
		spoolDir = table.spoolDir
		s.SetInput(spec)
		t := s.Next()
		//fmt.Println("thisisit")
		if len(t.Value) == 0 {
			return destinations, errors.New("addr not set for endpoint")
		}
		addr = string(t.Value)
		//fmt.Println(t.Token, word)
		if t.Token != word {
			//fmt.Println("wtf", t.Token, word)
			return destinations, fmt.Errorf("expected destination endpoint spec, not '%s'", t.Value)
		}
		for {
			t := s.Next()
			if t.Token == opt {
				val := string(t.Value)
				//fmt.Println("yes got my opt with val", val)
				switch val {
				case "prefix=":
					val := s.Next()
					prefix = string(val.Value)
				case "sub=":
					val := s.Next()
					sub = string(val.Value)
				case "regex=":
					val := s.Next()
					regex = string(val.Value)
				case "flush=":
					val := s.Next()
					i, err := strconv.Atoi(string(val.Value))
					if err != nil {
						return destinations, err
					}
					flush = i
				case "reconn=":
					val := s.Next()
					i, err := strconv.Atoi(string(val.Value))
					if err != nil {
						return destinations, err
					}
					reconn = i
				case "pickle=":
					t := s.Next()
					val := string(t.Value)
					if val == "true" {
						pickle = true
					} else if val == "false" {
					} else {
						return destinations, fmt.Errorf("unrecognized pickle value '%s'", val)
					}
				case "spool=":
					t := s.Next()
					val := string(t.Value)
					if val == "true" {
						spool = true
					} else if val != "false" {
						return destinations, fmt.Errorf("unrecognized spool value '%s'", val)
					}
				default:
					return destinations, fmt.Errorf("unrecognized option '%s'", val)
				}
			} else {
				break
				//return destinations, errors.New(fmt.Sprintf("expected endpoint option, not token type %v with value '%s'", t.Token, t.Value))
			}
		}

		periodFlush := time.Duration(flush) * time.Millisecond
		periodReConn := time.Duration(reconn) * time.Millisecond
		if !allowMatcher && (prefix != "" || sub != "" || regex != "") {
			return destinations, fmt.Errorf("matching options (prefix, sub, and regex) not allowed for this route type")
		}
		dest, err := NewDestination(prefix, sub, regex, addr, spoolDir, spool, pickle, periodFlush, periodReConn)
		if err != nil {
			return destinations, err
		}
		destinations = append(destinations, dest)
	}
	return destinations, nil
}
func applyCommand(table *Table, cmd string) error {
	inputs := strings.Split(cmd, "  ")
	s := toki.NewScanner(tokenDefGlobal)
	s.SetInput(inputs[0])
	t := s.Next()
	if t.Token == addBlack {
		inputs = strings.Fields(cmd)

		prefix_pat := ""
		sub_pat := ""
		regex_pat := ""

		if len(inputs) == 2 {
			// The fallback case to support the default substring method
			sub_pat = inputs[1]
		} else if len(inputs) == 3 {
			// New case supporting prefix, sub and regex patterns
			match_method := inputs[1]
			pattern := inputs[2]

			if match_method == "prefix" {
				prefix_pat = pattern
			} else if match_method == "sub" {
				sub_pat = pattern
			} else if match_method == "regex" {
				regex_pat = pattern
			} else {
				return errors.New("addBlack [prefix|sub|regex] <pattern> (invalid match type)")
			}
		} else {
			return errors.New("addBlack [prefix|sub|regex] <pattern>")
		}

		m, err := NewMatcher(prefix_pat, sub_pat, regex_pat)
		if err != nil {
			return err
		}
		table.AddBlacklist(m)

	} else if t.Token == addAgg {
		inputs = strings.Fields(cmd)
		if len(inputs) != 6 {
			return errors.New("addAgg <func> <match> <key> <interval> <wait>")
		}
		fun := inputs[1]
		regex := inputs[2]
		outFmt := inputs[3]
		interval, err := strconv.Atoi(inputs[4])
		if err != nil {
			return err
		}
		wait, err := strconv.Atoi(inputs[5])
		if err != nil {
			return err
		}
		agg, err := aggregator.New(fun, regex, outFmt, uint(interval), uint(wait), table.In)
		if err != nil {
			return err
		}
		table.AddAggregator(agg)
	} else if t.Token == addRouteSendAllMatch {
		split := strings.Split(string(t.Value), " ")
		key := split[2]
		if len(inputs) < 2 {
			return fmt.Errorf("must get at least 1 destination for route '%s'", key)
		}

		prefix, sub, regex, err := readRouteOpts(s)
		if err != nil {
			return err
		}
		destinations, err := readDestinations(inputs[1:], table, true)
		if err != nil {
			return err
		}
		route, err := NewRouteSendAllMatch(key, prefix, sub, regex, destinations)
		if err != nil {
			return err
		}
		table.AddRoute(route)
	} else if t.Token == addRouteSendFirstMatch {
		split := strings.Split(string(t.Value), " ")
		key := split[2]
		if len(inputs) < 2 {
			return fmt.Errorf("must get at least 1 destination for route '%s'", key)
		}

		prefix, sub, regex, err := readRouteOpts(s)
		if err != nil {
			return err
		}
		destinations, err := readDestinations(inputs[1:], table, true)
		if err != nil {
			return err
		}
		route, err := NewRouteSendFirstMatch(key, prefix, sub, regex, destinations)
		if err != nil {
			return err
		}
		table.AddRoute(route)
	} else if t.Token == addRouteConsistentHashing {
		split := strings.Split(string(t.Value), " ")
		key := split[2]
		if len(inputs) < 3 {
			return fmt.Errorf("must get at least 2 destinations for consistent hashing route '%s'", key)
		}

		prefix, sub, regex, err := readRouteOpts(s)
		if err != nil {
			return err
		}
		destinations, err := readDestinations(inputs[1:], table, false)
		if err != nil {
			return err
		}
		route, err := NewRouteConsistentHashing(key, prefix, sub, regex, destinations)
		if err != nil {
			return err
		}
		table.AddRoute(route)
	} else if t.Token == addDest {
		//split := strings.Split(string(t.Value), " ")
		//key := split[2]

		fmt.Println("val", t.Value)
		fmt.Println("inputs", inputs[0])
	} else if t.Token == modDest {
		split := strings.Split(string(t.Value), " ")
		if len(split) < 4 {
			return errors.New("need a key, index and at least one option")
		}
		key := split[1]
		index, err := strconv.Atoi(split[2])
		if err != nil {
			return err
		}
		opts := make(map[string]string)
		for _, str := range split[3:] {
			opt := strings.Split(str, "=")
			if len(opt) != 2 {
				return fmt.Errorf("bad option format at '%s'", str)
			}
			opts[opt[0]] = opt[1]
		}

		return table.UpdateDestination(key, index, opts)
	} else if t.Token == modRoute {
		split := strings.Split(string(t.Value), " ")
		if len(split) < 3 {
			return errors.New("need a key and at least one option")
		}
		key := split[1]
		opts := make(map[string]string)
		for _, str := range split[2:] {
			opt := strings.Split(str, "=")
			if len(opt) != 2 {
				return fmt.Errorf("bad option format at '%s'", str)
			}
			opts[opt[0]] = opt[1]
		}

		return table.UpdateRoute(key, opts)
	} else {
		return fmt.Errorf("unrecognized command '%s'", t.Value)
	}
	if t = s.Next(); t.Token != toki.EOF {
		//fmt.Println("h")
		return fmt.Errorf("extraneous input '%s'", t.Value)
	}
	return nil
}