package schema

import (
	
	
	

	
	
	
	
)

var nopFormatter = Formatter{
	dialect: newNopDialect(),
}

type Formatter struct {
	dialect Dialect
	args    *namedArgList
}

func ( Dialect) Formatter {
	return Formatter{
		dialect: ,
	}
}

func () Formatter {
	return nopFormatter
}

func ( Formatter) () bool {
	return .dialect.Name() == dialect.Invalid
}

func ( Formatter) () Dialect {
	return .dialect
}

func ( Formatter) () byte {
	return .dialect.IdentQuote()
}

func ( Formatter) ( []byte,  string) []byte {
	return dialect.AppendIdent(, , .IdentQuote())
}

func ( Formatter) ( []byte,  reflect.Value) []byte {
	if .Kind() == reflect.Ptr && .IsNil() {
		return dialect.AppendNull()
	}
	 := .dialect.Appender(.Type())
	return (, , )
}

func ( Formatter) ( feature.Feature) bool {
	return .dialect.Features().Has()
}

func ( Formatter) ( NamedArgAppender) Formatter {
	return Formatter{
		dialect: .dialect,
		args:    .args.WithArg(),
	}
}

func ( Formatter) ( string,  interface{}) Formatter {
	return Formatter{
		dialect: .dialect,
		args:    .args.WithArg(&namedArg{name: , value: }),
	}
}

func ( Formatter) ( string,  ...interface{}) string {
	if .IsNop() || ( == nil && .args == nil) || strings.IndexByte(, '?') == -1 {
		return 
	}
	return internal.String(.AppendQuery(nil, , ...))
}

func ( Formatter) ( []byte,  string,  ...interface{}) []byte {
	if .IsNop() || ( == nil && .args == nil) || strings.IndexByte(, '?') == -1 {
		return append(, ...)
	}
	return .append(, parser.NewString(), )
}

func ( Formatter) ( []byte,  *parser.Parser,  []interface{}) []byte {
	var  NamedArgAppender
	if len() == 1 {
		if ,  := [0].(NamedArgAppender);  {
			 = 
		} else if ,  := newStructArgs(, [0]);  {
			 = 
		}
	}

	var  int
	for .Valid() {
		,  := .ReadSep('?')
		if ! {
			 = append(, ...)
			continue
		}
		if len() > 0 && [len()-1] == '\\' {
			 = append(, [:len()-1]...)
			 = append(, '?')
			continue
		}
		 = append(, ...)

		,  := .ReadIdentifier()
		if  != "" {
			if  {
				,  := strconv.Atoi()
				if  != nil {
					goto 
				}

				if  >= len() {
					goto 
				}

				 = .appendArg(, [])
				continue
			}

			if  != nil {
				,  = .AppendNamedArg(, , )
				if  {
					continue
				}
			}

			,  = .args.AppendNamedArg(, , )
			if  {
				continue
			}

		:
			 = append(, '?')
			 = append(, ...)
			continue
		}

		if  >= len() {
			 = append(, '?')
			continue
		}

		 := []
		++

		 = .appendArg(, )
	}

	return 
}

func ( Formatter) ( []byte,  interface{}) []byte {
	switch arg := .(type) {
	case QueryAppender:
		,  := .AppendQuery(, )
		if  != nil {
			return dialect.AppendError(, )
		}
		return 
	default:
		return .dialect.Append(, , )
	}
}

//------------------------------------------------------------------------------

type NamedArgAppender interface {
	AppendNamedArg(fmter Formatter, b []byte, name string) ([]byte, bool)
}

//------------------------------------------------------------------------------

type namedArgList struct {
	arg  NamedArgAppender
	next *namedArgList
}

func ( *namedArgList) ( NamedArgAppender) *namedArgList {
	return &namedArgList{
		arg:  ,
		next: ,
	}
}

func ( *namedArgList) ( Formatter,  []byte,  string) ([]byte, bool) {
	for  != nil && .arg != nil {
		if ,  := .arg.AppendNamedArg(, , );  {
			return , true
		}
		 = .next
	}
	return , false
}

//------------------------------------------------------------------------------

type namedArg struct {
	name  string
	value interface{}
}

var _ NamedArgAppender = (*namedArg)(nil)

func ( *namedArg) ( Formatter,  []byte,  string) ([]byte, bool) {
	if .name ==  {
		return .appendArg(, .value), true
	}
	return , false
}

//------------------------------------------------------------------------------

var _ NamedArgAppender = (*structArgs)(nil)

type structArgs struct {
	table *Table
	strct reflect.Value
}

func ( Formatter,  interface{}) (*structArgs, bool) {
	 := reflect.ValueOf()
	if !.IsValid() {
		return nil, false
	}

	 = reflect.Indirect()
	if .Kind() != reflect.Struct {
		return nil, false
	}

	return &structArgs{
		table: .Dialect().Tables().Get(.Type()),
		strct: ,
	}, true
}

func ( *structArgs) ( Formatter,  []byte,  string) ([]byte, bool) {
	return .table.AppendNamedArg(, , , .strct)
}