// TODO:
// ･<name>.pageと<name>.tmplがあれば<name>.pageと<name>.htmlを読み込み<name>.htmlの仕様チェックが走る。
// ･仕様チェックは初期読み込みの一回限り。<name>.scriptファイルの日付で仕様チェックをしたりしなかったりする。
// ･<name>.pageがあって<name>.htmlがなければ_a_<name>.html生成
// ･<name>.tmplがあって<name>.pageがなければ_a_<name>.page生成
// ･日付を見て、scriptファイルをキャッシュする。
// ･文字コードを考慮する。
// ･セッション情報をもつ。
// ･PATH_INFOをみて、読み込みテンプレートを変更する。
// ･scriptのキャッシュフォルダは自動生成する。

import cgi;
import dom;
import tmpl;
import std.date;

extern(C) int exit(int status);
class NotFoundException:Exception
{
	this(char[]s){super(s);}
}
class Sample{
	static CGI cgi;
	this(){
	}
	CGI getCGI(){if(cgi===null)cgi = new CGI();return cgi;}
	static char[] getHeader(){
		return  "Content-type: text/html; charset=Shift_JIS\n"
				"Pragma: no-cache\n"
				"Cache-Control: no-cache\n"
				"Expires: "~getExpires()~"\n";
	}
	static char[] getFilename(){
		char[] pathinfo = Env["PATH_INFO"];
		pathinfo = pathinfo.replace("..","");
		if(pathinfo=="")pathinfo="/index.html";
		if(pathinfo=="/")pathinfo="/index.html";
		//.htaccessで名前変換して、.htmlを.tmplにした場合
		if(pathinfo.length>=5 && pathinfo[pathinfo.length-5..pathinfo.length]==".tmpl"){
			pathinfo[pathinfo.length-5..pathinfo.length]=".html";
		}
		//.htaccessで名前変換している場合に、自cgiを普通に使った場合
		if(pathinfo.length>=1+cgi.self_name.length && pathinfo[0..6]=="/"~cgi.self_name){
			pathinfo=pathinfo[1+cgi.self_name.length..pathinfo.length];
		}
		if(pathinfo.length>1 && pathinfo[0]=='/'){
			pathinfo = pathinfo[1..pathinfo.length];
		}
		return pathinfo;
	}

	void setCommandLineOption(char[] exename,char[][char[]] param){
		if(Env["REQUEST_METHOD"]==""){
			if(param["REQUEST_METHOD"]==""){
				param["REQUEST_METHOD"]="GET";
			}
			char[] str = "@echo off\n";
			foreach(char[] key,char[] value;param){
				str ~= "set "~key~"="~value~"\n";
			}
			str ~= exename;
			write("____cgigo____.bat",cast(void[])str);
			system("____cgigo____.bat");
			remove("____cgigo____.bat");
			exit(0);
		}
	}

	static char[] getCommand(){
		char[] filename = getFilename();
		if(filename.length>5 && filename[filename.length-5..filename.length]==".html"){
			return filename[0..filename.length-5];
		}
		return "index";
	}

	void function(CGI cgi,char[] command) f[char[]];
	void setFunction(char[] name, void function(CGI cgi,char[] filename) func){
		f[name]=func;
	}
	void dispatch()
	{
		char[]  command = getCommand();
		char[] filename = getFilename();
		try{
			if(f[command]!==null){
				f[command](cgi,filename);
			}else{
				print_default(cgi,filename);
			}
		}catch(NotFoundException o){
		}catch(Exception o){
			printf("%.*s\n%.*s",Sample.getHeader(),o.toString());
		}
		
	}
	private import std.regexp;
	private import std.file;
	version(GNU){
		import std.file;
		static bool exists(char[] filename){
			try{
				(new File(filename,FileMode.In)).close();
			}catch(Exception e){
				return false;
			}
			return true;
		}
	}
	static char[] read(char[] filename){
		if(!exists(filename)){
			printf("%.*s",Sample.getHeader());
		    printf("Status: 404 Not Found\n\n");
		    printf("filename=%.*s\n",filename);
			try{assert(false);}catch(Object o){throw(new NotFoundException("file not found. "~line(o)));}
		}
		return cast(char[])std.file.read(filename);
	}
	static char[] line(Object o){
		char[][] s = (new RegExp(`\(.*\)`,"")).match(o.toString());
		if(s.length>0)
			return s[0];
		return o.toString();
	}
}
void main()
{
	Sample sample = new Sample();
	char[][char[]]opt;
	opt["REQUEST_METHOD"]="GET";
	opt["PATH_INFO"]="/bbs/index.html";
	opt["QUERY_STRING"]="test=name";
	opt["REQUEST_URI"]="http:/localhost/test.cgi/"~opt["PATH_INFO"]~"?"~opt["QUERY_STRING"];
	sample.setCommandLineOption("test",opt);

    CGI cgi = sample.getCGI();
	sample.setFunction("bbs/index",&print_bbs_index);
	sample.setFunction("bbs/confilm",&print_bbs_confilm);
	sample.dispatch();
}

void print_default(CGI cgi,char[] filename)
{
	char[] html = Sample.read("tmpl/"~filename);
	printf("%.*s\n",Sample.getHeader());
	printf("%.*s",html.replace("\r",""));
}

// 対応するtest.xmlを読み出して、適用する。
void print_bbs_index(CGI cgi,char[] filename)
{
	char[] testname=cgi.reqs["test"];
	if(testname!=""){//testパラメータがあるときは、テストデータを使おうとする。
		char[] command = Sample.getCommand();
		char[] html = Sample.read("tmpl/"~filename);
		Dom dom;
		char[] testfilename = "tmpl/"~command~".test.xml";
		if(!isfile(testfilename)){// テストファイルが無ければエラー
			printf("%.*s\n",Sample.getHeader());
			printf("<html><body>file not found %.*s</body></html>\n",
				testfilename
			);
			return;
		}
		try{
			dom = Dom.parse(Sample.read(testfilename)).getDocument();
		}catch(Exception e){
			printf("%.*s\n",Sample.getHeader());
			printf("<html><body>perser error %.*s<pre>%.*s</pre></body></html>\n",testfilename,Dom.xmlEscape(e.toString(),0).replace("&#10;","\n"));
			return;
		}
		// testcase/test/@name=testnameを探す。
		Stack stack=new Stack();
		Dom test=null;
		if(dom.value=="testcase"){
			for(int i=0;i<dom.array.length;i++){
				if(dom.array[i].value=="model"){
					if(dom.array[i].getAttr("name")==testname){
						test=dom.array[i];
						break;
					}
				}
			}
		}
		if(test===null){// 該当testが見つからない。
			printf("%.*s\n",Sample.getHeader());
			//test一覧をリンク付きで表示しよう。
			printf("<html><body>not found test %.*s testname=%.*s</body></html>\n",testfilename,testname);
			return;
		}
		// とりあえず、テストケースのデータを表示する。
		printf("%.*s\n",Sample.getHeader());
		printf("<html><head><meta http-equiv=\"content-type\" content=\"text/html; charset=Shift_JIS\" />"
				"</head><body><pre>%.*s</pre></body></html>\n"
				,Dom.xmlEscape(test.toString(),1).replace("\r",""));
//html=`<html><T.error c="エラーメッセージがあるとき表示される"><$message></T.error></html>`;
		TmplDom tmpl = new TmplDom(html,"",command);
		tmpl.setDom(test);
		printf(tmpl.toString());
	}else{
		print_default(cgi,filename);
	}
}

void print_bbs_confilm(CGI cgi,char[] filename)
{
	printf("%.*s\n",Sample.getHeader());
	printf("%.*s","bbsconfilm");
}
