/*
 * 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.
 */
package org.apache.phoenix.expression;

import static org.junit.Assert.assertEquals;

import java.sql.SQLException;
import java.util.List;
import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
import org.apache.phoenix.expression.function.StringToArrayFunction;
import org.apache.phoenix.schema.SortOrder;
import org.apache.phoenix.schema.types.*;
import org.junit.Test;

import org.apache.phoenix.thirdparty.com.google.common.collect.Lists;

public class StringToArrayFunctionTest {

  private static void testExpression(LiteralExpression array, LiteralExpression delimiter,
    LiteralExpression nullString, PhoenixArray expected) throws SQLException {
    List<Expression> expressions = Lists.newArrayList((Expression) array);
    expressions.add(delimiter);
    expressions.add(nullString);

    Expression stringToArrayFunction = new StringToArrayFunction(expressions);
    ImmutableBytesWritable ptr = new ImmutableBytesWritable();
    stringToArrayFunction.evaluate(null, ptr);
    PhoenixArray result = (PhoenixArray) stringToArrayFunction.getDataType().toObject(ptr,
      stringToArrayFunction.getSortOrder(), stringToArrayFunction.getMaxLength(),
      stringToArrayFunction.getScale());
    assertEquals(expected, result);
  }

  private static void test(String string, String delimiter, String nullString,
    PhoenixArray expected, SortOrder stringSortOrder, SortOrder delimiterSortOrder,
    SortOrder nullStringSortOrder, PDataType stringType, PDataType delimiterType,
    PDataType nullStringType) throws SQLException {
    LiteralExpression arrayLiteral, delimiterLiteral, nullStringLiteral;
    arrayLiteral = LiteralExpression.newConstant(string, stringType, null, null, stringSortOrder,
      Determinism.ALWAYS);
    delimiterLiteral = LiteralExpression.newConstant(delimiter, delimiterType, null, null,
      delimiterSortOrder, Determinism.ALWAYS);
    nullStringLiteral = LiteralExpression.newConstant(nullString, nullStringType, null, null,
      nullStringSortOrder, Determinism.ALWAYS);
    testExpression(arrayLiteral, delimiterLiteral, nullStringLiteral, expected);
  }

  @Test
  public void testStringToArrayFunction1() throws SQLException {
    String string = "1,2,3,4,5";
    Object[] o1 = new Object[] { "1", "2", "3", "4", "5" };
    PhoenixArray expected = new PhoenixArray(PVarchar.INSTANCE, o1);
    String delimiter = ",";
    String nullString = "*";
    test(string, delimiter, nullString, expected, SortOrder.ASC, SortOrder.ASC, SortOrder.ASC,
      PVarchar.INSTANCE, PVarchar.INSTANCE, PVarchar.INSTANCE);
    test(string, delimiter, nullString, expected, SortOrder.DESC, SortOrder.ASC, SortOrder.ASC,
      PVarchar.INSTANCE, PVarchar.INSTANCE, PVarchar.INSTANCE);
  }

  @Test
  public void testStringToArrayFunction2() throws SQLException {
    String string = "1,2,3,4,5";
    Object[] o1 = new Object[] { "1", "2", "3", "4", "5" };
    PhoenixArray expected = new PhoenixArray(PVarchar.INSTANCE, o1);
    String delimiter = ",";
    String nullString = "";
    test(string, delimiter, nullString, expected, SortOrder.ASC, SortOrder.ASC, SortOrder.ASC,
      PVarchar.INSTANCE, PVarchar.INSTANCE, PVarchar.INSTANCE);
    test(string, delimiter, nullString, expected, SortOrder.DESC, SortOrder.ASC, SortOrder.ASC,
      PVarchar.INSTANCE, PVarchar.INSTANCE, PVarchar.INSTANCE);
  }

  @Test
  public void testStringToArrayFunction3() throws SQLException {
    String string = "1234";
    Object[] o1 = new Object[] { "1", "2", "3", "4" };
    PhoenixArray expected = new PhoenixArray(PVarchar.INSTANCE, o1);
    String delimiter = null;
    String nullString = "";
    test(string, delimiter, nullString, expected, SortOrder.ASC, SortOrder.ASC, SortOrder.ASC,
      PVarchar.INSTANCE, PVarchar.INSTANCE, PVarchar.INSTANCE);
    test(string, delimiter, nullString, expected, SortOrder.DESC, SortOrder.ASC, SortOrder.ASC,
      PVarchar.INSTANCE, PVarchar.INSTANCE, PVarchar.INSTANCE);
  }

  @Test
  public void testStringToArrayFunction4() throws SQLException {
    String string = "1";
    Object[] o1 = new Object[] { "1" };
    PhoenixArray expected = new PhoenixArray(PVarchar.INSTANCE, o1);
    String delimiter = ",";
    String nullString = "";
    test(string, delimiter, nullString, expected, SortOrder.ASC, SortOrder.ASC, SortOrder.ASC,
      PVarchar.INSTANCE, PVarchar.INSTANCE, PVarchar.INSTANCE);
    test(string, delimiter, nullString, expected, SortOrder.DESC, SortOrder.ASC, SortOrder.ASC,
      PVarchar.INSTANCE, PVarchar.INSTANCE, PVarchar.INSTANCE);
  }

  @Test
  public void testStringToArrayFunction5() throws SQLException {
    String string = "hello, hello, hello";
    Object[] o1 = new Object[] { "hello", "hello", "hello" };
    PhoenixArray expected = new PhoenixArray(PVarchar.INSTANCE, o1);
    String delimiter = ", ";
    String nullString = "";
    test(string, delimiter, nullString, expected, SortOrder.ASC, SortOrder.ASC, SortOrder.ASC,
      PVarchar.INSTANCE, PVarchar.INSTANCE, PVarchar.INSTANCE);
    test(string, delimiter, nullString, expected, SortOrder.DESC, SortOrder.ASC, SortOrder.ASC,
      PVarchar.INSTANCE, PVarchar.INSTANCE, PVarchar.INSTANCE);
  }

  @Test
  public void testStringToArrayFunction6() throws SQLException {
    String string = "1.2...2.3...5.6";
    Object[] o1 = new Object[] { "1.2", "2.3", "5.6" };
    PhoenixArray expected = new PhoenixArray(PVarchar.INSTANCE, o1);
    String delimiter = "...";
    String nullString = "";
    test(string, delimiter, nullString, expected, SortOrder.ASC, SortOrder.ASC, SortOrder.ASC,
      PVarchar.INSTANCE, PVarchar.INSTANCE, PVarchar.INSTANCE);
    test(string, delimiter, nullString, expected, SortOrder.DESC, SortOrder.ASC, SortOrder.ASC,
      PVarchar.INSTANCE, PVarchar.INSTANCE, PVarchar.INSTANCE);
  }

  @Test
  public void testStringToArrayFunction7() throws SQLException {
    String string = "a\\b\\c\\d\\e\\f";
    Object[] o1 = new Object[] { "a", "b", "c", "d", "e", "f" };
    PhoenixArray expected = new PhoenixArray(PVarchar.INSTANCE, o1);
    String delimiter = "\\";
    String nullString = "";
    test(string, delimiter, nullString, expected, SortOrder.ASC, SortOrder.ASC, SortOrder.ASC,
      PVarchar.INSTANCE, PVarchar.INSTANCE, PVarchar.INSTANCE);
    test(string, delimiter, nullString, expected, SortOrder.DESC, SortOrder.ASC, SortOrder.ASC,
      PVarchar.INSTANCE, PVarchar.INSTANCE, PVarchar.INSTANCE);
  }

  @Test
  public void testStringToArrayFunction8() throws SQLException {
    String string = "a-b-c-d-e-f-";
    Object[] o1 = new Object[] { "a", "b", "c", "d", "e", "f" };
    PhoenixArray expected = new PhoenixArray(PVarchar.INSTANCE, o1);
    String delimiter = "-";
    String nullString = "";
    test(string, delimiter, nullString, expected, SortOrder.ASC, SortOrder.ASC, SortOrder.ASC,
      PVarchar.INSTANCE, PVarchar.INSTANCE, PVarchar.INSTANCE);
    test(string, delimiter, nullString, expected, SortOrder.DESC, SortOrder.ASC, SortOrder.ASC,
      PVarchar.INSTANCE, PVarchar.INSTANCE, PVarchar.INSTANCE);
  }

  @Test
  public void testStringToArrayFunction9() throws SQLException {
    String string = "a b c d e f";
    Object[] o1 = new Object[] { "a", "b", "c", "d", "e", "f" };
    PhoenixArray expected = new PhoenixArray(PVarchar.INSTANCE, o1);
    String delimiter = " ";
    String nullString = "";
    test(string, delimiter, nullString, expected, SortOrder.ASC, SortOrder.ASC, SortOrder.ASC,
      PVarchar.INSTANCE, PVarchar.INSTANCE, PVarchar.INSTANCE);
    test(string, delimiter, nullString, expected, SortOrder.DESC, SortOrder.ASC, SortOrder.ASC,
      PVarchar.INSTANCE, PVarchar.INSTANCE, PVarchar.INSTANCE);
  }

  @Test
  public void testStringToArrayFunction10() throws SQLException {
    String string = "axbxcxdxexf";
    Object[] o1 = new Object[] { "a", "b", "c", "d", "e", "f" };
    PhoenixArray expected = new PhoenixArray(PVarchar.INSTANCE, o1);
    String delimiter = "x";
    String nullString = "";
    test(string, delimiter, nullString, expected, SortOrder.ASC, SortOrder.ASC, SortOrder.ASC,
      PVarchar.INSTANCE, PVarchar.INSTANCE, PVarchar.INSTANCE);
    test(string, delimiter, nullString, expected, SortOrder.DESC, SortOrder.ASC, SortOrder.ASC,
      PVarchar.INSTANCE, PVarchar.INSTANCE, PVarchar.INSTANCE);
  }

  @Test
  public void testStringToArrayFunction11() throws SQLException {
    String string = "axbxcxdxexfx*";
    Object[] o1 = new Object[] { "a", "b", "c", "d", "e", "f", null };
    PhoenixArray expected = new PhoenixArray(PVarchar.INSTANCE, o1);
    String delimiter = "x";
    String nullString = "*";
    test(string, delimiter, nullString, expected, SortOrder.ASC, SortOrder.ASC, SortOrder.ASC,
      PVarchar.INSTANCE, PVarchar.INSTANCE, PVarchar.INSTANCE);
    test(string, delimiter, nullString, expected, SortOrder.DESC, SortOrder.ASC, SortOrder.ASC,
      PVarchar.INSTANCE, PVarchar.INSTANCE, PVarchar.INSTANCE);
  }

  @Test
  public void testStringToArrayFunction12() throws SQLException {
    String string = "* a b c d e f";
    Object[] o1 = new Object[] { null, "a", "b", "c", "d", "e", "f" };
    PhoenixArray expected = new PhoenixArray(PVarchar.INSTANCE, o1);
    String delimiter = " ";
    String nullString = "*";
    test(string, delimiter, nullString, expected, SortOrder.ASC, SortOrder.ASC, SortOrder.ASC,
      PVarchar.INSTANCE, PVarchar.INSTANCE, PVarchar.INSTANCE);
    test(string, delimiter, nullString, expected, SortOrder.DESC, SortOrder.ASC, SortOrder.ASC,
      PVarchar.INSTANCE, PVarchar.INSTANCE, PVarchar.INSTANCE);
  }

  @Test
  public void testStringToArrayFunction13() throws SQLException {
    String string = "a * b * c d e f";
    Object[] o1 = new Object[] { "a", null, "b", null, "c", "d", "e", "f" };
    PhoenixArray expected = new PhoenixArray(PVarchar.INSTANCE, o1);
    String delimiter = " ";
    String nullString = "*";
    test(string, delimiter, nullString, expected, SortOrder.ASC, SortOrder.ASC, SortOrder.ASC,
      PVarchar.INSTANCE, PVarchar.INSTANCE, PVarchar.INSTANCE);
    test(string, delimiter, nullString, expected, SortOrder.DESC, SortOrder.ASC, SortOrder.ASC,
      PVarchar.INSTANCE, PVarchar.INSTANCE, PVarchar.INSTANCE);
  }

  @Test
  public void testStringToArrayFunction14() throws SQLException {
    String string = "null a null";
    Object[] o1 = new Object[] { null, "a", null };
    PhoenixArray expected = new PhoenixArray(PVarchar.INSTANCE, o1);
    String delimiter = " ";
    String nullString = "null";
    test(string, delimiter, nullString, expected, SortOrder.ASC, SortOrder.ASC, SortOrder.ASC,
      PVarchar.INSTANCE, PVarchar.INSTANCE, PVarchar.INSTANCE);
    test(string, delimiter, nullString, expected, SortOrder.DESC, SortOrder.ASC, SortOrder.ASC,
      PVarchar.INSTANCE, PVarchar.INSTANCE, PVarchar.INSTANCE);
  }

  @Test
  public void testStringToArrayFunction16() throws SQLException {
    String string = "null a null";
    Object[] o1 = new Object[] { null, "a", null };
    PhoenixArray expected = new PhoenixArray(PVarchar.INSTANCE, o1);
    String delimiter = " ";
    String nullString = "null";
    test(string, delimiter, nullString, expected, SortOrder.ASC, SortOrder.ASC, SortOrder.ASC,
      PVarchar.INSTANCE, PVarchar.INSTANCE, PVarchar.INSTANCE);
    test(string, delimiter, nullString, expected, SortOrder.DESC, SortOrder.ASC, SortOrder.ASC,
      PVarchar.INSTANCE, PVarchar.INSTANCE, PVarchar.INSTANCE);
  }

  @Test
  public void testStringToArrayFunction17() throws SQLException {
    String string = "null a null";
    Object[] o1 = new Object[] { null, "a", null };
    PhoenixArray expected = new PhoenixArray(PVarchar.INSTANCE, o1);
    String delimiter = " ";
    String nullString = "null";
    test(string, delimiter, nullString, expected, SortOrder.ASC, SortOrder.ASC, SortOrder.ASC,
      PChar.INSTANCE, PVarchar.INSTANCE, PVarchar.INSTANCE);
    test(string, delimiter, nullString, expected, SortOrder.DESC, SortOrder.ASC, SortOrder.ASC,
      PChar.INSTANCE, PVarchar.INSTANCE, PVarchar.INSTANCE);
  }

  @Test
  public void testStringToArrayFunction18() throws SQLException {
    String string = "null,a,null";
    Object[] o1 = new Object[] { null, "a", null };
    PhoenixArray expected = new PhoenixArray(PVarchar.INSTANCE, o1);
    String delimiter = ",";
    String nullString = "null";
    test(string, delimiter, nullString, expected, SortOrder.ASC, SortOrder.ASC, SortOrder.ASC,
      PChar.INSTANCE, PChar.INSTANCE, PVarchar.INSTANCE);
    test(string, delimiter, nullString, expected, SortOrder.DESC, SortOrder.ASC, SortOrder.ASC,
      PChar.INSTANCE, PChar.INSTANCE, PVarchar.INSTANCE);
  }

  @Test
  public void testStringToArrayFunction19() throws SQLException {
    String string = "null,a,null";
    Object[] o1 = new Object[] { null, "a", null };
    PhoenixArray expected = new PhoenixArray(PVarchar.INSTANCE, o1);
    String delimiter = ",";
    String nullString = "null";
    test(string, delimiter, nullString, expected, SortOrder.ASC, SortOrder.ASC, SortOrder.ASC,
      PChar.INSTANCE, PChar.INSTANCE, PChar.INSTANCE);
    test(string, delimiter, nullString, expected, SortOrder.DESC, SortOrder.ASC, SortOrder.ASC,
      PChar.INSTANCE, PChar.INSTANCE, PChar.INSTANCE);
  }

  @Test
  public void testStringToArrayFunction20() throws SQLException {
    String string = "abc";
    Object[] o1 = new Object[] { "a", "b", "c" };
    PhoenixArray expected = new PhoenixArray(PVarchar.INSTANCE, o1);
    String delimiter = null;
    String nullString = "null";
    test(string, delimiter, nullString, expected, SortOrder.ASC, SortOrder.ASC, SortOrder.ASC,
      PChar.INSTANCE, PChar.INSTANCE, PChar.INSTANCE);
    test(string, delimiter, nullString, expected, SortOrder.DESC, SortOrder.ASC, SortOrder.ASC,
      PChar.INSTANCE, PChar.INSTANCE, PChar.INSTANCE);
  }

  @Test
  public void testStringToArrayFunction21() throws SQLException {
    String string = "(?!^)";
    Object[] o1 = new Object[] { "(", "?", "!", "^", ")" };
    PhoenixArray expected = new PhoenixArray(PVarchar.INSTANCE, o1);
    String delimiter = null;
    String nullString = null;
    test(string, delimiter, nullString, expected, SortOrder.ASC, SortOrder.ASC, SortOrder.ASC,
      PVarchar.INSTANCE, PVarchar.INSTANCE, PVarchar.INSTANCE);
    test(string, delimiter, nullString, expected, SortOrder.DESC, SortOrder.ASC, SortOrder.ASC,
      PVarchar.INSTANCE, PVarchar.INSTANCE, PVarchar.INSTANCE);
  }
}
