// 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
//
//   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.

#include "udf/udf_test_harness.hpp"

#include <iostream>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <gtest/gtest.h>
#include "util/logging.h"
#include "common/logging.h"

namespace doris_udf {

DoubleVal zero_udf(FunctionContext* context) {
    return DoubleVal(0);
}

StringVal log_udf(FunctionContext* context, const StringVal& arg1) {
    std::cerr << (arg1.is_null ? "NULL" : std::string((char*)arg1.ptr, arg1.len)) << std::endl;
    return arg1;
}

StringVal upper_udf(FunctionContext* context, const StringVal& input) {
    if (input.is_null) {
        return StringVal::null();
    }

    // Create a new StringVal object that's the same length as the input
    StringVal result = StringVal(context, input.len);

    for (int i = 0; i < input.len; ++i) {
        result.ptr[i] = toupper(input.ptr[i]);
    }

    return result;
}

FloatVal min3(FunctionContext* context, const FloatVal& f1,
              const FloatVal& f2, const FloatVal& f3) {
    bool is_null = true;
    float v = 0.0;

    if (!f1.is_null) {
        if (is_null) {
            v = f1.val;
            is_null = false;
        } else {
            v = std::min(v, f1.val);
        }
    }

    if (!f2.is_null) {
        if (is_null) {
            v = f2.val;
            is_null = false;
        } else {
            v = std::min(v, f2.val);
        }
    }

    if (!f3.is_null) {
        if (is_null) {
            v = f3.val;
            is_null = false;
        } else {
            v = std::min(v, f3.val);
        }
    }

    return is_null ? FloatVal::null() : FloatVal(v);
}

StringVal concat(FunctionContext* context, int n, const StringVal* args) {
    int size = 0;
    bool all_null = true;

    for (int i = 0; i < n; ++i) {
        if (args[i].is_null) {
            continue;
        }

        size += args[i].len;
        all_null = false;
    }

    if (all_null) {
        return StringVal::null();
    }

    int offset = 0;
    StringVal result(context, size);

    for (int i = 0; i < n; ++i) {
        if (args[i].is_null) {
            continue;
        }

        memcpy(result.ptr + offset, args[i].ptr, args[i].len);
        offset += args[i].len;
    }

    return result;
}

IntVal num_var_args(FunctionContext*, const BigIntVal& dummy, int n, const IntVal* args) {
    return IntVal(n);
}

IntVal validat_udf(FunctionContext* context) {
    EXPECT_EQ(context->version(), FunctionContext::V2_0);
    EXPECT_FALSE(context->has_error());
    EXPECT_TRUE(context->error_msg() == NULL);
    return IntVal::null();
}

IntVal validate_fail(FunctionContext* context) {
    EXPECT_FALSE(context->has_error());
    EXPECT_TRUE(context->error_msg() == NULL);
    context->set_error("Fail");
    EXPECT_TRUE(context->has_error());
    EXPECT_TRUE(strcmp(context->error_msg(), "Fail") == 0);
    return IntVal::null();
}

IntVal validate_mem(FunctionContext* context) {
    EXPECT_TRUE(context->allocate(0) == NULL);
    uint8_t* buffer = context->allocate(10);
    EXPECT_TRUE(buffer != NULL);
    memset(buffer, 0, 10);
    context->free(buffer);
    return IntVal::null();
}

StringVal time_to_string(FunctionContext* context, const TimestampVal& time) {
    boost::posix_time::ptime t(*(boost::gregorian::date*)&time.date);
    t += boost::posix_time::nanoseconds(time.time_of_day);
    std::stringstream ss;
    ss << boost::posix_time::to_iso_extended_string(t) 
            << " " << boost::posix_time::to_simple_string(t.time_of_day());
    std::string s = ss.str();
    StringVal result(context, s.size());
    memcpy(result.ptr, s.data(), result.len);
    return result;
}

TEST(UdfTest, TestFunctionContext) {
    EXPECT_TRUE(UdfTestHarness::validat_udf<IntVal>(validat_udf, IntVal::null()));
    EXPECT_FALSE(UdfTestHarness::validat_udf<IntVal>(validate_fail, IntVal::null()));
    EXPECT_TRUE(UdfTestHarness::validat_udf<IntVal>(validate_mem, IntVal::null()));
}

TEST(UdfTest, TestValidate) {
    EXPECT_TRUE(UdfTestHarness::validat_udf<DoubleVal>(zero_udf, DoubleVal(0)));
    EXPECT_FALSE(UdfTestHarness::validat_udf<DoubleVal>(zero_udf, DoubleVal(10)));

    EXPECT_TRUE((UdfTestHarness::validat_udf<StringVal, StringVal>(
                     log_udf, StringVal("abcd"), StringVal("abcd"))));

    EXPECT_TRUE((UdfTestHarness::validat_udf<StringVal, StringVal>(
                     upper_udf, StringVal("abcd"), StringVal("ABCD"))));

    EXPECT_TRUE((UdfTestHarness::validat_udf<FloatVal, FloatVal, FloatVal, FloatVal>(
                     min3, FloatVal(1), FloatVal(2), FloatVal(3), FloatVal(1))));
    EXPECT_TRUE((UdfTestHarness::validat_udf<FloatVal, FloatVal, FloatVal, FloatVal>(
                     min3, FloatVal(1), FloatVal::null(), FloatVal(3), FloatVal(1))));
    EXPECT_TRUE((UdfTestHarness::validat_udf<FloatVal, FloatVal, FloatVal, FloatVal>(
                     min3, FloatVal::null(), FloatVal::null(),
                     FloatVal::null(), FloatVal::null())));
}

TEST(UdfTest, TestTimestampVal) {
    boost::gregorian::date d(2003, 3, 15);
    TimestampVal t1(*(int32_t*)&d);
    EXPECT_TRUE((UdfTestHarness::validat_udf<StringVal, TimestampVal>(
                     time_to_string, t1, "2003-03-15 00:00:00")));

    TimestampVal t2(*(int32_t*)&d, 1000L * 1000L * 5000L);
    EXPECT_TRUE((UdfTestHarness::validat_udf<StringVal, TimestampVal>(
                     time_to_string, t2, "2003-03-15 00:00:05")));
}

TEST(UdfTest, TestVarArgs) {
    std::vector<StringVal> input;
    input.push_back(StringVal("Hello"));
    input.push_back(StringVal("World"));

    EXPECT_TRUE((UdfTestHarness::validat_udf<StringVal, StringVal>(
                     concat, input, StringVal("HelloWorld"))));

    input.push_back(StringVal("More"));
    EXPECT_TRUE((UdfTestHarness::validat_udf<StringVal, StringVal>(
                     concat, input, StringVal("HelloWorldMore"))));

    std::vector<IntVal> args;
    args.resize(10);
    EXPECT_TRUE((UdfTestHarness::validat_udf<IntVal, BigIntVal, IntVal>(
                     num_var_args, BigIntVal(0), args, IntVal(args.size()))));
}
}

int main(int argc, char** argv) {
    std::string conffile = std::string(getenv("DORIS_HOME")) + "/conf/be.conf";
    if (!doris::config::init(conffile.c_str(), false)) {
        fprintf(stderr, "error read config file. \n");
        return -1;
    }
    init_glog("be-test");
    ::testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
}
