/**
  * Entry point of the special page.
  * 
  * Special:Connect uses a MVC architecture, with execute() being the
  * controller. The control flow happens by switching the subpage's name and
  * then moving through a boatload of nested ifs (notice how early returns
  * are avoided). Subpages are exclusively used for secondary stages of the
  * connecting process; that is, they are only invoked from Special:Connect
  * itself. AJAX functions can also streamline the connecting process by
  * sending the user directly to one of these subpages. A similar control
  * structure has been laid out in the login function of ext.facebook.js.
  * 
  * Now on to the MVC part.
  * 
  * Function execute() operates on three possible models: the MediaWiki User
  * class, FacebookUser and FacebookApplication. At the end of a control path,
  * the process is this: update the model, then call the view (which may depend
  * on the model, but strictly on a read-only basis).
  * 
  * Models are only updated from subpages, not Special:Connect. Subpages are
  * the endpoint of any connecting/disconnecting process, and are only valid
  * when POSTed to from Special:Connect or AJAX calls acting on behalf of
  * Special:Connect. The one exception is if the user is logged in to Facebook
  * and has a MediaWiki account, but isn't logged in to MediaWiki; they will
  * then be logged in to MediaWiki. Oh, and a second exception is that
  * Special:Connect/Debug can be navigated to, but it's only for testing purposes.
  */
 public function execute($subPageName)
 {
     global $wgUser, $wgRequest;
     // Setup the session
     global $wgSessionStarted;
     if (!$wgSessionStarted) {
         wfSetupSession();
     }
     $this->setReturnTo();
     switch ($subPageName) {
         /**
          * Special:Connect/ChooseName is POSTed to after the new Facebook user
          * has chosen their MediaWiki account options (the wpNameChoice param),
          * either to connect an existing account (if allowed) or to create a
          * new account with the specified options.
          * 
          * TODO: Verify that the request is a POST, not a GET (currently they
          * both do the same thing, I think).
          */
         case 'ChooseName':
             if ($wgRequest->getCheck('wpCancel')) {
                 $this->sendError('facebook-cancel', 'facebook-canceltext');
             } else {
                 $choice = $wgRequest->getText('wpNameChoice');
                 try {
                     if ($choice == 'existing') {
                         // Handle accidental reposts
                         if (!$wgUser->isLoggedIn()) {
                             $name = $wgRequest->getText('wpExistingName');
                             $password = $wgRequest->getText('wpExistingPassword');
                             // Update the model
                             $fbUser = new FacebookUser();
                             $fbUser->attachUser($name, $password, $this->getUpdatePrefs());
                         }
                         // Send the view
                         $this->sendPage('displaySuccessAttachingView');
                     } else {
                         // Handle accidental reposts
                         if (!$wgUser->isLoggedIn()) {
                             // Update the model (wpDomain isn't currently set...)
                             $fbUser = new FacebookUser();
                             $fbUser->createUser($this->getUsername($choice), $wgRequest->getText('wpDomain'));
                         }
                         $this->sendPage('loginSuccessView', true);
                     }
                 } catch (FacebookUserException $e) {
                     // HACK: If the title msg is 'connectNewUserView' then we
                     // send the view instead of an error
                     if ($e->getTitleMsg() == 'connectNewUserView') {
                         $this->sendPage('connectNewUserView', $e->getTextMsg());
                     } else {
                         $this->sendError($e->getTitleMsg(), $e->getTextMsg(), $e->getMsgParams());
                     }
                 }
             }
             break;
             /**
              * Special:Connect/LogoutAndContinue does just that -- logs the current
              * MediaWiki user out and another MediaWiki user in based on the current
              * Facebook credentials. No parameters, so if a non-Facebook users GETs
              * this they will be logged out and sent to Special:UserLogin.
              * 
              * TODO: In the case above, redirect to Special:UserLogout.
              */
         /**
          * Special:Connect/LogoutAndContinue does just that -- logs the current
          * MediaWiki user out and another MediaWiki user in based on the current
          * Facebook credentials. No parameters, so if a non-Facebook users GETs
          * this they will be logged out and sent to Special:UserLogin.
          * 
          * TODO: In the case above, redirect to Special:UserLogout.
          */
         case 'LogoutAndContinue':
             // Update the model (MediaWiki user)
             $oldName = $wgUser->logout();
             $injected_html = '';
             // unused
             wfRunHooks('UserLogoutComplete', array(&$wgUser, &$injected_html, $oldName));
             $fbUser = new FacebookUser();
             if ($fbUser->getMWUser()->getId()) {
                 // Update the model again (Facebook user)
                 $fbUser->login();
                 $this->sendPage('loginSuccessView');
             } else {
                 $this->sendRedirect('UserLogin');
             }
             break;
             /**
              * Special:Connect/MergeAccount takes care of connecting Facebook users
              * to existing accounts.
              * 
              * TODO: Verify this is a POST request
              */
         /**
          * Special:Connect/MergeAccount takes care of connecting Facebook users
          * to existing accounts.
          * 
          * TODO: Verify this is a POST request
          */
         case 'MergeAccount':
             if (!$wgUser->isLoggedIn()) {
                 // Shouldn't be here
                 $this->sendError('facebook-error', 'facebook-errortext');
             } else {
                 try {
                     $fbUser = new FacebookUser();
                     // Handle accidental reposts
                     if ($fbUser->getMWUser()->getId() != $wgUser->getId()) {
                         $fbUser->attachCurrentUser($this->getUpdatePrefs());
                     }
                     $this->sendPage('displaySuccessAttachingView');
                 } catch (FacebookUserException $e) {
                     $this->sendError($e->getTitleMsg(), $e->getTextMsg(), $e->getMsgParams());
                 }
             }
             break;
             /**
              * Special:Connect/Deauth is a callback used by Facebook to notify the
              * application that the Facebook user has deauthenticated the app
              * (removed it from their app settings page). If the request for this
              * page isn't signed by Facebook, it will redirect to Special:Connect.
              */
         /**
          * Special:Connect/Deauth is a callback used by Facebook to notify the
          * application that the Facebook user has deauthenticated the app
          * (removed it from their app settings page). If the request for this
          * page isn't signed by Facebook, it will redirect to Special:Connect.
          */
         case 'Deauth':
             // Facebook will include a signed_request param to verify authenticity
             global $facebook;
             $signed_request = $facebook->getSignedRequest();
             if ($signed_request) {
                 // Update the model
                 $fbUser = new FacebookUser($signed_request['user_id']);
                 $fbUser->disconnect();
                 // What view should we show to Facebook? It doesn't really matter...
                 $this->setHeaders();
             } else {
                 // signed_request not present or hash mismatch
                 $this->sendRedirect('Connect');
             }
             break;
             /**
              * Special:Connect/Debug allows an administrator to verify that both
              * the Facebook application this extension are setup and working
              * correctly. This page can only be accessed if $wgFbAllowDebug is
              * true; see config.default.php for more information.
              */
         /**
          * Special:Connect/Debug allows an administrator to verify that both
          * the Facebook application this extension are setup and working
          * correctly. This page can only be accessed if $wgFbAllowDebug is
          * true; see config.default.php for more information.
          */
         case 'Debug':
             global $wgFbAllowDebug;
             if (!empty($wgFbAllowDebug)) {
                 $app = new FacebookApplication();
                 if ($app->canEdit()) {
                     $this->sendPage('debugView');
                 } else {
                     // Something went wrong
                     $fbUser = new FacebookUser();
                     // First, check MediaWiki permissions. Then check with Facebook
                     if ($fbUser->getMWUser()->getId()) {
                         // If $wgFbUserRightsFromGroups is set, this should trigger a group check
                         $groups = $fbUser->getMWUser()->getEffectiveGroups();
                         if (in_array('sysop', $groups) || in_array('fb-admin', $groups)) {
                             $this->sendError('facebook-error', 'facebook-error-application');
                             // roles
                         } else {
                             global $wgLang, $wgFbUserRightsFromGroup;
                             $groupArray = array('sysop');
                             if (!empty($wgFbUserRightsFromGroup)) {
                                 $groupArray[] = 'fb-admin';
                             }
                             $groupsList = array_map(array('User', 'makeGroupLinkWiki'), $groupArray);
                             $this->sendError('facebook-error', 'badaccess-groups', array($wgLang->commaList($groupsList), count($groupsList)));
                         }
                     } else {
                         // Facebook user is not logged in or is not connected to a MediaWiki user
                         // Show special instructions to MediaWiki administrators
                         $isAdmin = false;
                         if ($wgUser->isLoggedIn()) {
                             $groups = $wgUser->getEffectiveGroups();
                             if (in_array('sysop', $groups) || in_array('fb-admin', $groups)) {
                                 $isAdmin = true;
                             }
                         }
                         if ($isAdmin) {
                             $this->sendError('facebook-error', 'facebook-error-needs-convert');
                         } else {
                             // Generic validation error
                             $this->sendError('facebook-error', 'facebook-errortext');
                         }
                     }
                 }
                 break;
             }
             // no break
             /**
              * Special:Connect was called with no subpage specified.
              */
         // no break
         /**
          * Special:Connect was called with no subpage specified.
          */
         default:
             // If $subPageName was invalid, redirect to Special:Connect with no subpage
             if (!empty($subPageName)) {
                 $this->sendRedirect('Connect');
                 break;
             }
             $fbUser = new FacebookUser();
             // Our Facebook session might be stale. If this becomes a problem, use
             // $fbUser->isLoggedIn($ping = true) to force a refresh of the state
             if (!$fbUser->isLoggedIn()) {
                 // The user isn't logged in to Facebook
                 if (!$wgUser->isLoggedIn()) {
                     // The user isn't logged in to Facebook or MediaWiki
                     $this->sendRedirect('UserLogin');
                     // Nothing to see here, move along
                 } else {
                     // The user is logged in to MediaWiki but not Facebook
                     $this->sendPage('loginToFacebookView');
                 }
             } else {
                 // The user is logged in to Facebook
                 $mwId = $fbUser->getMWUser()->getId();
                 if (!$wgUser->isLoggedIn()) {
                     // The user is logged in to Facebook but not MediaWiki
                     if (!$mwId) {
                         // The Facebook user is new to MediaWiki
                         $this->sendPage('connectNewUserView');
                     } else {
                         // The user is logged in to Facebook, but not MediaWiki.
                         // The UserLoadAfterLoadFromSession hook might have misfired
                         // if the user's "remember me" option was disabled.
                         $fbUser->login();
                         $this->sendPage('loginSuccessView');
                     }
                 } else {
                     // The user is logged in to Facebook and MediaWiki
                     if ($mwId == $wgUser->getId()) {
                         // MediaWiki user belongs to the Facebook account
                         $this->sendRedirect('UserLogin');
                         // Nothing to see here, move along
                     } else {
                         // Accounts don't agree
                         if (!$mwId) {
                             // Facebook user is new
                             $fb_ids = FacebookDB::getFacebookIDs($wgUser);
                             if (count($fb_ids) == 0) {
                                 // MediaWiki user is free
                                 // Both accounts are free. Ask to merge
                                 $this->sendPage('mergeAccountView');
                             } else {
                                 // MediaWiki user already associated with Facebook ID
                                 // TODO: LogoutAndCreateNewUser form
                                 global $wgContLang;
                                 $param1 = '[[' . $wgContLang->getNsText(NS_USER) . ":{$wgUser->getName()}|{$wgUser->getName()}]]";
                                 $param2 = $fbUser->getUserInfo('name');
                                 $this->sendError('errorpagetitle', 'facebook-error-wrong-id', array('$1' => $param1, '$2' => $param2));
                             }
                         } else {
                             // Facebook account has a MediaWiki user
                             // Ask to log out and continue as the new user ($mwId)
                             $this->sendPage('logoutAndContinueView', $mwId);
                         }
                     }
                 }
             }
     }
 }