package com.github.mustachejava.util;

import com.github.mustachejava.MustacheException;

import java.io.IOException;
import java.io.Writer;
import java.util.concurrent.CountDownLatch;

/**
 * Buffer content while a future is being evaluated in another thread.
 */
public class LatchedWriter extends Writer {

  // Write to the buffer while latched, when unlatched, write the
  // buffer to the underlying writer and any future writes
  private final CountDownLatch latch = new CountDownLatch(1);

  // A buffer that holds writes until the latch is unlatched
  private final StringBuilder buffer = new StringBuilder();

  // The underlying writer
  private final Writer writer;

  // This is set when the latch holder fails
  private volatile Throwable e;

  public LatchedWriter(Writer writer) {
    this.writer = writer;
  }

  // Call this when your processing is complete
  public synchronized void done() throws IOException {
    writer.append(buffer);
    latch.countDown();
  }

  // If you fail to complete, put an exception here
  public void failed(Throwable e) {
    this.e = e;
    latch.countDown();
  }

  @Override
  public synchronized void write(char[] cbuf, int off, int len) throws IOException {
    checkException();
    if (latch.getCount() == 0) {
      writer.write(cbuf, off, len);
    } else {
      buffer.append(cbuf, off, len);
    }
  }

  private void checkException() throws IOException {
    if (e != null) {
      if (e instanceof IOException) {
        throw ((IOException) e);
      }
      throw new IOException(e);
    }
  }

  @Override
  public void flush() throws IOException {
    checkException();
    if (latch.getCount() == 0) {
      synchronized (this) {
        writer.flush();
      }
    }
  }

  @Override
  public void close() throws IOException {
    checkException();
    await();
    flush();
    writer.close();
  }

  public void await() {
    try {
      latch.await();
    } catch (InterruptedException e1) {
      throw new MustacheException("Interrupted while waiting for completion", e1);
    }
  }

}
