/*
 * Copyright 2017 Google Inc.
 *
 * 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.
 */
package com.example.androidthings.robocar.shared;


import android.Manifest;
import android.arch.lifecycle.Lifecycle.Event;
import android.arch.lifecycle.Lifecycle.State;
import android.arch.lifecycle.LifecycleObserver;
import android.arch.lifecycle.OnLifecycleEvent;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.support.v4.content.ContextCompat;

import com.example.androidthings.robocar.shared.lifecycle.ConfigResistantObserver;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.common.api.GoogleApiClient.ConnectionCallbacks;
import com.google.android.gms.common.api.GoogleApiClient.OnConnectionFailedListener;


/**
 * Fragment that handles connecting with GoogleClientApi. It is retained across configuration
 * changes, so there won't be spurious disconnecting and reconnecting when the user does
 * something like rotate the screen.
 * <br/><br/>
 * Usage: call {@link #attachTo(FragmentActivity, GoogleApiClient)} from your Activity. You need not
 * check that there is already one of these attached.
 */
public class ConnectorFragment extends Fragment implements ConnectionCallbacks,
        OnConnectionFailedListener {

    private static final String FRAGMENT_TAG =
            "com.example.androidthings.robocar.shared.ConnectorFragment";

    private static final int REQUEST_PERMISSIONS = 1;

    private GoogleApiClient mGoogleApiClient;
    private ConnectorCallbacks mCallbacks;
    private LifecycleObserver mLifecycleObserver = new ConfigResistantObserver() {
        @Override
        protected void onReallyStart() {
            connect();
        }

        @Override
        protected void onReallyStop() {
            disconnect();
        }

        @OnLifecycleEvent(Event.ON_CREATE)
        public void create() {
            mGoogleApiClient.registerConnectionCallbacks(ConnectorFragment.this);
            mGoogleApiClient.registerConnectionFailedListener(ConnectorFragment.this);
        }

        @OnLifecycleEvent(Event.ON_DESTROY)
        public void destroy() {
            mGoogleApiClient.unregisterConnectionCallbacks(ConnectorFragment.this);
            mGoogleApiClient.unregisterConnectionFailedListener(ConnectorFragment.this);
        }
    };

    public interface ConnectorCallbacks {
        void onGoogleApiConnected(Bundle bundle);
        void onGoogleApiConnectionSuspended(int cause);
        void onGoogleApiConnectionFailed(ConnectionResult connectionResult);
    }

    public static void attachTo(FragmentActivity activity, GoogleApiClient client) {
        FragmentManager fm = activity.getSupportFragmentManager();
        ConnectorFragment fragment = get(fm);
        if (fragment == null) {
            fragment = newInstance(client);
            fm.beginTransaction().add(fragment, FRAGMENT_TAG).commitAllowingStateLoss();
        }
    }

    public static void connect(FragmentActivity activity) {
        ConnectorFragment fragment = get(activity.getSupportFragmentManager());
        if (fragment != null) {
            fragment.connect();
        }
    }

    private static ConnectorFragment get(FragmentManager fm) {
        if (fm.isDestroyed()) {
            throw new IllegalStateException("Can't get fragment after onDestroy");
        }

        Fragment fragment = fm.findFragmentByTag(FRAGMENT_TAG);
        if (fragment != null && !(fragment instanceof ConnectorFragment)) {
            throw new IllegalStateException("Unexpected fragment instance was returned by tag");
        }
        return (ConnectorFragment) fragment;
    }

    private static ConnectorFragment newInstance(GoogleApiClient client) {
        ConnectorFragment fragment = new ConnectorFragment();
        fragment.mGoogleApiClient = client;
        return fragment;
    }

    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        if (context instanceof ConnectorCallbacks) {
            mCallbacks = (ConnectorCallbacks) context;
        }
    }

    @Override
    public void onDetach() {
        super.onDetach();
        mCallbacks = null;
    }

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setRetainInstance(true);
        getLifecycle().addObserver(mLifecycleObserver);
    }

    private void connect() {
        if (getLifecycle().getCurrentState().isAtLeast(State.STARTED)
                && !mGoogleApiClient.isConnected()
                && !mGoogleApiClient.isConnecting()) {
            if (hasPermissions()) {
                mGoogleApiClient.connect();
            } else {
                requestPermissions();
            }
        }
    }

    private void disconnect() {
        if (mGoogleApiClient.isConnected() || mGoogleApiClient.isConnecting()) {
            mGoogleApiClient.disconnect();
        }
    }

    private boolean hasPermissions() {
        return ContextCompat.checkSelfPermission(getActivity(),
                Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED;
    }

    private void requestPermissions() {
        super.requestPermissions(new String[]{Manifest.permission.ACCESS_COARSE_LOCATION},
                REQUEST_PERMISSIONS);
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
            @NonNull int[] grantResults) {
        if (requestCode == REQUEST_PERMISSIONS) {
            if (grantResults.length > 0
                    && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                connect();
            }
        } else {
            super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        }
    }

    @Override
    public void onConnected(@Nullable Bundle bundle) {
        if (mCallbacks != null) {
            mCallbacks.onGoogleApiConnected(bundle);
        }
    }

    @Override
    public void onConnectionSuspended(int cause) {
        if (mCallbacks != null) {
            mCallbacks.onGoogleApiConnectionSuspended(cause);
        }
    }

    @Override
    public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
        if (mCallbacks != null) {
            mCallbacks.onGoogleApiConnectionFailed(connectionResult);
        }
    }
}