/*
 * 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.lucene.codecs.simpletext;

import java.io.IOException;
import java.util.Locale;
import org.apache.lucene.index.CorruptIndexException;
import org.apache.lucene.store.ChecksumIndexInput;
import org.apache.lucene.store.DataInput;
import org.apache.lucene.store.DataOutput;
import org.apache.lucene.store.IndexOutput;
import org.apache.lucene.util.BytesRef;
import org.apache.lucene.util.BytesRefBuilder;
import org.apache.lucene.util.StringHelper;

class SimpleTextUtil {
  public static final byte NEWLINE = 10;
  public static final byte ESCAPE = 92;
  static final BytesRef CHECKSUM = new BytesRef("checksum ");

  public static void write(DataOutput out, String s, BytesRefBuilder scratch) throws IOException {
    scratch.copyChars(s, 0, s.length());
    write(out, scratch.get());
  }

  public static void write(DataOutput out, BytesRef b) throws IOException {
    for (int i = 0; i < b.length; i++) {
      final byte bx = b.bytes[b.offset + i];
      if (bx == NEWLINE || bx == ESCAPE) {
        out.writeByte(ESCAPE);
      }
      out.writeByte(bx);
    }
  }

  public static void writeNewline(DataOutput out) throws IOException {
    out.writeByte(NEWLINE);
  }

  public static void readLine(DataInput in, BytesRefBuilder scratch) throws IOException {
    int upto = 0;
    while (true) {
      byte b = in.readByte();
      scratch.grow(1 + upto);
      if (b == ESCAPE) {
        scratch.setByteAt(upto++, in.readByte());
      } else {
        if (b == NEWLINE) {
          break;
        } else {
          scratch.setByteAt(upto++, b);
        }
      }
    }
    scratch.setLength(upto);
  }

  public static void writeChecksum(IndexOutput out, BytesRefBuilder scratch) throws IOException {
    // Pad with zeros so different checksum values use the
    // same number of bytes
    // (BaseIndexFileFormatTestCase.testMergeStability cares):
    String checksum = String.format(Locale.ROOT, "%020d", out.getChecksum());
    write(out, CHECKSUM);
    write(out, checksum, scratch);
    writeNewline(out);
  }

  public static void checkFooter(ChecksumIndexInput input) throws IOException {
    BytesRefBuilder scratch = new BytesRefBuilder();
    String expectedChecksum = String.format(Locale.ROOT, "%020d", input.getChecksum());
    readLine(input, scratch);
    if (StringHelper.startsWith(scratch.get(), CHECKSUM) == false) {
      throw new CorruptIndexException(
          "SimpleText failure: expected checksum line but got " + scratch.get().utf8ToString(),
          input);
    }
    String actualChecksum =
        new BytesRef(scratch.bytes(), CHECKSUM.length, scratch.length() - CHECKSUM.length)
            .utf8ToString();
    if (!expectedChecksum.equals(actualChecksum)) {
      throw new CorruptIndexException(
          "SimpleText checksum failure: " + actualChecksum + " != " + expectedChecksum, input);
    }
    if (input.length() != input.getFilePointer()) {
      throw new CorruptIndexException(
          "Unexpected stuff at the end of file, please be careful with your text editor!", input);
    }
  }

  /** Inverse of {@link BytesRef#toString}. */
  public static BytesRef fromBytesRefString(String s) {
    if (s.length() < 2) {
      throw new IllegalArgumentException(
          "string " + s + " was not created from BytesRef.toString?");
    }
    if (s.charAt(0) != '[' || s.charAt(s.length() - 1) != ']') {
      throw new IllegalArgumentException(
          "string " + s + " was not created from BytesRef.toString?");
    }
    if (s.length() == 2) {
      return new BytesRef(BytesRef.EMPTY_BYTES);
    }
    String[] parts = s.substring(1, s.length() - 1).split(" ");
    byte[] bytes = new byte[parts.length];
    for (int i = 0; i < parts.length; i++) {
      bytes[i] = (byte) Integer.parseInt(parts[i], 16);
    }

    return new BytesRef(bytes);
  }
}
