/**
 * This software is licensed to you under the Apache License, Version 2.0 (the
 * "Apache License"). 
 *
 * LinkedIn's contributions are made under the Apache License.  If you contribute
 * to the Software, the contributions will be deemed to have been made under the
 * Apache License, unless you expressly indicate otherwise.  Please do not make any
 * contributions that would be inconsistent with the Apache License. 
 *
 * You may obtain a copy of the Apache License at http://www.apache.org/licenses/LICENSE-2.0
 * Unless required by applicable law or agreed to in writing, this software
 * distributed under the Apache License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the Apache
 * License for the specific language governing permissions and limitations for the
 * software governed under the Apache License. 
 *
 * © 2012 LinkedIn Corp. All Rights Reserved. 
 */
package com.senseidb.search.req;


import com.linkedin.norbert.network. Serializer;
import it.unimi.dsi.fastutil.ints. IntArrayList;
import it.unimi.dsi.fastutil.ints. IntList;
import org.apache.commons.io. IOUtils;
import org.apache.log4j. Logger;
import org.iq80.snappy. SnappyInputStream;
import org.iq80.snappy. SnappyOutputStream;

import java.io. ByteArrayInputStream;
import java.io. ByteArrayOutputStream;
import java.io. IOException;
import java.util. Arrays;


/**
 * Takes any Serializer and compresses/decompresses the data. 
 */
public class SenseiSnappySerializer<RequestType, ResponseType> implements Serializer<RequestType, ResponseType> {
  private final static Logger logger = Logger.getLogger(SenseiSnappySerializer.class);

  private final Serializer<RequestType, ResponseType> _inner;
  private final String requestName;
  private final String responseName;

  public SenseiSnappySerializer(Serializer<RequestType, ResponseType> inner) {
    _inner = inner;
    requestName = "SnappyRequest(" + (inner.requestName() == null ? "" :  inner.requestName()) + ")";
    responseName = "SnappyResponse(" + (inner.responseName() == null ? "" :  inner.responseName())  + ")";
  }

  public static <RequestType, ResponseType> SenseiSnappySerializer<RequestType, ResponseType> wrap(Serializer<RequestType, ResponseType> inner) {
    return new SenseiSnappySerializer<RequestType, ResponseType>(inner);
  }

  @Override
  public String requestName() {
    return requestName;
  }

  @Override
  public String responseName() {
    return responseName;
  }

  @Override
  public RequestType requestFromBytes(byte[] compressedBytes) {
    ByteArrayInputStream bais = new ByteArrayInputStream(compressedBytes);

    byte[] uncompressedBytes = null;
    try {
      SnappyInputStream snappyInputStream = new SnappyInputStream(bais);
      uncompressedBytes = IOUtils.toByteArray(snappyInputStream);
    } catch (IOException e) {
      // This should not happen
      logger.warn("Could not decompress sensei request", e);
    }

    RequestType request = _inner.requestFromBytes(uncompressedBytes);

//    // For debugging serialization
//    byte[] bytes2 = _inner.requestToBytes(request);
//    RequestType request2 = _inner.requestFromBytes(bytes2);
//    if(!request.equals(request2)) {
//      throw new IllegalArgumentException();
//    }

    return request;
  }

  @Override
  public ResponseType responseFromBytes(byte[] compressedBytes) {
    ByteArrayInputStream bais = new ByteArrayInputStream(compressedBytes);

    byte[] uncompressedBytes = null;
    try {
      SnappyInputStream snappyInputStream = new SnappyInputStream(bais);
      uncompressedBytes = IOUtils.toByteArray(snappyInputStream);
    } catch (IOException e) {
      // This should not happen
      logger.warn("Could not decompress sensei request", e);
    }

    ResponseType response = _inner.responseFromBytes(uncompressedBytes);

//    // For debugging serialization
//    byte[] bytes = _inner.responseToBytes(response);
//    ResponseType response2 = _inner.responseFromBytes(bytes);
//    if(!response.equals(response2)) {
//      throw new IllegalArgumentException();
//    }

    return response;
  }

  @Override
  public byte[] requestToBytes(RequestType request) {
    byte[] uncompressedBytes = _inner.requestToBytes(request);

//    // For debugging serialization
//    RequestType request2 = _inner.requestFromBytes(uncompressedBytes);
//    if(!request.equals(request2)) {
//      throw new IllegalArgumentException();
//    }

    ByteArrayOutputStream baos = new ByteArrayOutputStream();

    try {
      SnappyOutputStream snappyOutputStream = new SnappyOutputStream(baos);
      snappyOutputStream.write(uncompressedBytes);
      snappyOutputStream.close();
      baos.close();
    } catch (IOException e) {
      // This should not happen
      logger.error("Could not compress sensei request ", e);
    }

    return baos.toByteArray();
  }

  @Override
  public byte[] responseToBytes(ResponseType response) {
    byte[] uncompressedBytes = _inner.responseToBytes(response);

//    // For debugging serialization
//    ResponseType response2 = _inner.responseFromBytes(uncompressedBytes);
//    if(!response.equals(response2)) {
//      throw new IllegalArgumentException();
//    }

    ByteArrayOutputStream baos = new ByteArrayOutputStream();

    try {
      SnappyOutputStream snappyOutputStream = new SnappyOutputStream(baos);
      snappyOutputStream.write(uncompressedBytes);
      snappyOutputStream.close();
      baos.close();
    } catch (IOException e) {
      // This should not happen
      logger.error("Could not compress sensei request ", e);
    }
    return baos.toByteArray();
  }
}
