/*
 * 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 java.net.URI;

import javax.websocket.ClientEndpoint;
import javax.websocket.ContainerProvider;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.CloseReason;
import javax.websocket.EndpointConfig; 
import javax.websocket.WebSocketContainer;
import javax.websocket.DeploymentException;

import javax.websocket.ClientEndpointConfig;
import javax.websocket.Endpoint;
import javax.xml.bind.DatatypeConverter;
import java.util.Map;
import java.util.List;
import java.util.Arrays;
import java.io.IOException;

/**
 * Websocket Endpoint implementation class WebSocketClient
 *
 * Client を実行するには、%CATALINA_HOME%/lib/websocket-api.jar ではだめです。
 * META-INF\services\javax.websocket.ContainerProvider に、Provider を
 * 記述しておく必要があるそうです。
 *
 * 方法としては、
 *  ① tyrus-standalone-client-jdk-1.13.1.jar を使用する。
 *  ② %CATALINA_HOME%/lib/tomcat-websocket.jar を使用する。
 *       この場合、依存関係で、/lib/tomcat-util.jar 、bin/tomcat-juli.jar も
 *       使用します。
 *     ※ 今現在、java Client は動いていません。
 *
 */

@ClientEndpoint
public class WebSocketClient extends Endpoint {

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

	/**
	 * サーバーからの通知受信のためのコールバック
	 * 
	 * 引数は以下が設定可能だが、メソッド内で使用しないなら省略できる。
	 * 
	 * @param session サーバーの接続情報
	 * @param config 設定情報
	 */
	@OnOpen
	public void onOpen( final Session session, final EndpointConfig config ) {
		/* セッション確立時の処理 */
		System.out.println("[セッション確立]");
	}

	/**
	 * 5.テキストメッセージ受信時の処理
	 * 
	 * 引数は使用しなければ省略可能。
	 * 
	 * @param message サーバーから送信されたテキスト
	 * @param session 接続情報
	 */
	@OnMessage
	public void onMessage( final String message , final Session session ) {
		/* メッセージ受信時の処理 */
		System.out.println("[受信]:" + message);
	}

	/**
	 * 4.エラー時にコールされる。
	 * 
	 * 引数は使用しなければ省略可能。
	 * 
	 * @param session サーバーの接続情報
	 * @param th エラー
	 */
	@OnError
	public void onError( final Session session , final Throwable th ) {
		/* エラー発生時の処理 */
		System.out.println("[エラー発生]");
	}

	/**
	 * 3.切断時にコールされる。
	 * 
	 * 引数は使用しなければ省略可能。
	 * 
	 * @param session サーバーの接続情報
	 * @param reason 切断理由
	 */
	@OnClose
	public void onClose( final Session session, final CloseReason reason ) {
		/* セッション解放時の処理 */
		System.out.println("[セッション解放]");
	}

	/**
	 * メインメソッド。
	 * 
	 * @param args 引数
	 * @throws DeploymentException	WebSocketで、何らかの種類の障害が発生したことを示すチェック例外。
	 * @throws IOException	なんらかの入出力例外の発生を通知するシグナルを発生させます。
	 * @throws InterruptedException	スレッドで割り込みが発生した場合にスローされます。
	 */
	public static void main( final String[] args ) throws DeploymentException,IOException,InterruptedException {
		// BASIC認証対応
		final ClientEndpointConfig.Configurator configurator = new ClientEndpointConfig.Configurator() {
			/**
			 * サーバーへの接続を開始するために使用されるハンドシェイク要求を作成した後で、要求の一部を送信する前に実装によって呼び出されます。
			 * @param headers 実装がハンドシェイクのやりとりを開始するために送信しようとしているハンドシェイク要求ヘッダーの可変マップ。
			 */
			@Override
			public void beforeRequest( final Map<String, List<String>> headers ) {
				headers.put("Authorization", Arrays.asList("Basic " + DatatypeConverter.printBase64Binary("admin:admin".getBytes())));
			}
		};

		final ClientEndpointConfig clientConfig = ClientEndpointConfig.Builder.create()
				.configurator(configurator)
				.build();

		// 初期化のため WebSocket コンテナのオブジェクトを取得する
		final WebSocketContainer container = ContainerProvider.getWebSocketContainer();
		// サーバー・エンドポイントの URI
		final URI uri = URI.create( "ws://localhost:8826/gf/wsdemo?AAA=BBB" );
		// サーバー・エンドポイントとのセッションを確立する
		final Session session = container.connectToServer( new WebSocketClient(),clientConfig,uri );		// BASIC認証対応

		session.getBasicRemote().sendText("こんにちは");

		// 仮想マシンのシャットダウン・フックを登録(Ctrl-C で正常終了します)
		Runtime.getRuntime().addShutdownHook(
			new Thread( WebSocketClient.class.getName() ) {
				/**
				 * runメソッド。
				 */
				@Override
				public void run() {
					// シャットダウン中は、LOGGER が実行されないようです。
					System.out.println( "Shutdown WebSocketClient ..." );
					try {
						session.close();
					}
					catch( final Throwable th ) {
						th.printStackTrace();
					}
				}
			}
		);

		int cnt = 0;
		while( session.isOpen() ) {
			Thread.sleep( 10 * 1000 );
			System.out.println( "open " + cnt );
			session.getBasicRemote().sendText( "カウンター:" +  (cnt++) );
		}

		System.out.println( "Session closed." );
		session.close();
	}
}
