/*
 * Copyright 2012 Last.fm
 *
 *  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 fm.last.moji.impl;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.util.concurrent.locks.Lock;

import org.apache.commons.io.IOUtils;
import org.apache.commons.io.output.CountingOutputStream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import fm.last.moji.tracker.Destination;
import fm.last.moji.tracker.Tracker;
import fm.last.moji.tracker.TrackerFactory;

class FileUploadOutputStream extends OutputStream {

  private static final Logger log = LoggerFactory.getLogger(FileUploadOutputStream.class);

  private static final int CHUNK_LENGTH = 4096;
  private final Destination destination;
  private final TrackerFactory trackerFactory;
  private final String key;
  private final String domain;
  private final Lock writeLock;
  private final HttpURLConnection httpConnection;
  private final CountingOutputStream delegate;

  FileUploadOutputStream(TrackerFactory trackerFactory, HttpConnectionFactory httpFactory, String key, String domain,
      Destination destination, Lock writeLock) throws IOException {
    this.destination = destination;
    this.trackerFactory = trackerFactory;
    this.domain = domain;
    this.key = key;
    this.writeLock = writeLock;

    log.debug("HTTP PUT -> opening chunked stream -> {}", destination.getPath());
    httpConnection = httpFactory.newConnection(destination.getPath());
    httpConnection.setRequestMethod("PUT");
    httpConnection.setChunkedStreamingMode(CHUNK_LENGTH);
    httpConnection.setDoOutput(true);
    delegate = new CountingOutputStream(httpConnection.getOutputStream());
  }

  @Override
  public void write(int b) throws IOException {
    delegate.write(b);
  }

  @Override
  public void write(byte[] b) throws IOException {
    delegate.write(b);
  }

  @Override
  public void write(byte[] b, int off, int len) throws IOException {
    delegate.write(b, off, len);
  }

  @Override
  public void flush() throws IOException {
    delegate.flush();
  }

  @Override
  public void close() throws IOException {
    log.debug("Close called on {}", this);
    long size = -1L;
    try {
      try {
        delegate.flush();
        size = delegate.getByteCount();
      } finally {
        try {
          delegate.close();
        } finally {
          try {
            int code = httpConnection.getResponseCode();
            if (HttpURLConnection.HTTP_OK != code && HttpURLConnection.HTTP_CREATED != code) {
              InputStream is;
              if (200 <= httpConnection.getResponseCode() && httpConnection.getResponseCode() <= 299) {
                is = httpConnection.getInputStream();
              } else {
                is = httpConnection.getErrorStream();
              }
              String body = IOUtils.toString(is);
              String message = httpConnection.getResponseMessage();
              throw new IOException(code + " " + message + " peer: '{" + httpConnection + "}'" + " body: '{" + body + "}'");
            }
          } finally {
            httpConnection.disconnect();
          }
        }
      }
    } finally {
      log.debug("Bytes written: {}", size);
      Tracker tracker = trackerFactory.getTracker();
      try {
        tracker.createClose(key, domain, destination, size);
      } finally {
        try {
          tracker.close();
        } finally {
          unlockQuietly(writeLock);
        }
      }
    }
  }

  @Override
  public String toString() {
    StringBuilder builder = new StringBuilder();
    builder.append("FileUploadOutputStream [domain=");
    builder.append(domain);
    builder.append(", key=");
    builder.append(key);
    builder.append(", destination=");
    builder.append(destination);
    builder.append("]");
    return builder.toString();
  }

  private void unlockQuietly(Lock lock) {
    try {
      lock.unlock();
    } catch (IllegalMonitorStateException e) {
    }
  }

}

@@ -3084,339 +3084,8 @@
 ) %7B%0A
-              InputStream is;%0A              if (200 %3C= httpConnection.getResponseCode() && httpConnection.getResponseCode() %3C= 299) %7B%0A                is = httpConnection.getInputStream();%0A              %7D else %7B%0A                is = httpConnection.getErrorStream();%0A              %7D%0A              String body = IOUtils.toString(is);%0A
     
@@ -3208,16 +3208,17 @@
 sage + %22
+,
  peer: '
@@ -3242,36 +3242,8 @@
 on +
- %22%7D'%22 + %22 body: '%7B%22 + body +
  %22%7D'

Improve closing upload stream error info