/*
 * 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.geode.internal.cache.tier.sockets;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.UTFDataFormatException;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;

import org.apache.geode.cache.server.CacheServer;
import org.apache.geode.internal.HeapDataOutputStream;
import org.apache.geode.internal.cache.CacheServerImpl;
import org.apache.geode.internal.serialization.KnownVersion;
import org.apache.geode.internal.util.BlobHelper;

/**
 * <code>CacheServerHelper</code> is a static class that provides helper methods for the CacheServer
 * classes.
 *
 * @since GemFire 3.5
 */
public class CacheServerHelper {

  public static void setIsDefaultServer(CacheServer server) {
    if (server instanceof CacheServerImpl) {
      ((CacheServerImpl) server).setIsDefaultServer();
    }
  }

  public static boolean isDefaultServer(CacheServer server) {
    if (!(server instanceof CacheServerImpl)) {
      return false;
    }
    return ((CacheServerImpl) server).isDefaultServer();
  }

  public static byte[] serialize(Object obj) throws IOException {
    return serialize(obj, false);
  }

  public static byte[] serialize(Object obj, boolean zipObject) throws IOException {
    return zipObject ? zip(obj) : BlobHelper.serializeToBlob(obj);
  }

  public static Object deserialize(byte[] blob) throws IOException, ClassNotFoundException {
    return deserialize(blob, false);
  }

  public static Object deserialize(byte[] blob, boolean unzipObject)
      throws IOException, ClassNotFoundException {
    return unzipObject ? unzip(blob) : BlobHelper.deserializeBlob(blob);
  }

  public static Object deserialize(byte[] blob, KnownVersion version, boolean unzipObject)
      throws IOException, ClassNotFoundException {
    return unzipObject ? unzip(blob) : BlobHelper.deserializeBlob(blob, version, null);
  }

  public static byte[] zip(Object obj) throws IOException {
    // logger.info("CacheServerHelper: Zipping object to blob: " + obj);
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    GZIPOutputStream gz = new GZIPOutputStream(baos);
    ObjectOutputStream oos = new ObjectOutputStream(gz);
    oos.writeObject(obj);
    oos.flush();
    oos.close();
    byte[] blob = baos.toByteArray();
    // logger.info("CacheServerHelper: Zipped object to blob: " + blob);
    return blob;
  }

  public static Object unzip(byte[] blob) throws IOException, ClassNotFoundException {
    // logger.info("CacheServerHelper: Unzipping blob to object: " + blob);
    ByteArrayInputStream bais = new ByteArrayInputStream(blob);
    GZIPInputStream gs = new GZIPInputStream(bais);
    ObjectInputStream ois = new ObjectInputStream(gs);
    Object obj = ois.readObject();
    // logger.info("CacheServerHelper: Unzipped blob to object: " + obj);
    ois.close();
    bais.close();
    return obj;
  }


  /**
   * The logic used here is based on java's DataInputStream.writeUTF() from the version 1.6.0_10.
   *
   * @return byte[]
   */
  public static byte[] toUTF(String s) {
    HeapDataOutputStream hdos = new HeapDataOutputStream(s);
    return hdos.toByteArray();
  }

  /**
   * The logic used here is based on java's DataInputStream.readUTF() from the version 1.6.0_10.
   *
   */
  public static String fromUTF(byte[] bytearr) {
    int utflen = bytearr.length;
    int c, char2, char3;
    int count = 0;
    int chararr_count = 0;

    char[] chararr = new char[utflen];

    while (count < utflen) {
      c = (int) bytearr[count] & 0xff;
      if (c > 127) {
        break;
      }
      count++;
      chararr[chararr_count++] = (char) c;
    }

    while (count < utflen) {
      c = (int) bytearr[count] & 0xff;
      switch (c >> 4) {
        case 0:
        case 1:
        case 2:
        case 3:
        case 4:
        case 5:
        case 6:
        case 7:
          /* 0xxxxxxx */
          count++;
          chararr[chararr_count++] = (char) c;
          break;
        case 12:
        case 13:
          /* 110x xxxx 10xx xxxx */
          count += 2;
          if (count > utflen) {
            throw new RuntimeException(
                "UTF-8 Exception malformed input",
                new UTFDataFormatException("malformed input: partial character at end"));
          }
          char2 = bytearr[count - 1];
          if ((char2 & 0xC0) != 0x80) {
            throw new RuntimeException("malformed input around byte " + count);
          }
          chararr[chararr_count++] = (char) (((c & 0x1F) << 6) | (char2 & 0x3F));
          break;
        case 14:
          /* 1110 xxxx 10xx xxxx 10xx xxxx */
          count += 3;
          if (count > utflen) {
            throw new RuntimeException(
                "UTF-8 Exception malformed input",
                new UTFDataFormatException("malformed input: partial character at end"));
          }
          char2 = bytearr[count - 2];
          char3 = bytearr[count - 1];
          if (((char2 & 0xC0) != 0x80) || ((char3 & 0xC0) != 0x80)) {
            throw new RuntimeException(
                "UTF-8 Exception malformed input",
                new UTFDataFormatException("malformed input around byte " + (count - 1)));
          }
          chararr[chararr_count++] =
              (char) (((c & 0x0F) << 12) | ((char2 & 0x3F) << 6) | ((char3 & 0x3F) << 0));
          break;
        default:
          /* 10xx xxxx, 1111 xxxx */
          throw new RuntimeException(
              "UTF-8 Exception malformed input",
              new UTFDataFormatException("malformed input around byte " + count));
      }
    }
    // The number of chars produced may be less than utflen
    return new String(chararr, 0, chararr_count);
  }

  private CacheServerHelper() {}
}
