/*
**	指定したURLからhtmlページをダウンロードするサンプル
*/
application HtmlDownloader;

import FileSystem;
import NetworkSystem;
import FileStream;
import SocketStream;
import Dialogs;
import Form;
import Label;
import Button;
import TextField;



/*
	URL解析失敗を表す例外
*/
exception URLParseFailure inherits ParseFailure;



/*
	簡易URLクラス
		https://hostname/path
		hostname/path
		の2形式にのみ対応したURL解析クラス。
*/
class URL is private :
for all :
	// コンストラクタ
	constructor new( url as String ) :
		this.url = url;
		parse;
	end

	// URLの文字列表現
	field url as String;

	// ホスト
	field host as String;

	// パス
	field path as String;

	// ホストとパスをまとめて返す
	method parts as String, String :
		return host, path;
	end

for self :
	constant HTTP_PROTOCOL_NAME as String = "https://";

	// urlからhostとpathを抽出する
	method parse :
		var length = url.length;
		var host_and_path as String;
		if length >= HTTP_PROTOCOL_NAME.size and url.cut_bytes(0, HTTP_PROTOCOL_NAME.size) == HTTP_PROTOCOL_NAME :
			var remain = url.size - HTTP_PROTOCOL_NAME.size;
			if remain == 0 :
				throw URLParseFailure.NoHost;
			end
			host_and_path = url.cut_bytes(HTTP_PROTOCOL_NAME.size, remain);
		else :
			host_and_path = url;
		end

		var slash_offset = host_and_path.find('/'@Byte);
		if slash_offset == -1 :
			throw URLParseFailure.NoPath;
		else if slash_offset == 0 :
			throw URLParseFailure.NoHost;
		end

		host = host_and_path.cut_bytes(0, slash_offset);
		path = host_and_path.cut_bytes(slash_offset, host_and_path.size - slash_offset);
	end
end



/*
	HTTPでファイルを取得するルーチンクラス
	ダウンロード処理を独立させるために作った。
*/
class HttpGetter :
for all :
	constant USER_AGENT as String = "Http Getter(Uva Sample)/0.0";

	// ダウンロード処理
	own method download( host as String, path as String, file_path as String ) :
		// 設定値
		var port = 80;

		//--- ソケットを開く
		var socket = Socket.open(host, port);
		var socket_out = SocketOutputStream.new(socket);
		System.display.println("--- connected ---");

		// リクエスト文字列の作成
		var message = String.format(
			"GET %1 HTTP/1.0\nUser-Agent: %2\n\n",
			[path, USER_AGENT]
		);

		//--- リクエストの送信
		System.display.println("--- request ---");
		System.display.println(message);
		socket_out.write(message);

		//--- レスポンスの表示
		System.display.println("--- response ---");
		read_response(SocketInputStream.new(socket), file_path);

		do :
			var dlg = MessageDialog.new_information("Sample", "取得しました。");
			dlg.execute;
		end

	catch NetworkSystemFailure :
		var dlg = MessageDialog.new_error("Sample", Exception.description, MessageDialog.BUTTON_OK);
		dlg.execute;

	finally :
		//--- ソケットを閉じる
		Socket.close(socket);
	end

for self :
	// サーバーからの応答を処理する
	own method read_response( input as InputStream, file_path as String ) :
		var reader = TextInputStream.new(input);
		var header_received = false;
		var line = "";

		// ヘッダをディスプレイに出力する
		System.display.println("[header]");
		while true :
			line = reader.read_line;
			break if line.length == 0;
			System.display.println(line);
		loop

		// 内容をファイルに出力する
		var file = FileOutputStream.open(file_path);
		while true :
			line = reader.read_line;
			file.write(line);
			file.write(System.EOL);
		catch StreamTerminator :
		loop
		file.close;

	catch FileSystemFailure :
		var dlg = MessageDialog.new_error("Sample", Exception.description, MessageDialog.BUTTON_OK);
		dlg.execute;
	end
end



/*
	アプリケーションの起動クラス
*/
class HtmlDownloader :
	// 起動メソッド
	method main :
		// フォームを作成して
		make_form;

		// フォームをセンタリングして
		form.move_to_center;

		// フォームをモーダル表示する
		form.show_modal;
	end

for self :
	// フォーム(メインウインドウ)
	field form as Form;

	// "URL"ラベル
	field url_label as Label;

	// URL入力フィールド
	field url_field as TextField;

	// 取得ボタンの作成
	field go_button as PushButton;

	// フォームを作成する
	method make_form :
		// フォームの作成
		form = Form.new(this);
		form.set_caption("Webページをダウンロード");
		form.set_client_extent(400, 30);
		form.set_resizable(false);

		// "URL"ラベルの作成
		url_label = Label.new(this);
		url_label.set_bounds(5, 5, 60, 20);
		url_label.set_caption("URL:");

		// URL入力フィールドの作成
		url_field = TextField.new(this);
		url_field.set_bounds(65, 5, 256, 20);
		url_field.set_text("www.yahoo.co.jp/index.html");

		// 取得ボタンの作成
		go_button = PushButton.new(this);
		go_button.set_bounds(65+256+5, 5, 60, 20);
		go_button.set_caption("Get!!");
		go_button.set_action("download");	// アクション名を"download"としている

		// フォームにウィジェットを追加する
		form.add(url_label);
		form.add(url_field);
		form.add(go_button);
	end

for self :
	// 開始ボタンが押された時に発生するアクションのハンドラ
	method download( source as Object ) :
		var url = URL.new(url_field.text);

		var file_dlg = FileDialog.new;
		if file_dlg.execute(FileDialog.FOR_SAVE) :
			var file_path = file_dlg.directory + FileSystem.FILENAME_SEPARATOR + file_dlg.file_name;
			HttpGetter.download(url.host, url.path, file_path);
		end
	catch URLParseFailure :
		var dlg = MessageDialog.new_error("Sample", "URLが正しくありません。", MessageDialog.BUTTON_OK);
		dlg.execute;
	end
end