/*
 * Licensed to CRATE Technology GmbH ("Crate") under one or more contributor
 * license agreements.  See the NOTICE file distributed with this work for
 * additional information regarding copyright ownership.  Crate 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
 *
 *     http://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.
 *
 * However, if you have executed another commercial license agreement
 * with Crate these terms will supersede the license and you may use the
 * software solely pursuant to the terms of the relevant commercial agreement.
 */

package io.crate.operation.scalar.arithmetic;

import io.crate.analyze.symbol.Function;
import io.crate.analyze.symbol.Literal;
import io.crate.analyze.symbol.Symbol;
import io.crate.metadata.FunctionIdent;
import io.crate.metadata.Reference;
import io.crate.metadata.StmtCtx;
import io.crate.operation.Input;
import io.crate.operation.scalar.AbstractScalarFunctionsTest;
import io.crate.types.DataType;
import io.crate.types.DataTypes;
import org.hamcrest.Matchers;
import org.junit.Test;

import java.util.Arrays;
import java.util.List;

import static io.crate.testing.TestingHelpers.createReference;
import static io.crate.testing.TestingHelpers.isLiteral;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.nullValue;

public class RoundFunctionTest extends AbstractScalarFunctionsTest {

    private Number evaluate(Number number, DataType dataType) {
        List<DataType> dataTypes = Arrays.asList(dataType);
        Input input = (Input) Literal.newLiteral(dataType, number);
        return getFunction(dataTypes).evaluate(input);
    }

    private Long evaluate(Double number) {
        return evaluate(number, DataTypes.DOUBLE).longValue();
    }

    private Integer evaluate(Float number) {
        return evaluate(number, DataTypes.FLOAT).intValue();
    }

    private RoundFunction getFunction(List<DataType> dataTypes) {
        return (RoundFunction)functions.get(new FunctionIdent(RoundFunction.NAME, dataTypes));
    }

    private Symbol normalize(Number number, DataType type) {
        RoundFunction function = getFunction(Arrays.asList(type));
        return function.normalizeSymbol(new Function(function.info(),
                Arrays.<Symbol>asList(Literal.newLiteral(type, number))), new StmtCtx());
    }

    @Test
    public void testEvaluateOnFloat() throws Exception {
        assertThat(evaluate(42.2f), is(42));
        assertThat(evaluate(null, DataTypes.FLOAT), nullValue());
    }

    @Test
    public void testEvaluateOnDouble() throws Exception {
        assertThat(evaluate(42.d), is(42L));
        assertThat(evaluate(null, DataTypes.DOUBLE), nullValue());
    }

    @Test
    public void testEvaluateOnIntegerAndLong() throws Exception {
        assertThat((Long) evaluate(2L, DataTypes.LONG), is(2L));
        assertThat(evaluate(null, DataTypes.LONG), nullValue());
        assertThat((Integer) evaluate(2, DataTypes.INTEGER), is(2));
        assertThat(evaluate(null, DataTypes.INTEGER), nullValue());
    }

    @Test
    public void testNormalizeValueSymbol() throws Exception {
        assertThat(normalize(42.9, DataTypes.DOUBLE), isLiteral(43L));
        assertThat(normalize(42.9f, DataTypes.FLOAT), isLiteral(43));
        assertThat(normalize(null, DataTypes.FLOAT), isLiteral(null, DataTypes.INTEGER));
    }

    @Test
    public void testNormalizeReference() throws Exception {
        Reference height = createReference("height", DataTypes.DOUBLE);
        RoundFunction round = getFunction(Arrays.<DataType>asList(DataTypes.DOUBLE));
        Function function = new Function(round.info(), Arrays.<Symbol>asList(height));
        Function normalized = (Function) round.normalizeSymbol(function, new StmtCtx());
        assertThat(normalized, Matchers.sameInstance(function));
    }
}

@@ -1083,1759 +1083,242 @@
 ate.
-analyze.symbol.Function;%0Aimport io.crate.analyze.symbol.Literal;%0Aimport io.crate.analyze.symbol.Symbol;%0Aimport io.crate.metadata.FunctionIdent;%0Aimport io.crate.metadata.Reference;%0Aimport io.crate.metadata.StmtCtx;%0Aimport io.crate.operation.Input;%0Aimport io.crate.operation.scalar.AbstractScalarFunctionsTest;%0Aimport io.crate.types.DataType;%0Aimport io.crate.types.DataTypes;%0Aimport org.hamcrest.Matchers;%0Aimport org.junit.Test;%0A%0Aimport java.util.Arrays;%0Aimport java.util.List;%0A%0Aimport static io.crate.testing.TestingHelpers.createReference;%0Aimport static io.crate.testing.TestingHelpers.isLiteral;%0Aimport static org.hamcrest.CoreMatchers.is;%0Aimport static org.hamcrest.CoreMatchers.nullValue;%0A%0Apublic class RoundFunctionTest extends AbstractScalarFunctionsTest %7B%0A%0A    private Number evaluate(Number number, DataType dataType) %7B%0A        List%3CDataType%3E dataTypes = Arrays.asList(dataType);%0A        Input input = (Input) Literal.newLiteral(dataType, number);%0A        return getFunction(dataTypes).evaluate(input);%0A    %7D%0A%0A    private Long evaluate(Double number) %7B%0A        return evaluate(number, DataTypes.DOUBLE).longValue();%0A    %7D%0A%0A    private Integer evaluate(Float number) %7B%0A        return evaluate(number, DataTypes.FLOAT).intValue();%0A    %7D%0A%0A    private RoundFunction getFunction(List%3CDataType%3E dataTypes) %7B%0A        return (RoundFunction)functions.get(new FunctionIdent(RoundFunction.NAME, dataTypes));%0A    %7D%0A%0A    private Symbol normalize(Number number, DataType type) %7B%0A        RoundFunction function = getFunction(Arrays.asList(type));%0A        return function.normalizeSymbol(new Function(function.info(),%0A                Arrays.%3CSymbol%3EasList(Literal.newLiteral(type, number))), new StmtCtx());%0A    %7D%0A%0A    @Test%0A    public void testEvaluateOnFloat
+operation.scalar.AbstractScalarFunctionsTest;%0Aimport org.junit.Test;%0A%0Aimport static io.crate.testing.TestingHelpers.isFunction;%0A%0Apublic class RoundFunctionTest extends AbstractScalarFunctionsTest %7B%0A%0A    @Test%0A    public void testRound
 () t
@@ -1349,30 +1349,25 @@
   assert
-That(e
+E
 valuate(
 42.2f), 
@@ -1362,22 +1362,26 @@
 ate(
-42.2f), is(42)
+%22round(42.2)%22, 42L
 );%0A 
@@ -1397,178 +1397,34 @@
 sert
-That(evaluate(null, DataTypes.FLOAT), nullValue());%0A    %7D%0A%0A    @Test%0A    public void testEvaluateOnDouble() throws Exception %7B%0A        assertThat(evaluate(42.d), is(
+Evaluate(%22round(42)%22, 
 42L)
-)
 ;%0A  
@@ -1431,38 +1431,33 @@
       assert
-That(e
+E
 valuate(
 null, DataTy
@@ -1448,192 +1448,40 @@
 ate(
-null, DataTypes.DOUBLE), nullValue());%0A    %7D%0A%0A    @Test%0A    public void testEvaluateOnIntegerAndLong() throws Exception %7B%0A        assertThat((Long) evaluate(2L, DataTypes.LONG), is(2L)
+%22round(cast(42 as integer))%22, 42
 );%0A 
@@ -1489,38 +1489,33 @@
       assert
-That(e
+E
 valuate(
 null, DataTy
@@ -1506,111 +1506,40 @@
 ate(
-null, DataTypes.LONG), nullValue());%0A        assertThat((Integer) evaluate(2, DataTypes.INTEGER), is(2)
+%22round(cast(42.2 as float))%22, 42
 );%0A 
@@ -1555,22 +1555,17 @@
 sert
-That(e
+E
 valuate(
 null
@@ -1564,354 +1564,86 @@
 ate(
-null, DataTypes.INTEGER), nullValue());%0A    %7D%0A%0A    @Test%0A    public void testNormalizeValueSymbol() throws Exception %7B%0A        assertThat(normalize(42.9, DataTypes.DOUBLE), isLiteral(43L));%0A        assertThat(normalize(42.9f, DataTypes.FLOAT), isLiteral(43));%0A        assertThat(normalize(null, DataTypes.FLOAT), isLiteral(null, DataTypes.INTEGER
+%22round(null)%22, null);%0A%0A        assertNormalize(%22round(id)%22, isFunction(%22round%22
 ));%0A
@@ -1683,25 +1683,18 @@
 test
-NormalizeReferenc
+InvalidTyp
 e() 
@@ -1724,396 +1724,119 @@
     
-Reference height = createReference(%22height%22, DataTypes.DOUBLE);%0A        RoundFunction round = getFunction(Arrays.%3CDataType%3EasList(DataTypes.DOUBLE));%0A        Function function = new Function(round.info(), Arrays.%3CSymbol%3EasList(height));%0A        Function normalized = (Function) round.normalizeSymbol(function, new StmtCtx());%0A        assertThat(normalized, Matchers.sameInstance(function)
+expectedException.expectMessage(%22unknown function: round(string)%22);%0A        assertEvaluate(%22round('foo')%22, null
 );%0A 

simplify RoundFunctionTest