/*
 * Copyright (C) 2013-2015 RoboVM AB
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 *   
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License. 
 * 
 * Portions of this code is based on Parse's AnyPic sample
 * which is copyright (C) 2013 Parse.
 */
package org.robovm.samples.robopods.parse.anypic.ios.ui.controllers.login;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

import org.robovm.apple.foundation.NSArray;
import org.robovm.apple.foundation.NSData;
import org.robovm.apple.foundation.NSDictionary;
import org.robovm.apple.foundation.NSError;
import org.robovm.apple.foundation.NSMutableData;
import org.robovm.apple.foundation.NSObject;
import org.robovm.apple.foundation.NSURL;
import org.robovm.apple.foundation.NSURLConnection;
import org.robovm.apple.foundation.NSURLConnectionDataDelegate;
import org.robovm.apple.foundation.NSURLConnectionDataDelegateAdapter;
import org.robovm.apple.foundation.NSURLRequest;
import org.robovm.apple.foundation.NSURLRequestCachePolicy;
import org.robovm.apple.foundation.NSURLResponse;
import org.robovm.apple.uikit.UIApplication;
import org.robovm.apple.uikit.UIImage;
import org.robovm.apple.uikit.UIImageView;
import org.robovm.apple.uikit.UIScreen;
import org.robovm.apple.uikit.UIViewController;
import org.robovm.objc.block.VoidBlock1;
import org.robovm.objc.block.VoidBlock3;
import org.robovm.pods.facebook.core.FBSDKAccessToken;
import org.robovm.pods.facebook.core.FBSDKGraphRequest;
import org.robovm.pods.facebook.core.FBSDKGraphRequestConnection;
import org.robovm.pods.facebook.core.FBSDKProfile;
import org.robovm.pods.facebook.core.FBSDKProfileChangeNotification;
import org.robovm.pods.parse.PFError;
import org.robovm.pods.parse.PFErrorCode;
import org.robovm.pods.parse.PFGetCallback;
import org.robovm.pods.parse.PFSaveCallback;
import org.robovm.pods.parse.PFUser;
import org.robovm.samples.robopods.parse.anypic.ios.AnyPicApp;
import org.robovm.samples.robopods.parse.anypic.ios.model.PAPCache;
import org.robovm.samples.robopods.parse.anypic.ios.model.PAPUser;
import org.robovm.samples.robopods.parse.anypic.ios.util.Log;
import org.robovm.samples.robopods.parse.anypic.ios.util.PAPUtility;

public class PAPWelcomeViewController extends UIViewController implements PAPLogInViewControllerDelegate {
    private boolean presentedLoginViewController;
    private int facebookResponseCount;
    private int expectedFacebookResponseCount;

    private NSMutableData profilePicData;

    @Override
    public void loadView() {
        UIImageView backgroundImageView = new UIImageView(UIScreen.getMainScreen().getBounds());
        backgroundImageView.setImage(UIImage.getImage("Default.png"));
        setView(backgroundImageView);
    }

    @Override
    public void viewDidLoad() {
        super.viewDidLoad();

        FBSDKProfile.Notifications.observeCurrentProfileDidChange(new VoidBlock1<FBSDKProfileChangeNotification>() {
            @Override
            public void invoke(FBSDKProfileChangeNotification notification) {
                if (FBSDKProfile.getCurrentProfile() != null && PAPUser.getCurrentUser() != null) {
                    PAPUser.getCurrentUser().fetchInBackground(new PFGetCallback<PAPUser>() {
                        @Override
                        public void done(PAPUser object, NSError error) {
                            refreshCurrentUser(object, error);
                        }
                    });
                }
            }
        });
    }

    @Override
    public void viewWillAppear(boolean animated) {
        super.viewWillAppear(animated);

        if (PAPUser.getCurrentUser() == null) {
            presentLoginViewController(false);
            return;
        }

        // Present Anypic UI
        ((AnyPicApp) UIApplication.getSharedApplication().getDelegate()).presentTabBarController();

        // Refresh current user with server side data -- checks if user is still
        // valid and so on
        facebookResponseCount = 0;
        PAPUser.getCurrentUser().fetchInBackground(new PFGetCallback<PAPUser>() {
            @Override
            public void done(PAPUser object, NSError error) {
                if (FBSDKProfile.getCurrentProfile() != null) {
                    refreshCurrentUser(object, error);
                }
            }
        });
    }

    public void presentLoginViewController(boolean animated) {
        if (presentedLoginViewController) {
            return;
        }
        presentedLoginViewController = true;
        PAPLoginViewController loginViewController = new PAPLoginViewController();
        loginViewController.setDelegate(this);
        getNavigationController().presentViewController(loginViewController, animated, null);
    }

    @Override
    public void didLogin(PAPLoginViewController logInViewController) {
        if (presentedLoginViewController) {
            presentedLoginViewController = false;
            getNavigationController().dismissViewController(true, null);
        }
    }

    private void processedFacebookResponse() {
        // Once we handled all necessary facebook batch responses, save
        // everything necessary and continue
        synchronized (this) {
            facebookResponseCount++;
            if (facebookResponseCount != expectedFacebookResponseCount) {
                return;
            }
        }
        facebookResponseCount = 0;
        Log.d("done processing all Facebook requests");

        PAPUser.getCurrentUser().saveInBackground(new PFSaveCallback() {
            @Override
            public void done(boolean success, NSError error) {
                if (!success) {
                    Log.e("Failed save in background of user, %s", error);
                } else {
                    Log.d("saved current parse user");
                }
            }
        });
    }

    private void refreshCurrentUser(PFUser refreshedUser, NSError error) {
        // This fetches the most recent data from FB, and syncs up all data with
        // the server including profile pic and friends list from FB.

        if (error != null && error instanceof PFError) {
            PFError e = (PFError) error;
            // A PFErrorCode.ObjectNotFound error on currentUser refresh signals
            // a deleted user
            if (e.getErrorCode() == PFErrorCode.ObjectNotFound) {
                Log.d("User does not exist.");
                ((AnyPicApp) UIApplication.getSharedApplication().getDelegate()).logOut();
                return;
            }
        }

        if (FBSDKAccessToken.getCurrentAccessToken() == null) {
            Log.e("FB access token does not exist, logout");
            ((AnyPicApp) UIApplication.getSharedApplication().getDelegate()).logOut();
            return;
        }

        if (FBSDKProfile.getCurrentProfile() == null) {
            Log.e("FB user profile does not exist, logout");
            ((AnyPicApp) UIApplication.getSharedApplication().getDelegate()).logOut();
            return;
        }

        final PAPUser currentParseUser = PAPUser.getCurrentUser();
        if (currentParseUser == null) {
            Log.d("Current Parse user does not exist, logout");
            ((AnyPicApp) UIApplication.getSharedApplication().getDelegate()).logOut();
            return;
        }

        String facebookId = currentParseUser.getFacebookId();
        if (facebookId == null || facebookId.length() == 0) {
            // set the parse user's FBID
            currentParseUser.setFacebookId(FBSDKProfile.getCurrentProfile().getUserID());
        }

        if (!PAPUtility.userHasValidFacebookData(currentParseUser)) {
            Log.d("User does not have valid facebook ID. PFUser's FBID: %s, Facebook FBID: %s. logout",
                    currentParseUser.getFacebookId(), FBSDKProfile.getCurrentProfile().getUserID());
            ((AnyPicApp) UIApplication.getSharedApplication().getDelegate()).logOut();
            return;
        }

        // Finished checking for invalid stuff
        expectedFacebookResponseCount = 0;
        Set<String> permissions = FBSDKAccessToken.getCurrentAccessToken().getPermissions();
        if (permissions.contains("public_profile")) {
            // Logged in with FB
            // Create batch request for all the stuff

            FBSDKGraphRequestConnection connection = new FBSDKGraphRequestConnection();
            expectedFacebookResponseCount++;
            connection.addRequest(new FBSDKGraphRequest("me", null),
                    new VoidBlock3<FBSDKGraphRequestConnection, NSObject, NSError>() {
                        @Override
                        public void invoke(FBSDKGraphRequestConnection connection, NSObject result, NSError error) {
                            if (error != null) {
                                // Failed to fetch me data.. logout to be safe
                                Log.e("couldn't fetch facebook /me data: %s, logout", error);
                                ((AnyPicApp) UIApplication.getSharedApplication().getDelegate()).logOut();
                                return;
                            }
                            NSDictionary data = (NSDictionary) result;

                            String facebookName = data.getString("name");
                            if (facebookName != null && facebookName.length() > 0) {
                                currentParseUser.setDisplayName(facebookName);
                            }

                            processedFacebookResponse();
                        }
                    });

            // profile pic request
            // Now add the data to the UI elements
            NSURL profilePictureURL = new NSURL(String.format(
                    "https://graph.facebook.com/%s/picture?type=large&return_ssl_resources=1", facebookId));
            NSURLRequest profilePictureURLRequest = new NSURLRequest(profilePictureURL,
                    NSURLRequestCachePolicy.UseProtocolCachePolicy, 10);
            new NSURLConnection(profilePictureURLRequest, profilePictureRequestDelegate).start();

            if (permissions.contains("user_friends")) {
                // Fetch FB Friends + me
                expectedFacebookResponseCount++;
                connection.addRequest(new FBSDKGraphRequest("me/friends", null),
                        new VoidBlock3<FBSDKGraphRequestConnection, NSObject, NSError>() {
                            @Override
                            public void invoke(FBSDKGraphRequestConnection connection, NSObject result, NSError error) {
                                Log.d("processing Facebook friends");
                                if (error != null) {
                                    // just clear the FB friend cache
                                    PAPCache.getSharedCache().clear();
                                } else {
                                    NSDictionary object = (NSDictionary) result;
                                    NSArray<NSDictionary> data = (NSArray<NSDictionary>) object.get("data");
                                    List<String> facebookIds = new ArrayList<>(data.size());
                                    for (NSDictionary friendData : data) {
                                        String friendId = friendData.getString("id");
                                        if (friendId != null) {
                                            facebookIds.add(friendId);
                                        }
                                    }
                                    // cache friend data
                                    PAPCache.getSharedCache().setFacebookFriends(facebookIds);

                                    currentParseUser.removeFacebookFriends();

                                    if (currentParseUser.hasAlreadyAutoFollowedFacebookFriends()) {
                                        ((AnyPicApp) UIApplication.getSharedApplication().getDelegate())
                                                .autoFollowUsers();
                                    }
                                }
                                processedFacebookResponse();
                            }
                        });
            }
            connection.start();
        } else {
            NSData profilePictureData = UIImage.getImage("AvatarPlaceholder").toPNGData();
            PAPUtility.processFacebookProfilePictureData(profilePictureData);

            PAPCache.getSharedCache().clear();
            currentParseUser.setDisplayName("Someone");
            expectedFacebookResponseCount++;
            processedFacebookResponse();
        }
    }

    private final NSURLConnectionDataDelegate profilePictureRequestDelegate = new NSURLConnectionDataDelegateAdapter() {
        @Override
        public void didReceiveResponse(NSURLConnection connection, NSURLResponse response) {
            profilePicData = new NSMutableData();
        }

        @Override
        public void didReceiveData(NSURLConnection connection, NSData data) {
            profilePicData.append(data);
        }

        @Override
        public void didFinishLoading(NSURLConnection connection) {
            PAPUtility.processFacebookProfilePictureData(profilePicData);
        }

        @Override
        public void didFail(NSURLConnection connection, NSError error) {
            Log.e("Connection error downloading profile pic data: %s", error);
        }
    };
}