package config

// dirFunc is a type of parsing function which processes
// a particular directive and populates the config.
type dirFunc func(*parser) error

// validDirectives is a map of valid directive names to
// their parsing function.
var validDirectives map[string]dirFunc

func init() {
	// This has to be in the init function
	// to avoid an initialization loop error because
	// the 'import' directive (key) in this map
	// invokes a method that uses this map.
	validDirectives = map[string]dirFunc{
		"root": func(p *parser) error {
			if !p.lexer.NextArg() {
				return p.argErr()
			}
			p.cfg.Root = p.tkn()
			return nil
		},
		"import": func(p *parser) error {
			if !p.lexer.NextArg() {
				return p.argErr()
			}

			p2 := parser{}
			err := p2.lexer.Load(p.tkn())
			if err != nil {
				return p.err("Parse", err.Error())
			}
			defer p2.lexer.Close()

			p2.cfg = p.cfg
			err = p2.directives()
			if err != nil {
				return err
			}
			p.cfg = p2.cfg

			return nil
		},
		"gzip": func(p *parser) error {
			p.cfg.Gzip = true
			return nil
		},
		"log": func(p *parser) error {
			log := Log{Enabled: true}

			// Get the type of log (requests, errors, etc.)
			if !p.lexer.NextArg() {
				return p.argErr()
			}
			logWhat := p.tkn()

			// Set the log output file
			if p.lexer.NextArg() {
				log.OutputFile = p.tkn()
			}

			// Set the log output format
			if p.lexer.NextArg() {
				log.Format = p.tkn()
			}

			switch logWhat {
			case "requests":
				if log.OutputFile == "" || log.OutputFile == "_" {
					log.OutputFile = DefaultRequestsLog
				}
				p.cfg.RequestLog = log
			case "errors":
				if log.OutputFile == "" || log.OutputFile == "_" {
					log.OutputFile = DefaultErrorsLog
				}
				p.cfg.ErrorLog = log
			default:
				return p.err("Parse", "Unknown log '"+logWhat+"'")
			}

			return nil
		},
		"rewrite": func(p *parser) error {
			var rw Rewrite

			if !p.lexer.NextArg() {
				return p.argErr()
			}
			rw.From = p.tkn()

			if !p.lexer.NextArg() {
				return p.argErr()
			}
			rw.To = p.tkn()

			p.cfg.Rewrites = append(p.cfg.Rewrites, rw)
			return nil
		},
		"redir": func(p *parser) error {
			var redir Redirect

			// From
			if !p.lexer.NextArg() {
				return p.argErr()
			}
			redir.From = p.tkn()

			// To
			if !p.lexer.NextArg() {
				return p.argErr()
			}
			redir.To = p.tkn()

			// Status Code
			if !p.lexer.NextArg() {
				return p.argErr()
			}
			if code, ok := httpRedirs[p.tkn()]; !ok {
				return p.err("Parse", "Invalid redirect code '"+p.tkn()+"'")
			} else {
				redir.Code = code
			}

			p.cfg.Redirects = append(p.cfg.Redirects, redir)
			return nil
		},
		"ext": func(p *parser) error {
			if !p.lexer.NextArg() {
				return p.argErr()
			}
			p.cfg.Extensions = append(p.cfg.Extensions, p.tkn())
			for p.lexer.NextArg() {
				p.cfg.Extensions = append(p.cfg.Extensions, p.tkn())
			}
			return nil
		},
		"error": func(p *parser) error {
			if !p.lexer.NextArg() {
				return p.argErr()
			}
			if code, ok := httpErrors[p.tkn()]; !ok {
				return p.err("Syntax", "Invalid error code '"+p.tkn()+"'")
			} else if val, exists := p.cfg.ErrorPages[code]; exists {
				return p.err("Config", p.tkn()+" error page already configured to be '"+val+"'")
			} else {
				if !p.lexer.NextArg() {
					return p.argErr()
				}
				p.cfg.ErrorPages[code] = p.tkn()
			}
			return nil
		},
		"header": func(p *parser) error {
			var head Headers
			var isNewPattern bool

			if !p.lexer.NextArg() {
				return p.argErr()
			}
			pattern := p.tkn()

			// See if we already have a definition for this URL pattern...
			for _, h := range p.cfg.Headers {
				if h.Url == pattern {
					head = h
					break
				}
			}

			// ...otherwise, this is a new pattern
			if head.Url == "" {
				head.Url = pattern
				isNewPattern = true
			}

			processHeaderBlock := func() error {
				err := p.openCurlyBrace()
				if err != nil {
					return err
				}
				for p.lexer.Next() {
					if p.tkn() == "}" {
						break
					}
					h := Header{Name: p.tkn()}
					if p.lexer.NextArg() {
						h.Value = p.tkn()
					}
					head.Headers = append(head.Headers, h)
				}
				err = p.closeCurlyBrace()
				if err != nil {
					return err
				}
				return nil
			}

			// A single header could be declared on the same line, or
			// multiple headers can be grouped by URL pattern, so we have
			// to look for both here.
			if p.lexer.NextArg() {
				if p.tkn() == "{" {
					err := processHeaderBlock()
					if err != nil {
						return err
					}
				} else {
					h := Header{Name: p.tkn()}
					if p.lexer.NextArg() {
						h.Value = p.tkn()
					}
					head.Headers = append(head.Headers, h)
				}
			} else {
				// Okay, it might be an opening curly brace on the next line
				if !p.lexer.Next() {
					return p.eofErr()
				}
				err := processHeaderBlock()
				if err != nil {
					return err
				}
			}

			if isNewPattern {
				p.cfg.Headers = append(p.cfg.Headers, head)
			} else {
				for i := 0; i < len(p.cfg.Headers); i++ {
					if p.cfg.Headers[i].Url == pattern {
						p.cfg.Headers[i] = head
						break
					}
				}
			}

			return nil
		},
		"tls": func(p *parser) error {
			tls := TLSConfig{Enabled: true}

			if !p.lexer.NextArg() {
				return p.argErr()
			}
			tls.Certificate = p.tkn()

			if !p.lexer.NextArg() {
				return p.argErr()
			}
			tls.Key = p.tkn()

			p.cfg.TLS = tls
			return nil
		},
	}
}