/*
 * Copyright (c) 2009 The openGion Project.
 *
 * 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.opengion.hayabusa.servlet;

import javax.websocket.OnOpen; 
import javax.websocket.OnClose; 
import javax.websocket.OnMessage;
import javax.websocket.OnError;
import javax.websocket.Session; 
import javax.websocket.EndpointConfig; 
import javax.websocket.CloseReason; 
import javax.websocket.server.ServerEndpoint;
import java.io.IOException; 
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.nio.ByteBuffer; 
import java.awt.image.BufferedImage;
import javax.imageio.ImageIO;

/**
 * http://enterprisegeeks.hatenablog.com/entry/2015/12/17/104815 
 *
 * WebSocketBasicEndpoint.java のソースを参照しています。
 *
 * 設定が、いくつか必要です。
 *   ① /wsdemo をサーバーエンドポイントのURLにしているため
 *      WEB-INF/web.xml の security-constraint の web-resource-collection の
 *      url-pattern に、/wsdemo を追加する必要がある。
 *        <url-pattern>/wsdemo/*</url-pattern>
 *
 *   ② コンパイル時(build.xml)のクラスパスの設定に、
 *      <pathelement path="${env.CATALINA_HOME}/lib/websocket-api.jar" />
 *      を追加する必要がある。
 */
// 1. サーバーエンドポイントのURLの設定を行う。
@ServerEndpoint(value="/wsdemo")
public class WebSocketDemo {

	/**
	 * デフォルトコンストラクター
	 *
	 */
	public WebSocketDemo() { super(); }		// これも、自動的に呼ばれるが、空のメソッドを作成すると警告されるので、明示的にしておきます。

	/**
	 * 2.クライアントからの接続時にコールされる。
	 * 
	 * 引数は以下が設定可能だが、メソッド内で使用しないなら省略できる。
	 * @param client クライアントの接続情報
	 * @param config 設定情報
	 */
	@OnOpen
	public void onOpen( final Session client, final EndpointConfig config ) {
		System.out.println( client.getId() + " was connected." );
	}

	/** 
	 * 3.クライアントの切断時にコールされる
	 * 
	 * 引数は使用しなければ省略可能。
	 * @param client 接続
	 * @param reason 切断理由
	 */
	@OnClose
	public void onClose( final Session client, final CloseReason reason ) {
		System.out.println( client.getId() + " was closed by " 
								+ reason.getCloseCode()
								+ "[" + reason.getCloseCode().getCode()+"]" );
	}

	/**
	 * 4.エラー時にコールされる。
	 * 
	 * 引数は使用しなければ省略可能。
	 * @param client クライアント接続
	 * @param error エラー
	 */
	@OnError
	public void onError( final Session client, final Throwable error ) {
		System.out.println( client.getId() + " was error." );
		error.printStackTrace();
	}

	/**
	 * 5.テキストメッセージ受信時の処理
	 * 
	 * 全クライアントにメッセージを送信する。（サンプル）
	 * 
	 * 引数は使用しなければ省略可能。
	 * @param text クライアントから送信されたテキスト
	 * @param client 接続情報
	 */
	@OnMessage
	public void onMessage( final String text, final Session client ) throws IOException {
		for( final Session other : client.getOpenSessions() ) {
			other.getBasicRemote().sendText( text );
		}
	}

	/**
	 * 6.バイナリ受信時の処理
	 * 
	 * 送信元に画像を変換して送り返す。
	 * 
	 * 引数は使用しなければ省略可能。
	 * @param buf クライアントから送信されたバイナリ
	 * @param client 接続情報
	 */
	@OnMessage
	public void onMessage( final ByteBuffer buf, final Session client ) throws IOException {
		client.getBasicRemote().sendBinary( grayScall( buf ) );
	}

	/**
	 * 7.画像をグレースケールに変換する。本筋とは関係ない。
	 * 
	 * @param input 引数のバイトバッファー
	 * @return グレースケールに変換されたバイトバッファー
	 */
	private ByteBuffer grayScall( final ByteBuffer input ) throws IOException {
		final BufferedImage img = ImageIO.read( new ByteArrayInputStream( input.array() ) );
		final BufferedImage glay = new BufferedImage( img.getWidth(), img.getHeight(), BufferedImage.TYPE_BYTE_GRAY );
		glay.getGraphics().drawImage( img, 0, 0, null );
		final ByteArrayOutputStream bos = new ByteArrayOutputStream();
		ImageIO.write( glay, "png", bos );

		return ByteBuffer.wrap( bos.toByteArray() );
	}
}
