package com.tibbo.aggregate.common.expression.function;

import static org.junit.jupiter.api.Assertions.assertAll;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertThrows;

import java.util.Date;

import org.junit.jupiter.api.Test;

import com.tibbo.aggregate.common.data.Data;
import com.tibbo.aggregate.common.expression.EvaluationException;
import com.tibbo.aggregate.common.expression.function.datablock.ByteAtFunction;
import com.tibbo.aggregate.common.expression.function.datablock.BytesFunction;
import com.tibbo.aggregate.common.expression.function.datablock.CopyBytesFunction;
import com.tibbo.aggregate.common.expression.function.string.DataBlockFunction;
import com.tibbo.aggregate.common.expression.function.type.DoubleFunction;
import com.tibbo.aggregate.common.expression.function.type.FloatFunction;
import com.tibbo.aggregate.common.expression.function.type.IntegerFunction;
import com.tibbo.aggregate.common.expression.function.type.LongFunction;
import com.tibbo.aggregate.common.expression.function.type.TimestampFunction;

public class TestDataBlockFunctions
{
  
  private static final long RELEASE_DATE = 1630443600000L;
  
  @Test
  public void testDataBlockFromNumberFunction() throws Exception
  {
    Data execute = (Data) new DataBlockFunction().execute(null, null, Integer.MAX_VALUE);
    assertEquals(Integer.MAX_VALUE, (int) new IntegerFunction().execute(null, null, execute));
    
    execute = (Data) new DataBlockFunction().execute(null, null, Long.MAX_VALUE);
    assertEquals(Long.MAX_VALUE, (long) new LongFunction().execute(null, null, execute));
    
    execute = (Data) new DataBlockFunction().execute(null, null, Double.MAX_VALUE);
    assertEquals(Double.MAX_VALUE, (double) new DoubleFunction().execute(null, null, execute));
    
    execute = (Data) new DataBlockFunction().execute(null, null, Float.MAX_VALUE);
    assertEquals(Float.MAX_VALUE, (float) new FloatFunction().execute(null, null, execute));
    
    Date date = (Date) new TimestampFunction().convert(RELEASE_DATE);
    execute = (Data) new DataBlockFunction().execute(null, null, date);
    assertEquals(RELEASE_DATE, ((Date) new TimestampFunction().execute(null, null, execute)).getTime());
  }
  
  @Test
  public void testByteAtFunction() throws Exception
  {
    Data data = new Data();
    data.setData(new byte[] { 0, 2, 4, 8, 16, 32 });
    assertAll(() -> {
      assertEquals(0, new ByteAtFunction().execute(null, null, data, 0));
      assertEquals(32, new ByteAtFunction().execute(null, null, data, 5));
      assertThrows(EvaluationException.class, () -> new ByteAtFunction().execute(null, null, data, 6));
      assertThrows(EvaluationException.class, () -> new ByteAtFunction().execute(null, null, data, -1));
    });
  }
  
  @Test
  public void testBytesFunction() throws Exception
  {
    Data data = new Data(new byte[] { 0, 2, 4, 8, 16, 32 });
    assertAll(() -> {
      assertArrayEquals(new byte[] { 2, 4, 8, 16, 32 }, ((Data) new BytesFunction().execute(null, null, data, 1)).getData());
      assertArrayEquals(new byte[] { 2, 4 }, ((Data) new BytesFunction().execute(null, null, data, 1, 3)).getData());
      assertArrayEquals(new byte[] { 2, 4, 8, 16, 32 }, ((Data) new BytesFunction().execute(null, null, data, 1, 6)).getData());
      assertThrows(EvaluationException.class, () -> new BytesFunction().execute(null, null, data, 6));
      assertThrows(EvaluationException.class, () -> new BytesFunction().execute(null, null, data, 0, 7));
      assertThrows(EvaluationException.class, () -> new BytesFunction().execute(null, null, data, -1));
      assertThrows(EvaluationException.class, () -> new BytesFunction().execute(null, null, data, 0, -1));
    });
  }
  
  @Test
  public void testCopyBytesFunction() throws Exception
  {
    Data source = new Data(new byte[] { 0, 2, 4, 8, 16, 32 });
    Data target = new Data(new byte[] { 0, 0, 0, 0, 0, 0 });
    Data returnedValue = (Data) new CopyBytesFunction().execute(null, null, source, 1, target, 2, 3);
    assertAll(() -> {
      assertSame(target, returnedValue);
      assertArrayEquals(new byte[] { 0, 0, 2, 4, 8, 0  }, target.getData());
      assertThrows(EvaluationException.class, () -> new CopyBytesFunction().execute(null, null, source, -1, target, 2, 3));
      assertThrows(EvaluationException.class, () -> new CopyBytesFunction().execute(null, null, source, 1, target, -1, 3));
      assertThrows(EvaluationException.class, () -> new CopyBytesFunction().execute(null, null, source, 1, target, 2, -1));
      assertThrows(EvaluationException.class, () -> new CopyBytesFunction().execute(null, null, source, 1, target, 2, 8));
    });
  }
}
