var AUTH_SIGNIN                 = 'authenticate';
var AUTH_SIGNUP                 = 'register';
var AUTH_RESET_PASSWORD_REQUEST = 'resetpasswordrequest';
var AUTH_RESET_PASSWORD_CONFIRM = 'resetpasswordconfirm';
var AUTH_LOGOUT                 = 'logout';

var JacValidAuthCallbackCodes = [ AUTH_SIGNIN, AUTH_SIGNUP,
    AUTH_RESET_PASSWORD_REQUEST, AUTH_RESET_PASSWORD_CONFIRM, AUTH_LOGOUT ];

function jacGetResultErr(actionCode, err)
{
    return { "actionCode": actionCode, 
             "error": err, 
             "sessionid": null };
}

/* Note: also see pasGetAuthCookieDomain */
function jacGetAuthCookieDomain(varName)
{
    return 'auth' + varName;
}

/* Note: also see pacs_setUsername */
function jac_setUsername(userName)
{
    v_setCookie(jacGetAuthCookieDomain('userName'), userName);
}

/* Note: also see jac_deleteUsername() */
function jac_deleteUsername()
{
    v_deleteCookie(jacGetAuthCookieDomain('userName'));
}

/* Note: see also pacs_getUsername() */
function jac_getUsername()
{
    return v_getCookie(jacGetAuthCookieDomain('userName'));
}

/* Note: see also pacs_setSessionId() */
function jac_setSessionId(sessionId)
{
    v_setCookie(jacGetAuthCookieDomain('sessionId'), sessionId);
}

/* Note: also see jac_deleteSessionId() */
function jac_deleteSessionId()
{
    v_deleteCookie(jacGetAuthCookieDomain('sessionId'));
}

/* Note: see also pacs_getSessionId() */
function jac_getSessionId()
{
    return v_getCookie(jacGetAuthCookieDomain('sessionId'));
}

/* This function returns the last server authentication state, cached 
 * locally on the client. Don't rely on it to (dis)allow operations
 * that should trully be authenticated. */
function jac_isAuthenticated()
{
    return isSet( jac_getSessionId() );
}

function jacCallbackAuth(res, err)
{
    if( ! v_areCookiesEnabled() ) {
        alert( 'jacCallbackAuth:cookies not enabled.' );
        return;
    }

    if( err ) {
        alert( 'err:' + err );
        return;
    }

    if( ! res ) {
        alert( '! res' );
        return;
    }

    jslog('jacCallbackAuth:actionCode:' + res['actionCode'] + 
        ':username:' + res['username'] + 
        ':error:' + res['error'] + ':err:' + err + 
        ':sessionid:' + res['sessionid']);

    actionCode = res['actionCode'];

    if( AUTH_LOGOUT == actionCode ) {
        if( null == res['error'] ) {
            jac_deleteSessionId();
            jac_deleteUsername();
            jslog('sucesfully logged out');
        } else {
            msg  = 'Logout failed on server. To finalize logout, try again';
            msg += ' or erase local cookies and restart your browser session.';
            alert(msg);
            jslog('logout failed');
        }
    } else if( AUTH_RESET_PASSWORD_REQUEST != actionCode ) {
        if( null == res['error'] && null != res['sessionid'] && 
            null != res['username']) 
        {
            jac_setUsername(res['username']);
            jac_setSessionId(res['sessionid']);
            jslog('AUTHORIZED');
        } else {
            jac_deleteUsername();
            jac_deleteSessionId();
            jslog('NOT AUTHORIZED');
        }
    }
}

function jacPasswordClearToCrypt(clearPass)
{
    /* Never store clear password. Furthermore do not store md5 hashes
     * of passwords either - there are websites out there such as 
     * http://md5.rednoize.com/ that cache millions of reverse engineered 
     * passwords. 
     * 
     * A simple solution is to never store the password - just store a
     * session generated by the server upon validating the password. 
     * 
     * NOTE: see pacs_clientPasswordClearToCrypt as well. */
    return hex_md5('AuthClientScrambler' + 
                   hex_md5(clearPass + 'AuthClientScrambler'));
}

function jacCallbackUser(res)
{
    actionCode = res['actionCode'];
    if( -1 == JacValidAuthCallbackCodes.find(actionCode) ) {
        alert('jacCallbackUser: cannot find actionCode=' + actionCode +
            '. Valid actionCodes are:' + JacValidAuthCallbackCodes);
        return;
    }

    var cbArr = PhpAuthJSCallbacks[actionCode];
    if( ! isDefined(cbArr) ) { 
        jslog('No callback defined for actionCode=' + actionCode);
        return;
    }
    for( var i = 0; i < cbArr.length; i++ ) {
        var cb = cbArr[i];
        (cb)(res);
    }
}

function jacCallback(res, err)
{
    jacCallbackAuth(res, err);
    if( isDefined(err) && null != err && '' != err ) {
        res['error'] = err;
    }
    jacCallbackUser(res);
}

function jac_authenticate(username, password)
{
    var key = AUTH_SIGNIN;
    if( ! isDefined(PhpAuthJSCallbacks[key]) ) {
        jslog( 'No signin cb registered; call jac_addCb(AUTH_SIGNIN).' );
    }

    if( ! v_areCookiesEnabled() ) {
        res = jacGetResultErr(AUTH_SIGNIN, ':cookies not enabled.');
        jacCallbackUser(res);
        return;
    }
    
    jac_setUsername(username);

    PhpAuthServer.pas_authenticate(AUTH_SIGNIN, username, 
        jacPasswordClearToCrypt(password), jacCallback);
}

/* The "ticket" parameter is going to be remembered for every
 * registration. */
function jac_register(username, email, password, ticket)
{
    var key = AUTH_SIGNUP;
    if( ! isDefined(PhpAuthJSCallbacks[key]) ) {
        jslog( 'No signup cb registered; call jac_addCb(AUTH_SIGNUP).' );
    }

    if( ! v_areCookiesEnabled() ) {
        res = jacGetResultErr(AUTH_SIGNIN, ':cookies not enabled.');
        jacCallbackUser(res);
        return;
    }

    jac_setUsername(username);

    PhpAuthServer.pas_register(AUTH_SIGNUP, username, email, 
        jacPasswordClearToCrypt(password), ticket, jacCallback);
}

function jac_sendResetPasswordRequest(clientAppFriendlyName, username, cbPage)
{
    var key = AUTH_RESET_PASSWORD_REQUEST;
    if( ! isDefined(PhpAuthJSCallbacks[key]) ) {
        jslog( 'No reset password request cb registered;' + 
            ' call jac_addCb(AUTH_RESET_PASSWORD_REQUEST).' );
    }

    if( ! v_areCookiesEnabled() ) {
        res = jacGetResultErr(AUTH_RESET_PASSWORD_REQUEST, 
            ':cookies not enabled.');
        jacCallbackUser(res);
        return;
    }

    if( !isDefined(cbPage) || null == cbPage || '' == cbPage ) {
        alert( 'cbPage not provided' );
        return;
    }
 
    PhpAuthServer.pas_serveResetPasswordRequest(AUTH_RESET_PASSWORD_REQUEST,
        clientAppFriendlyName, username, cbPage, jacCallback);
}

function jac_sendResetPasswordConfirm(sessionidReset, passwordNew)
{
    var key = AUTH_RESET_PASSWORD_CONFIRM;
    if( ! isDefined(PhpAuthJSCallbacks[key]) ) {
        jslog( 'No reset password request cb registered;' + 
            ' call jac_addCb(AUTH_RESET_PASSWORD_CONFIRM).' );
    }

    if( ! v_areCookiesEnabled() ) {
        res = jacGetResultErr(AUTH_RESET_PASSWORD_CONFIRM, 
            ':cookies not enabled.');
        jacCallbackUser(res);
        return;
    }

    PhpAuthServer.pas_serveResetPasswordConfirm(AUTH_RESET_PASSWORD_CONFIRM,
        sessionidReset, jacPasswordClearToCrypt(passwordNew), jacCallback);
}

function jac_logout()
{
    if( ! jac_isAuthenticated() ) {
        jslog('User not authenticated');
        return;
    }

    var sessionId = jac_getSessionId();
    PhpAuthServer.pas_logout(AUTH_LOGOUT, sessionId, jacCallback);
}

function jac_addCb(actionCode, cb)
{
    if( -1 == JacValidAuthCallbackCodes.find(actionCode) ) {
        alert('provided invalid actionCode=' + actionCode +
            '. Valid actionCodes are:' + JacValidAuthCallbackCodes);
        return;
    }
    var key = actionCode;
    if( ! isDefined(PhpAuthJSCallbacks[key]) ) { 
        PhpAuthJSCallbacks[key] = new Array();
    }
    PhpAuthJSCallbacks[key].push(cb);
}
