/*****************************************************************
 *   Licensed to the Apache Software Foundation (ASF) under one
 *  or more contributor license agreements.  See the NOTICE file
 *  distributed with this work for additional information
 *  regarding copyright ownership.  The ASF licenses this file
 *  to you under the Apache License, Version 2.0 (the
 *  "License"); you may not use this file except in compliance
 *  with the License.  You may obtain a copy of the License at
 *
 *    https://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing,
 *  software distributed under the License is distributed on an
 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 *  KIND, either express or implied.  See the License for the
 *  specific language governing permissions and limitations
 *  under the License.
 ****************************************************************/

package org.apache.cayenne.exp.property;

import org.apache.cayenne.exp.Expression;
import org.apache.cayenne.exp.ExpressionFactory;
import org.apache.cayenne.exp.FunctionExpressionFactory;

/**
 * Property that represents date/time attribute.
 * <pre>{@code
 * ObjectSelect.query(Artist.class)
 *      .where(Artist.DATE_OF_BIRTH.year().lte(1900))
 *      .or(Artist.DATE_OF_BIRTH.month().between(6, 8))
 * }</pre>
 *
 * @see org.apache.cayenne.exp.property
 * @since 4.2
 */
public class DateProperty<E> extends BaseProperty<E> implements ComparableProperty<E> {

    /**
     * Constructs a new property with the given name and expression
     *
     * @param name       of the property (will be used as alias for the expression)
     * @param expression expression for property
     * @param type       of the property
     */
    protected DateProperty(String name, Expression expression, Class<E> type) {
        super(name, expression, type);
    }

    /**
     * It is a caller responsibility to check that underlying attribute has year component
     *
     * @return new property that represents year component of this date property
     * @see FunctionExpressionFactory#yearExp(Expression)
     */
    public NumericProperty<Integer> year() {
        return PropertyFactory.createNumeric(FunctionExpressionFactory.yearExp(getExpression()), Integer.class);
    }

    /**
     * It is a caller responsibility to check that underlying attribute has month component
     *
     * @return new property that represents month component of this date property
     * @see FunctionExpressionFactory#monthExp(Expression)
     */
    public NumericProperty<Integer> month() {
        return PropertyFactory.createNumeric(FunctionExpressionFactory.monthExp(getExpression()), Integer.class);
    }

    /**
     * It is a caller responsibility to check that underlying attribute has day component
     *
     * @return new property that represents day of month component of this date property
     * @see FunctionExpressionFactory#dayOfMonthExp(Expression)
     */
    public NumericProperty<Integer> dayOfMonth() {
        return PropertyFactory.createNumeric(FunctionExpressionFactory.dayOfMonthExp(getExpression()), Integer.class);
    }

    /**
     * It is a caller responsibility to check that underlying attribute has day component
     *
     * @return new property that represents day of year component of this date property
     * @see FunctionExpressionFactory#dayOfMonthExp(Expression)
     */
    public NumericProperty<Integer> dayOfYear() {
        return PropertyFactory.createNumeric(FunctionExpressionFactory.dayOfYearExp(getExpression()), Integer.class);
    }

    /**
     * It is a caller responsibility to check that underlying attribute has time component
     *
     * @return new property that represents hour component of this time property
     * @see FunctionExpressionFactory#hourExp(Expression)
     */
    public NumericProperty<Integer> hour() {
        return PropertyFactory.createNumeric(FunctionExpressionFactory.hourExp(getExpression()), Integer.class);
    }

    /**
     * It is a caller responsibility to check that underlying attribute has time component
     *
     * @return new property that represents minute component of this time property
     * @see FunctionExpressionFactory#minuteExp(Expression)
     */
    public NumericProperty<Integer> minute() {
        return PropertyFactory.createNumeric(FunctionExpressionFactory.minuteExp(getExpression()), Integer.class);
    }

    /**
     * It is a caller responsibility to check that underlying attribute has time component
     *
     * @return new property that represents second component of this time property
     * @see FunctionExpressionFactory#secondExp(Expression)
     */
    public NumericProperty<Integer> second() {
        return PropertyFactory.createNumeric(FunctionExpressionFactory.secondExp(getExpression()), Integer.class);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public DateProperty<E> alias(String alias) {
        return PropertyFactory.createDate(alias, this.getExpression(), this.getType());
    }

    /**
     * @see FunctionExpressionFactory#maxExp(Expression)
     */
    public DateProperty<E> max() {
        return PropertyFactory.createDate(FunctionExpressionFactory.maxExp(getExpression()), getType());
    }

    /**
     * @see FunctionExpressionFactory#minExp(Expression)
     */
    public DateProperty<E> min() {
        return PropertyFactory.createDate(FunctionExpressionFactory.minExp(getExpression()), getType());
    }

    /**
     * @return property that will be translated relative to parent query
     */
    public DateProperty<E> enclosing() {
        return PropertyFactory.createDate(ExpressionFactory.enclosingObjectExp(getExpression()), getType());
    }
}
