/*
 * Copyright 2016 Google Inc. All Rights Reserved.
 *
 * Licensed 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 com.google.devrel.gmscore.tools.apk.arsc;

import static com.google.common.truth.Truth.assertThat;
import static com.google.devrel.gmscore.tools.apk.arsc.BinaryResourceString.Type.UTF16;
import static com.google.devrel.gmscore.tools.apk.arsc.BinaryResourceString.Type.UTF8;

import com.google.common.base.Strings;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Arrays;

@RunWith(JUnit4.class)
public class BinaryResourceStringTest {

  /**
   * A special string chosen because it contains a different encoding length compared to its
   * character length.
   */
  private static final String TEST_STRING = "ābĉ123";

  /** Used to test length where length > 0x7F. */
  private static final String LENGTH_BYTE_STRING = Strings.repeat("a", 0xFF);

  /** Used to test length where length > 0x7FFF. */
  private static final String LENGTH_WORD_STRING = Strings.repeat("a", 0xFFFF);

  /** Contains the {@code TEST_STRING} in UTF-8 encoding. */
  private static final byte[] UTF8_STRING = {0x06, 0x08,  // 6 characters; 8 bytes
      (byte) 0xC4, (byte) 0x81, 0x62, (byte) 0xC4, (byte) 0x89, 0x31, 0x32, 0x33,  // ābĉ123
      0x00};  // Null-terminated

  /** Contains the {@code TEST_STRING} in UTF-16 encoding. */
  private static final byte[] UTF16_STRING = {0x06, 0x00,  // Length of string in little-endian
      0x01, 0x01, 0x62, 0x00, 0x09, 0x01, 0x31, 0x00, 0x32, 0x00, 0x33, 0x00,  // ābĉ123
      0x00, 0x00};  // Null-terminated

  /** Contains the two length prefixes for {@code LENGTH_BYTE_STRING} in UTF-8 encoding. */
  private static final byte[] UTF8_LENGTH_BYTE =
    {(byte) 0x80, (byte) 0xFF, (byte) 0x80, (byte) 0xFF};

  /** Contains the length prefix for {@code LENGTH_BYTE_STRING} in UTF-16 encoding. */
  private static final byte[] UTF16_LENGTH_BYTE = {(byte) 0xFF, 0x00};

  /** Contains the length prefix for {@code LENGTH_BYTE_WORD} in UTF-16 encoding. */
  private static final byte[] UTF16_LENGTH_WORD = {0x00, (byte) 0x80, (byte) 0xFF, (byte) 0xFF};

  @Test
  public void decodeUtf8String() {
    ByteBuffer utf8 = ByteBuffer.wrap(UTF8_STRING).order(ByteOrder.LITTLE_ENDIAN);
    assertThat(BinaryResourceString.decodeString(utf8, 0, UTF8)).isEqualTo(TEST_STRING);
  }

  @Test
  public void decodeUtf16String() {
    ByteBuffer utf16 = ByteBuffer.wrap(UTF16_STRING).order(ByteOrder.LITTLE_ENDIAN);
    assertThat(BinaryResourceString.decodeString(utf16, 0, UTF16)).isEqualTo(TEST_STRING);
  }

  @Test
  public void encodeUtf8String() {
    byte[] utf8 = BinaryResourceString.encodeString(TEST_STRING, UTF8);
    assertThat(utf8).isEqualTo(UTF8_STRING);
  }

  @Test
  public void encodeUtf16String() {
    byte[] utf16 = BinaryResourceString.encodeString(TEST_STRING, UTF16);
    assertThat(utf16).isEqualTo(UTF16_STRING);
  }

  @Test
  public void parseLargeUtf8String() {
    parseString(LENGTH_BYTE_STRING, UTF8, UTF8_LENGTH_BYTE);
  }

  @Test
  public void parseMediumUtf16String() {
    parseString(LENGTH_BYTE_STRING, UTF16, UTF16_LENGTH_BYTE);
  }

  @Test
  public void parseLargeUtf16String() {
    parseString(LENGTH_WORD_STRING, UTF16, UTF16_LENGTH_WORD);
  }

  private void parseString(String str, BinaryResourceString.Type type, byte[] prefix) {
    byte[] utf = BinaryResourceString.encodeString(str, type);
    byte[] utfPrefix = Arrays.copyOf(utf, prefix.length);
    assertThat(utfPrefix).isEqualTo(prefix);
    ByteBuffer buffer = ByteBuffer.wrap(utf).order(ByteOrder.LITTLE_ENDIAN);
    assertThat(BinaryResourceString.decodeString(buffer, 0, type)).isEqualTo(str);
  }
}
