/*
 * Copyright 2013 the original author or authors.
 *
 * 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 org.spockframework.util;

import groovy.lang.MissingMethodException;
import org.spockframework.runtime.GroovyRuntimeUtil;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.util.List;
import java.util.Locale;

public class TeePrintStream extends PrintStream {
  private final List<PrintStream> delegates;

  public TeePrintStream(List<PrintStream> delegates) {
    super(new ByteArrayOutputStream(0));
    this.delegates = delegates;
  }

  public List<PrintStream> getDelegates() {
    return delegates;
  }

  @Override
  public void flush() {
    for (PrintStream stream : delegates) {
      stream.flush();
    }
  }

  @Override
  public void close() {
    for (PrintStream stream : delegates) {
      stream.close();
    }
  }

  @Override
  public boolean checkError() {
    for (PrintStream stream : delegates) {
      if (stream.checkError()) return true;
    }
    return false;
  }

  @Override
  protected void setError() {
    throw new UnsupportedOperationException("setError");
  }

  @Override
  protected void clearError() {
    for (PrintStream stream : delegates) {
      try {
        GroovyRuntimeUtil.invokeMethod(stream, "clearError");
      } catch (MissingMethodException e) {
        // method doesn't exist in JDK 1.5
        if (!e.getMethod().equals("clearError")) {
          throw e;
        }
      }
    }
  }

  @Override
  public void write(int b) {
    for (PrintStream stream : delegates) {
      stream.write(b);
    }
  }

  @Override
  public void write(byte[] buf, int off, int len) {
    for (PrintStream stream : delegates) {
      stream.write(buf, off, len);
    }
  }

  @Override
  public void print(boolean b) {
    for (PrintStream stream : delegates) {
      stream.print(b);
    }
  }

  @Override
  public void print(char c) {
    for (PrintStream stream : delegates) {
      stream.print(c);
    }
  }

  @Override
  public void print(int i) {
    for (PrintStream stream : delegates) {
      stream.print(i);
    }
  }

  @Override
  public void print(long l) {
    for (PrintStream stream : delegates) {
      stream.print(l);
    }
  }

  @Override
  public void print(float f) {
    for (PrintStream stream : delegates) {
      stream.print(f);
    }
  }

  @Override
  public void print(double d) {
    for (PrintStream stream : delegates) {
      stream.print(d);
    }
  }

  @Override
  public void print(char[] s) {
    for (PrintStream stream : delegates) {
      stream.print(s);
    }
  }

  @Override
  public void print(String s) {
    for (PrintStream stream : delegates) {
      stream.print(s);
    }
  }

  @Override
  public void print(Object obj) {
    for (PrintStream stream : delegates) {
      stream.print(obj);
    }
  }

  @Override
  public void println() {
    for (PrintStream stream : delegates) {
      stream.println();
    }
  }

  @Override
  public void println(boolean x) {
    for (PrintStream stream : delegates) {
      stream.println(x);
    }
  }

  @Override
  public void println(char x) {
    for (PrintStream stream : delegates) {
      stream.println(x);
    }
  }

  @Override
  public void println(int x) {
    for (PrintStream stream : delegates) {
      stream.println(x);
    }
  }

  @Override
  public void println(long x) {
    for (PrintStream stream : delegates) {
      stream.println(x);
    }
  }

  @Override
  public void println(float x) {
    for (PrintStream stream : delegates) {
      stream.println(x);
    }
  }

  @Override
  public void println(double x) {
    for (PrintStream stream : delegates) {
      stream.println(x);
    }
  }

  @Override
  public void println(char[] x) {
    for (PrintStream stream : delegates) {
      stream.println(x);
    }
  }

  @Override
  public void println(String x) {
    for (PrintStream stream : delegates) {
      stream.println(x);
    }
  }

  @Override
  public void println(Object x) {
    for (PrintStream stream : delegates) {
      stream.println(x);
    }
  }

  @Override
  public PrintStream printf(String format, Object... args) {
    for (PrintStream stream : delegates) {
      stream.printf(format, args);
    }
    return this;
  }

  @Override
  public PrintStream printf(Locale l, String format, Object... args) {
    for (PrintStream stream : delegates) {
      stream.printf(l, format, args);
    }
    return this;
  }

  @Override
  public PrintStream format(String format, Object... args) {
    for (PrintStream stream : delegates) {
      stream.printf(format, args);
    }
    return this;
  }

  @Override
  public PrintStream format(Locale l, String format, Object... args) {
    for (PrintStream stream : delegates) {
      stream.printf(format, args);
    }
    return this;
  }

  @Override
  public PrintStream append(CharSequence csq) {
    for (PrintStream stream : delegates) {
      stream.append(csq);
    }
    return this;
  }

  @Override
  public PrintStream append(CharSequence csq, int start, int end) {
    for (PrintStream stream : delegates) {
      stream.append(csq, start, end);
    }
    return this;
  }

  @Override
  public PrintStream append(char c) {
    for (PrintStream stream : delegates) {
      stream.append(c);
    }
    return this;
  }

  @Override
  public void write(byte[] b) throws IOException {
    for (PrintStream stream : delegates) {
      stream.write(b);
    }
  }
}

@@ -641,105 +641,8 @@
 l;%0A%0A
-import groovy.lang.MissingMethodException;%0Aimport org.spockframework.runtime.GroovyRuntimeUtil;%0A%0A
 impo
@@ -723,32 +723,57 @@
 io.PrintStream;%0A
+import java.util.Arrays;%0A
 import java.util
@@ -804,16 +804,176 @@
 Locale;%0A
+import java.util.concurrent.CopyOnWriteArrayList;%0A%0Aimport groovy.lang.MissingMethodException;%0A%0Aimport org.spockframework.runtime.GroovyRuntimeUtil;%0A%0A@ThreadSafe
 %0Apublic 
@@ -1061,16 +1061,109 @@
 gates;%0A%0A
+  public TeePrintStream(PrintStream... delegates) %7B%0A    this(Arrays.asList(delegates));%0A  %7D%0A%0A
   public
@@ -1271,16 +1271,54 @@
 gates = 
+new CopyOnWriteArrayList%3CPrintStream%3E(
 delegate
@@ -1310,32 +1310,33 @@
 tream%3E(delegates
+)
 ;%0A  %7D%0A%0A  public 

make TeePrintStream thread-safe