package online.filter.helper;

import java.io.CharArrayWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.util.Arrays;
import java.util.Objects;

import javax.servlet.ServletOutputStream;
import javax.servlet.WriteListener;
import javax.servlet.http.HttpServletResponse;

import org.apache.logging.log4j.LogManager;

/**
 * レスポンス複製
 *
 * @author Tadashi Nakayama
 */
public class DuplicateBodyResponse implements Serializable {
	/** serialVersionUID */
	private static final long serialVersionUID = 1L;

	/** レスポンスオブジェクト複製クラス */
	private final DuplicateHeaderResponse dhr;

	/** キャラクタ配列ライタ */
	private DuplicateCharArrayWriter charWriter = null;
	/** 出力ストリーム */
	private DuplicateServletOutputStream dow = null;

	/** リクエストURL */
	private String requestUrl = null;
	/** トークン */
	private String token = null;

	/**
	 * コンストラクタ
	 *
	 * @param header DuplicateHeaderResponse
	 */
	public DuplicateBodyResponse(final DuplicateHeaderResponse header) {
		this.dhr = header;
	}

	/**
	 * RequestUrl取得
	 *
	 * @return the requestUrl
	 */
	public String getRequestUrl() {
		return this.requestUrl;
	}

	/**
	 * RequestUrl設定
	 *
	 * @param val the requestUrl to set
	 */
	public void setRequestUrl(final String val) {
		this.requestUrl = val;
	}

	/**
	 * Token取得
	 *
	 * @return the token;
	 */
	public String getToken() {
		return this.token;
	}

	/**
	 * Token設定
	 *
	 * @param val token
	 */
	public void setToken(final String val) {
		this.token = val;
	}

	/**
	 * ヘッダ取得
	 *
	 * @return ヘッダ
	 */
	public DuplicateHeaderResponse getHeader() {
		return this.dhr;
	}

	/**
	 * 出力ストリーム取得
	 *
	 * @return 出力ストリーム
	 */
	public ServletOutputStream getOutputStream() {
		if (this.charWriter != null) {
			throw new java.lang.IllegalStateException(
					"getWriter() has already been called for this object");
		}

		if (this.dow == null) {
			this.dow = new DuplicateServletOutputStream(this.dhr.getCharacterEncoding());
		}

		return this.dow;
	}

	/**
	 * ライタ取得
	 *
	 * @return ライタオブジェクト
	 */
	public PrintWriter getWriter() {
		if (this.dow != null) {
			throw new java.lang.IllegalStateException(
				"getOutputStream() has already been called for this object");
		}

		if (this.charWriter == null) {
			this.charWriter = new DuplicateCharArrayWriter();
		}

		return new PrintWriter(this.charWriter);
	}

	/**
	 * リセットバッファ
	 */
	public void resetBuffer() {
		this.charWriter = null;
		this.dow = null;
	}

	/**
	 * @see java.lang.Object#toString()
	 */
	@Override
	public String toString() {
		return Objects.toString(this.charWriter, "");
	}

	/**
	 * レスポンス複製処理
	 *
	 * @param response 複製対象レスポンス
	 */
	public void copyResponse(final HttpServletResponse response) {
		if (response != null) {
			this.dhr.copyResponse(response);

			try {
				// キャラクタ配列ライタ
				if (this.charWriter != null) {
					response.getWriter().write(this.charWriter.toString());
				}
				// 出力ストリーム
				if (this.dow != null) {
					response.getOutputStream().print(this.dow.toString());
				}
			} catch (final IOException ex) {
				LogManager.getLogger().info(ex.getMessage());
			}
		}
	}

	/**
	 * 出力ストリームラッパ
	 *
	 * @author Tadashi Nakayama
	 */
	private static final class DuplicateServletOutputStream
			extends ServletOutputStream implements Serializable {
		/** serialVersionUID */
		private static final long serialVersionUID = 1L;

		/** バイト配列 */
		private byte[] bytes = null;
		/** カウンタ */
		private int count = 0;

		/** Charset名 */
		private final String charset;

		/**
		 * コンストラクタ
		 *
		 * @param val Charset名
		 */
		DuplicateServletOutputStream(final String val) {
			this.charset = val;
		}

		/**
		 * @see java.io.OutputStream#write(int)
		 */
		@Override
		public void write(final int b) {
			final var newcount = this.count + 1;
			if (this.bytes.length < newcount) {
				this.bytes = Arrays.copyOf(this.bytes, Math.max(this.bytes.length * 2, newcount));
			}
			this.bytes[this.count] = (byte)b;
			this.count = newcount;
		}

		/**
		 * @see java.lang.Object#toString()
		 */
		@Override
		public String toString() {
			try {
				return new String(this.bytes, 0, this.count, this.charset);
			} catch (final UnsupportedEncodingException e) {
				LogManager.getLogger().info(e.getMessage());
				return "";
			}
		}

		/**
		 * @see javax.servlet.ServletOutputStream#isReady()
		 */
		@Override
		public boolean isReady() {
			return false;
		}

		/**
		 * @see javax.servlet.ServletOutputStream#setWriteListener(javax.servlet.WriteListener)
		 */
		@Override
		public void setWriteListener(final WriteListener arg0) {
			return;
		}
	}

	/**
	 * 複製ライタ
	 *
	 * @author Tadashi Nakayama
	 */
	private static final class DuplicateCharArrayWriter
			extends CharArrayWriter implements Serializable {
		/** serialVersionUID */
		private static final long serialVersionUID = 1L;

		/**
		 * コンストラクタ
		 */
		DuplicateCharArrayWriter() {
			super();
		}
	}
}
