public static function instance()
 {
     if ( is_null(self::$_instance) )
         self::$_instance = new self();
     return self::$_instance;
 }
    /**
     * @param string $method login|autologin
     * @param array  $input  The input to parse to get the entry data, typically $_POST data
     */
    public function __construct( $method, $input = null )
    {
        if ( is_null( $input ) )
        {
            $input = $_POST;
        }

        $this->_method = $method;
        $this->_input  = array();

        $this->_isAutologin = ( $method == self::ESB_METHOD_AUTOLOGIN );

        if ( isset( $input['context'] ) )
        {
            $this->_input['context'] = $input['context'];
        }

        $params = array();

        UserLog::instance()->action( $this->_method )->step( 'received' );
        if (   isset($input['format'])
            && $input['format'] == 'json'
        ) {
            $this->redirectMode(self::LOGIN_REDIRECT_MODE_JSON);
        }

        switch ( $this->_method )
        {
            case self::ESB_METHOD_RELOG:
                $this->_input = $input;
                return $this->relog();
                break;
            case self::ESB_METHOD_LOGIN:
                $userID     = isset( $input['userID'] ) ? $input['userID'] : null;
                $password   = isset( $input['password'] ) ? $input['password'] : null;
                $rememberme = isset( $input['rememberme'] ) ? $input['rememberme'] == 'on' : null;

                if ( $userID && $password )
                {
                    $params = array(
                        $this->getBusinessNameMapping('identifier') => $userID,
                        $this->getBusinessNameMapping('password') => $password,
                    );


                    // Brute force prevention
                    if ( SolrSafeOperatorHelper::featureIsActive('PreventBruteForce') )
                    {
                        $freeLoginAttempts          = SolrSafeOperatorHelper::feature('PreventBruteForce', 'FreeLoginAttempts');
                        $loginAttemptsDelay         = SolrSafeOperatorHelper::feature('PreventBruteForce', 'LoginAttemptsDelay');
                        $maxLoginAttempts           = SolrSafeOperatorHelper::feature('PreventBruteForce', 'MaxLoginAttempts');
                        $maxLoginAttemptsPageUrl    = SolrSafeOperatorHelper::feature('PreventBruteForce', 'MaxLoginAttemptsPageUrl');

                        if ( empty($freeLoginAttempts) )
                            $freeLoginAttempts = 5;
                        if ( empty($loginAttemptsDelay) )
                            $loginAttemptsDelay = 60;
                        if ( empty($maxLoginAttempts) )
                            $maxLoginAttempts = 10;
                        if ( empty($maxLoginAttemptsPageUrl) )
                            $maxLoginAttemptsPageUrl    = eZINI::instance('merck.ini')->variable('LoginSettings', 'MaxLoginAttemptsPageUrl');

                        $memcacheKey = 'msd_login_attempts_'.$userID;
                        $attempts = MemcacheTool::instance()->get( $memcacheKey );
                        if ( $attempts === false )
                            $attempts = 0;

                        $attempts++;

                        if ( $attempts > $maxLoginAttempts )
                        {
                            switch( $this->redirectMode() )
                            {
                                case self::LOGIN_REDIRECT_MODE_JS:
                                case self::LOGIN_REDIRECT_MODE_JSON:
                                    echo json_encode(array(
                                        'status'        => 1,
                                        'LoginRedirect' => $maxLoginAttemptsPageUrl,
                                    ));
                                    eZExecution::cleanExit();
                                    break;
                                default:
                                    header( 'Location: '.$maxLoginAttemptsPageUrl );
                                    eZExecution::cleanExit();
                                    break;
                            }
                        }

                        $waitingAttempts = max(0, ($attempts - $freeLoginAttempts));
                        $timeToSleep = $waitingAttempts > 0 ? pow(2, ($waitingAttempts - 1)) : 0;
                        sleep( $timeToSleep );
                        MemcacheTool::instance()->set( $memcacheKey, $attempts, $loginAttemptsDelay );
                        if( ContextTool::instance()->environment() != ContextTool::ENVIRONMENT_PROD )
                            header("X-login-attempts: $attempts - waiting $timeToSleep s");

                    }

                    if ( $rememberme )
                    {
                        $params['rememberme'] = $input['rememberme'];
                    }
                }
                break;
            case self::ESB_METHOD_AUTOLOGIN:
                $u = isset( $input['u'] ) ? $input['u'] : null;
                $t = isset( $input['t'] ) ? $input['t'] : null;
                $eal    = isset( $input['eal'] ) ? $input['eal'] : null;
                $origin = isset( $input['origin'] ) ? $input['origin'] : null;

                if ( $t )
                {
                    $params['Token'] = str_replace(' ', '+', $t);
                }
                elseif ( $u )
                {
                    $str = self::decipherCreatorMailES( $u );
                    // add missing quotes if needed
                    $str      = preg_replace( '#([{,]\s*"[^"]+"\s*:\s*)([^"][^},"]+)#', '\1"\2"', $str );
                    $userData = json_decode( $str, true );
                    if ( is_null( $userData ) )
                    {
                        eZDebug::writeError( print_r( array(
                                'u' => $u,
                                'd' => $str ), true ),
                            'Error decoding JSON from CreatorMail' );
                    }

                    $LoginID    = isset( $userData['LoginID'] ) ? $userData['LoginID'] : null;
                    $Password   = isset( $userData['Password'] ) ? $userData['Password'] : null;
                    $UUMPID      = isset( $userData['UUMPID'] ) ? $userData['UUMPID'] : null;
                    $rememberme = isset( $input['rememberme'] ) ? $input['rememberme'] == 'on' : null;

                    $this->_method         = self::ESB_METHOD_LOGIN;
                    $this->_forceAutologin = true;

                    if ( $UUMPID )
                    {
                        $params = array(
                            $this->getBusinessNameMapping( 'userId' ) => $UUMPID
                        );
                        $this->_method = self::ESB_METHOD_READ;
                    }
                    elseif ( $LoginID && $Password )
                    {
                        $params = array(
                            $this->getBusinessNameMapping( 'userName' ) => $LoginID,
                            $this->getBusinessNameMapping( 'password' ) => $Password,
                        );
                    }

                    if ( $rememberme )
                    {
                        $params['rememberme'] = $input['rememberme'];
                    }
                }
                elseif( $eal && $origin )
                {
                    // Email AutoLogin (ESBv2 exclusive feature)
                    $tripleDESKeyAndIV = $this->getTripleDESKeyAndIVPerOrigin( $origin );
                    if( SolrSafeOperatorHelper::featureIsActive('UUMP') && $tripleDESKeyAndIV )
                    {
                        $r = mcrypt_decrypt(
                            MCRYPT_3DES,
                            pack( 'H*', $tripleDESKeyAndIV['key'] ),
                            base64_decode( str_replace( ' ', '+', $eal ) ),
                            MCRYPT_MODE_CBC,
                            $tripleDESKeyAndIV['iv']
                        );
                        $r = substr( $r, 0, -1 * ord( $r[strlen( $r ) - 1] ) );

                        $service = ESBFactory::getUserService();
                        $searchResult = $service->search( $r, 'emailAddress' );
                        if( $searchResult['data']['errorCode'] != 0 )
                        {
                            return self::error();
                        }

                        $params = array(
                            $this->getBusinessNameMapping( 'userId' ) => $searchResult['data']['results']['userId']
                        );
                        $this->_method = self::ESB_METHOD_READ;

                        if ( $rememberme )
                        {
                            $params['rememberme'] = $input['rememberme'];
                        }
                    }
                    else
                    {
                        return self::error();
                    }
                }
                break;
        }

        if( !empty($params) )
        {
            if ( isset( $params[$this->getBusinessNameMapping( 'userName' )] ) )
            {
                UserLog::instance()->uuid( $params[$this->getBusinessNameMapping( 'userName' )] );
            }
            if ( isset( $params[$this->getBusinessNameMapping( 'userId' )] ) )
            {
                UserLog::instance()->uuid( $params[$this->getBusinessNameMapping( 'userId' )] );
            }
            if ( isset( $params['Token'] ) )
            {
                UserLog::instance()->uuid( $params['Token'] );
            }
        }
        else
        {
            UserLog::instance()
                ->uuid( 'Unknown' )
                ->msg( 'Could not parse input: ' . print_r( $_REQUEST, true ) );
        }

        UserLog::instance()->store();

        if( !empty($params) )
        {
            $this->_input          = array_merge( $this->_input, $params );
            $countryOfRegistration = $this->getCountryOfRegistration();

            $this->_loginParams = array();
            if ( SolrSafeOperatorHelper::featureIsActive( 'UUMP' ) )
            {
                $params['countryOfRegistration'] = $countryOfRegistration;
                $this->_loginParams['data']      = $params;
            }
            else
            {
                $this->_loginParams['Data'] = $params;
                $this->_loginParams['cr']   = $countryOfRegistration;
            }
        }
    }