/*
 * Copyright (C) 2016 mc
 *
 * 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.xixicm.ca.presentation.mvp;

import android.os.Bundle;
import android.os.Parcelable;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;

import java.io.Serializable;

/**
 * An Activity that uses an {@link MvpPresenter} to implement a Model-View-Presenter architecture.
 *
 * @author mc
 */
public abstract class MvpActivity<M, V extends MvpView, P extends MvpPresenter<V, M>> extends AppCompatActivity implements MvpView {
    protected P mPresenter;
    private static String VIEW_MODE_KEY;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        VIEW_MODE_KEY = getClass().getCanonicalName() + "$ViewModel";
        onCreatePresenter(savedInstanceState);
        onInitializePresenter(savedInstanceState);
    }

    /**
     * Called in {@link #onCreate} before {@link #onInitializePresenter}
     *
     * @param savedInstanceState
     */
    protected void onCreatePresenter(Bundle savedInstanceState) {
        if (isRetainPresenterByNonConfigurationInstance()) {
            mPresenter = getPresenterFromNonConfigurationInstance();
        }
        if (mPresenter == null) {
            mPresenter = createPresenter();
        }
    }

    /**
     * Called in {@link #onCreate} after {@link #onCreatePresenter}
     *
     * @param savedInstanceState
     */
    protected void onInitializePresenter(Bundle savedInstanceState) {
        if (savedInstanceState == null) {
            mPresenter.attachView((V) this, null);
        } else {
            M viewModel;
            if (mPresenter.getViewModel() != null) {
                // no need to restore from the savedInstanceState, if mPresenter.getViewModel() is not null.
                viewModel = mPresenter.getViewModel();
            } else {
                viewModel = (M) savedInstanceState.get(VIEW_MODE_KEY);
            }
            mPresenter.attachView((V) this, viewModel);
        }
    }

    /**
     * Whether retain {@link #mPresenter} by NonConfigurationInstance.
     * If return true, should override {@link #getPresenterFromNonConfigurationInstance()} to get {@link #mPresenter}.
     * If return false,  {@link #mPresenter} will be destroyed when this activity {@link #onDestroy}
     *
     * @return default false
     */
    protected boolean isRetainPresenterByNonConfigurationInstance() {
        return false;
    }

    /**
     * If {@link #isRetainPresenterByNonConfigurationInstance} return true, should override this function to get {@link #mPresenter}.
     *
     * @return default null
     */
    @Nullable
    protected P getPresenterFromNonConfigurationInstance() {
        return null;
    }


    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        // Even it's isRetainPresenterByNonConfigurationInstance, still save to outState.
        // For runtime permission change or kill process case. RetainNonConfigurationInstance doesn't work.
        M viewModel = mPresenter.getViewModel();
        if (viewModel != null) {
            if (viewModel instanceof Serializable) {
                outState.putSerializable(VIEW_MODE_KEY, (Serializable) viewModel);
            } else if (viewModel instanceof Parcelable) {
                outState.putParcelable(VIEW_MODE_KEY, (Parcelable) viewModel);
            }
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mPresenter.detachView();
        if (!isRetainPresenterByNonConfigurationInstance() || null == getPresenterFromNonConfigurationInstance()) {
            mPresenter.onDestroy();
        } else if (isFinishing()) {
            mPresenter.onDestroy();
        }
    }

    @NonNull
    protected abstract P createPresenter();

    /**
     * Get {@link #mPresenter}. If mPresenter is null, try to get it from {@link #getPresenterFromNonConfigurationInstance} first,
     * if it's till null, create it by {@link #createPresenter}, otherwise return it directly
     * @return
     */
    @NonNull
    public P getPresenter() {
        if (mPresenter == null) {
            // when recreate the whole activity(restore the process), this function will be called from sub fragment
            // before creating the presenter on the onCreate of the activity. Usually getPresenterFromNonConfigurationInstance() is null,
            // but if it's not null, keep to use it first.
            P presenter = getPresenterFromNonConfigurationInstance();
            if (presenter != null) {
                mPresenter = presenter;
            } else {
                mPresenter = createPresenter();
            }
        }
        return mPresenter;
    }
}

@@ -2808,32 +2808,40 @@
 ink #mPresenter%7D
+ if need
 .%0A     * If retu
@@ -2957,20 +2957,19 @@
 default 
-fals
+tru
 e%0A     *
@@ -3055,20 +3055,19 @@
  return 
-fals
+tru
 e;%0A    %7D
@@ -3142,33 +3142,126 @@
 nce%7D return true
-,
+ and %7B@link #getLastCustomNonConfigurationInstance%7D doesn't return mPresenter directly,%0A     *
  should override
@@ -3325,23 +3325,38 @@
 @return 
-default
+%7B@link #mPresenter%7D or
  null%0A  
@@ -3448,27 +3448,572 @@
     
-return null;%0A    %7D%0A
+if (mPresenter != null) %7B%0A            // this only happens when getPresenter() is called before MvpActivity#onCreatePresenter%0A            return mPresenter;%0A        %7D%0A        Object o = getLastCustomNonConfigurationInstance();%0A        if (o instanceof MvpPresenter) %7B%0A            //noinspection unchecked%0A            return (P) o;%0A        %7D%0A        return null;%0A    %7D%0A%0A    /**%0A     * Default to return the %7B@link #mPresenter%7D.%0A     *%0A     * @return%0A     */%0A    @Override%0A    public Object onRetainCustomNonConfigurationInstance() %7B%0A        return mPresenter;%0A    %7D
 %0A%0A  
@@ -5347,16 +5347,23 @@
 irectly%0A
+     *%0A
      * @

change default presenter retain behavior