/**
 *  ---------
 * |.##> <##.|  Open Smart Card Development Platform (www.openscdp.org)
 * |#       #|
 * |#       #|  Copyright (c) 2018 CardContact Systems GmbH
 * |'##> <##'|  Schuelerweg 38, 32429 Minden, Germany (www.cardcontact.de)
 *  ---------
 *
 *  This file is part of OpenSCDP.
 *
 *  OpenSCDP is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License version 2 as
 *  published by the Free Software Foundation.
 *
 *  OpenSCDP is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with OpenSCDP; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 * @fileoverview TokenController
 */

SmartCardHSM	= require('scsh/sc-hsm/SmartCardHSM').SmartCardHSM;
SubjectDAO	= require('scsh/pki-db/db/SubjectDAO').SubjectDAO;
CommonUI	= require('scsh/srv-cc1/CommonUI').CommonUI;
PKAWizard	= require('pki-as-a-service/ui/PKAWizard').PKAWizard;



function TokenController(ui) {
	this.ui = ui;
	this.service = ui.service;
}

exports.TokenController = TokenController;



TokenController.prototype.getView = function(req, res) {
	default xml namespace = "http://www.w3.org/1999/xhtml";

	var page =	<div>
				<h1>Remote Token Management</h1>
				<p>You can connect a device by running:</p>
				<pre>
					$ java -jar ocf-cc.jar http://localhost:8080/rt/hsm
				</pre>
				<p>Press the <a href="">refresh</a> button once the SmartCard-HSM is connected.</p>
				<h2>Connected Token</h2>
				<div id="token"/>
			</div>;

	var homeURL = this.ui.homeURL(req.url);
	var hsmlist = this.service.hsmService.getHSMStates();

	for each (var hsm in hsmlist) {
		var assignedTo = this.getSubject(hsm.path);
		var usedBy = this.getCASubjects(hsm.keyDomain);

		if (!assignedTo && usedBy.length == 0) {
			continue;
		}

		var tokenControl = this.renderToken(hsm, homeURL, assignedTo, usedBy);
		page.div.appendChild(<p>{tokenControl}</p>);
	}

	if (page.div.p.length() == 0) {
		page.appendChild(<p>No SmartCard-HSM connected yet.</p>);
	}
	return page;
}



TokenController.prototype.getCASubjects = function(keyDomain) {
	var list = [];

	var subjectDAO = this.service.daof.getSubjectDAO();
	var cas = subjectDAO.listSubjectsByType(SubjectDAO.TYPE_ROOT_CA);
	for  each (var bo in cas) {
		var ca = this.service.getSubject(bo.id);
		if (ca.getKeyDomain() == keyDomain && ca.isCertificationOfficer(this.ui.session.user)) {
			list.push(ca);
		}
	}

	return list;
}



TokenController.prototype.getSubject = function(tokenId) {
	var tokenDAO = this.service.daof.getTokenDAO();
	var token = tokenDAO.getToken(tokenId);
	if (token) {
		var subject = this.service.getSubject(token.subjectId);
		if (subject.canRead(this.ui.session.user)) {
			return subject;
		}
	}
}



TokenController.prototype.getHSM = function(tokenId) {
	var hsmlist = this.service.hsmService.getHSMStates();
	for each (var hsm in hsmlist) {
		if (hsm.path != tokenId) {
			continue;
		}

		var subject = this.getSubject(tokenId);
		if (subject && subject.canRead(this.ui.session.user)) {
			return hsm;
		}

		var cas = this.getCASubjects(tokenId);
		if (cas.length) {
			return hsm;
		}
	}
}



/**
 * Prepare table with status information for HSM
 *
 * @param {HSMState} hsmstate the current state
 */
TokenController.prototype.renderToken = function(hsmstate, homeURL, assignedTo, usedBy) {
	default xml namespace = "http://www.w3.org/1999/xhtml";

	// ID

	var t = <table class="pure-table pure-table-horizontal form-table"/>;
	t.tr += <tr>
			<th>Id</th>
			<td>{ hsmstate.path }</td>
		</tr>;

	// Assigned To

	if (assignedTo) {
		var desc = assignedTo.getType();
		if (assignedTo.getName()) {
			desc += " " + assignedTo.getName();
		} else {
			desc += " " + assignedTo.getEmail();
		}

		var url = homeURL + "/subject/" + assignedTo.getId();

		t.tr +=
			<tr>
				<th>Assigned To</th>
				<td>
					<a href={url}>{desc}</a>
				</td>
			</tr>;
	}

	// Use by CA

	if (usedBy && usedBy.length) {
		var ul = <ul class="plain"/>;
		for each (var subject in usedBy) {
			if (subject.getType() == SubjectDAO.TYPE_ROOT_CA) {
				var desc = "Root CA";
			} else {
				var desc = subject.getType();
			}
			desc += " " + subject.getName();

			var url = homeURL + "/subject/" + subject.getId();

			ul.li += <li><a href={url}>{desc}</a></li>;
		}

		t.tr +=
			<tr>
				<th>Used By</th>
				<td>{ul}</td>
			</tr>;
	}

	// Reader

	if (hsmstate.readerName) {
		t.tr += <tr>
				<th>Reader</th>
				<td>{ hsmstate.readerName }</td>
			</tr>;
	}

	// Status

	var statusRow = <tr>
				<th>Status</th>
			</tr>;

	if (hsmstate.isOffline()) {
		if (hsmstate.error) {
			var str = "Offline (" + hsmstate.error + ")";
		} else {
			var str = "Offline";
		}
		statusRow.appendChild(<td>{ str }</td>);
		t.appendChild(statusRow);
	} else {
		if (hsmstate.isLocal) {
			statusRow.appendChild(<td>Online</td>);
		} else {
			statusRow.appendChild(
				<td>
					<form class="pure-form" action="" method="get">
						Online
						<input name="op" type="hidden" value="disconnect"/>
						<input name="token" type="hidden" value={hsmstate.path}/>
						<button class="pure-button" type="submit">Disconnect</button>
					</form>
				</td>);
		}
		t.appendChild(statusRow);

		// Authentication Status

		var authenticationRow = <tr/>
		authenticationRow.appendChild(<th>Authentication Status</th>);

		if (hsmstate.pka) {
			var authStatus = hsmstate.pka.describeStatus();
		} else {
			var authStatus = SmartCardHSM.describePINStatus( hsmstate.pinsw, "User");
		}

		if (hsmstate.isUserAuthenticated()) {
			authenticationRow.appendChild(
				<td>
					<form class="pure-form" action="" method="get">
						{ authStatus }
						<input name="op" type="hidden" value="logout"/>
						<input name="token" type="hidden" value={hsmstate.path}/>
						<button class="pure-button" type="submit">Logout</button>
					</form>
				</td>);
		} else {
			if (hsmstate.pka) {
				authenticationRow.appendChild(
					<td>
						<form class="pure-form" action="" method="get">
							{ authStatus }
							<input name="op" type="hidden" value="authenticate"/>
							<input name="token" type="hidden" value={hsmstate.path}/>
							<button class="pure-button" type="submit">Authenticate</button>
						</form>
					</td>);
			} else if (hsmstate.hasProtectedPINEntry()) {
				authenticationRow.appendChild(
					<td>
						<form class="pure-form" action="" method="get">
							{ authStatus }
							<input name="op" type="hidden" value="verifypin"/>
							<input name="token" type="hidden" value={hsmstate.path}/>
							<button class="pure-button" type="submit">Verify PIN</button>
						</form>
					</td>);
			} else {
				authenticationRow.appendChild(
					<td>
						<form class="pure-form" action="" method="get">
							<p>{ authStatus }</p>
							<input name="op" type="hidden" value="verifypin"/>
							<input name="token" type="hidden" value={hsmstate.path}/>
							<input name="pin" type="password" size="20" class="pure-input"/>
							<button class="pure-button" type="submit">Verify PIN</button>
						</form>
					</td>);
			}
		}
		t.appendChild(authenticationRow);
	}

	return t;
}



/**
 * Create wizard to handle public key authentication
 */
TokenController.prototype.handlePKAAuthenticate = function(req, res, hsm) {
	this.ui.wizard = new PKAWizard(this.ui, hsm, "/token");
	this.ui.redirectTo(req, res, req.url, "/wizard");
}



TokenController.prototype.handleRequest = function(req, res) {
	default xml namespace = "http://www.w3.org/1999/xhtml";
	GPSystem.log(GPSystem.DEBUG, module.id, "handleRequest()");

	var page = this.getView(req, res);
	var t = this.ui.generateTemplate(req.url);
	var c = t..div.(@id == "content");
	c.div = page;

	if (req.queryString) {
		// Handle operations
		var operation = CommonUI.parseQueryString(req.queryString);
		var hsm = this.getHSM(operation.token);
		if (!hsm || hsm.isOffline()) {
			this.ui.redirectTo(req, res, req.url, "/token");
			return;
		}


		switch(operation.op) {
		case "verifypin":
			this.service.hsmService.verifyUserPIN(hsm, operation.pin);
			break;
		case "authenticate":
			this.handlePKAAuthenticate(req, res, hsm);
			return;
			break;
		case "logout":
			hsm.logout();
			break;
		case "disconnect":
			hsm.disconnect();
			break;
		}
		this.ui.redirectTo(req, res, req.url, "/token");
	} else {
		this.ui.sendPage(req, res, req.url, t);
	}
}
