// Modeling of columns

package jet

type column interface {
	Name() string
	TableName() string

	setTableName(table string)
	setSubQuery(subQuery SelectTable)
	defaultAlias() string
}

// Column is common column interface for all types of columns.
type Column interface {
	Expression
	column
}

// The base type for real materialized columns.
type columnImpl struct {
	expressionInterfaceImpl

	name      string
	tableName string

	subQuery SelectTable
}

func newColumn(name string, tableName string, parent Column) columnImpl {
	bc := columnImpl{
		name:      name,
		tableName: tableName,
	}

	bc.expressionInterfaceImpl.parent = parent

	return bc
}

func (c *columnImpl) Name() string {
	return c.name
}

func (c *columnImpl) TableName() string {
	return c.tableName
}

func (c *columnImpl) setTableName(table string) {
	c.tableName = table
}

func (c *columnImpl) setSubQuery(subQuery SelectTable) {
	c.subQuery = subQuery
}

func (c *columnImpl) defaultAlias() string {
	if c.tableName != "" {
		return c.tableName + "." + c.name
	}

	return c.name
}

func (c *columnImpl) serializeForOrderBy(statement statementType, out *sqlBuilder) error {
	if statement == setStatement {
		// set Statement (UNION, EXCEPT ...) can reference only select projections in order by clause
		out.writeString(`"` + c.defaultAlias() + `"`) //always quote

		return nil
	}

	return c.serialize(statement, out)
}

func (c columnImpl) serializeForProjection(statement statementType, out *sqlBuilder) error {
	err := c.serialize(statement, out)

	if err != nil {
		return err
	}

	out.writeString(`AS "` + c.defaultAlias() + `"`)

	return nil
}

func (c columnImpl) serialize(statement statementType, out *sqlBuilder, options ...serializeOption) error {

	if c.subQuery != nil {
		out.writeIdentifier(c.subQuery.Alias())
		out.writeByte('.')
		out.writeQuotedString(c.defaultAlias())
	} else {
		if c.tableName != "" {
			out.writeIdentifier(c.tableName)
			out.writeByte('.')
		}

		out.writeIdentifier(c.name)
	}

	return nil
}

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

// ColumnList is redefined type to support list of columns as single projection
type ColumnList []Column

// projection interface implementation
func (cl ColumnList) isProjectionType() {}

func (cl ColumnList) from(subQuery SelectTable) projection {
	newProjectionList := ProjectionList{}

	for _, column := range cl {
		newProjectionList = append(newProjectionList, column.from(subQuery))
	}

	return newProjectionList
}

func (cl ColumnList) serializeForProjection(statement statementType, out *sqlBuilder) error {
	projections := columnListToProjectionList(cl)

	err := serializeProjectionList(statement, projections, out)

	if err != nil {
		return err
	}

	return nil
}

// dummy column interface implementation

// Name is placeholder for ColumnList to implement Column interface
func (cl ColumnList) Name() string { return "" }

// TableName is placeholder for ColumnList to implement Column interface
func (cl ColumnList) TableName() string                { return "" }
func (cl ColumnList) setTableName(name string)         {}
func (cl ColumnList) setSubQuery(subQuery SelectTable) {}
func (cl ColumnList) defaultAlias() string             { return "" }
