package clause

import (
	
)

const (
	AndWithSpace = " AND "
	OrWithSpace  = " OR "
)

// Where where clause
type Where struct {
	Exprs []Expression
}

// Name where clause name
func ( Where) () string {
	return "WHERE"
}

// Build build where clause
func ( Where) ( Builder) {
	// Switch position if the first query expression is a single Or condition
	for ,  := range .Exprs {
		if ,  := .(OrConditions); ! || len(.Exprs) > 1 {
			if  != 0 {
				.Exprs[0], .Exprs[] = .Exprs[], .Exprs[0]
			}
			break
		}
	}

	buildExprs(.Exprs, , AndWithSpace)
}

func buildExprs( []Expression,  Builder,  string) {
	 := false

	for ,  := range  {
		if  > 0 {
			if ,  := .(OrConditions);  && len(.Exprs) == 1 {
				.WriteString(OrWithSpace)
			} else {
				.WriteString()
			}
		}

		if len() > 1 {
			switch v := .(type) {
			case OrConditions:
				if len(.Exprs) == 1 {
					if ,  := .Exprs[0].(Expr);  {
						 := strings.ToUpper(.SQL)
						 = strings.Contains(, AndWithSpace) || strings.Contains(, OrWithSpace)
					}
				}
			case AndConditions:
				if len(.Exprs) == 1 {
					if ,  := .Exprs[0].(Expr);  {
						 := strings.ToUpper(.SQL)
						 = strings.Contains(, AndWithSpace) || strings.Contains(, OrWithSpace)
					}
				}
			case Expr:
				 := strings.ToUpper(.SQL)
				 = strings.Contains(, AndWithSpace) || strings.Contains(, OrWithSpace)
			case NamedExpr:
				 := strings.ToUpper(.SQL)
				 = strings.Contains(, AndWithSpace) || strings.Contains(, OrWithSpace)
			}
		}

		if  {
			.WriteByte('(')
			.Build()
			.WriteByte(')')
			 = false
		} else {
			.Build()
		}
	}
}

// MergeClause merge where clauses
func ( Where) ( *Clause) {
	if ,  := .Expression.(Where);  {
		 := make([]Expression, len(.Exprs)+len(.Exprs))
		copy(, .Exprs)
		copy([len(.Exprs):], .Exprs)
		.Exprs = 
	}

	.Expression = 
}

func ( ...Expression) Expression {
	if len() == 0 {
		return nil
	}

	if len() == 1 {
		if ,  := [0].(OrConditions); ! {
			return [0]
		}
	}

	return AndConditions{Exprs: }
}

type AndConditions struct {
	Exprs []Expression
}

func ( AndConditions) ( Builder) {
	if len(.Exprs) > 1 {
		.WriteByte('(')
		buildExprs(.Exprs, , AndWithSpace)
		.WriteByte(')')
	} else {
		buildExprs(.Exprs, , AndWithSpace)
	}
}

func ( ...Expression) Expression {
	if len() == 0 {
		return nil
	}
	return OrConditions{Exprs: }
}

type OrConditions struct {
	Exprs []Expression
}

func ( OrConditions) ( Builder) {
	if len(.Exprs) > 1 {
		.WriteByte('(')
		buildExprs(.Exprs, , OrWithSpace)
		.WriteByte(')')
	} else {
		buildExprs(.Exprs, , OrWithSpace)
	}
}

func ( ...Expression) Expression {
	if len() == 0 {
		return nil
	}
	return NotConditions{Exprs: }
}

type NotConditions struct {
	Exprs []Expression
}

func ( NotConditions) ( Builder) {
	if len(.Exprs) > 1 {
		.WriteByte('(')
	}

	for ,  := range .Exprs {
		if  > 0 {
			.WriteString(AndWithSpace)
		}

		if ,  := .(NegationExpressionBuilder);  {
			.NegationBuild()
		} else {
			.WriteString("NOT ")
			,  := .(Expr)
			if  {
				 := strings.ToUpper(.SQL)
				if  = strings.Contains(, AndWithSpace) || strings.Contains(, OrWithSpace);  {
					.WriteByte('(')
				}
			}

			.Build()

			if  {
				.WriteByte(')')
			}
		}
	}

	if len(.Exprs) > 1 {
		.WriteByte(')')
	}
}