package com.netfluke.sergey.catpicker;

import android.app.Fragment;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.graphics.drawable.GradientDrawable;
import android.media.AudioManager;
import android.media.SoundPool;
import android.os.Build;
import android.os.PersistableBundle;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;

import java.util.List;

/**
 *   This example is different from the Android Guide's
 *        https://developer.android.com/training/basics/fragments/index.html
 *     in that the intent here is to programmatically present different views in
 *     different screen orientations: one fragment in portrait, two side-by-side in landscape.
 *
 *   In this example, fragments are added programmatically, via FragmentTransactions and
 *     inflation; they are not included in any XML layouts and must be manually inflated
 *     and attached.
 *
 *   Fragments also could be added statically via XML, see CatPickerStatic.
 *
 *   Given that fragments are created programmatically, we must inhibit automatic
 *     restoration of Views on flip, because after the flip we need a _different_ set of Views.
 *     Hence onSaveInstanceState(..) and onRestoreInstanceState(..), which normally
 *     automatically save and restore the views hierarchy, are overridden, so that the calls
 *     to their super's methods are suppressed. Then the only views created on a flip are
 *     the ones we create ourselves, programmatically. Not overriding these functions leads
 *     to duplication and accumulation of fragments not visible on the screen. Check this
 *     with  "adb shell dumpsys activity com.netfluke.sergey.catpicker"  and look for
 *     fragments reported.
 *
 *   So in this code is all fragments and views are destroyed on flip,
 *     and nothing gets restored automatically; our code is in full control.
 *     We thus rely on the activity getting destroyed on flip, and the only knowledge
 *     of its prior Views being contained in the savedInstanceState bundle passed to onCreate(..),
 *     which we suppress. See CatPickerWithConfigChange for how it could work when
 *     activities and fragments are _not_ destroyed on flip.
 */

/* NOTE: I started out using tags in FragmentTransaction replace() when adding fragments.
           I thought to use these tags to retrieve these fragments when I needed them,
            to update their contents or to remove them. I believed that findFragmentByTag(..)
            would give me the active visible fragment with that tag. That way I would not
            need to keep extra global references to fragments.

         Unfortunately, this failed. I would occasionally get fragments that have already
            been removed and were waiting to be destroyed, rather than the added visible
            fragment added under the same tag. I confirmed it by running
               "adb shell dumpsys activity com.netfluke.sergey.catpicker"; the output
            showed fragments with mRemoving=true mAdded=false alongside the visible
            fragments. Apparently, findFragmentByTag(..) returned these.

         I tried forcefully committing pending transactions with
               getFragmentManager().executePendingTransactions();
            but it did not eliminate the removed fragments from findFragmentByTag's output.
            See also the code snippet marked with "debug", which shows this is the case.

         Hence I opted to track active fragments explicitly in private class members; I left
            tags and code to show occasional defunct fragments in findFragmentByTag(..) results.
 */

public class MainActivity extends AppCompatActivity {

    private int catId;

    // Always start with the cat chosen at the previous run, even
    //   after the app has been quit.
    private SharedPreferences prefs;

    // keep the fragments explicitly for onChangeConfiguration
    private ImageFragment im;   // full screen or null in Portrait, always present in Landscape
    private PickerFragment pf;  // there can be only one, but can go on backstack in Portrait mode

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        Log.d("MAIN", "onCreate( " +
                ((savedInstanceState == null) ? "null" : savedInstanceState.toString() + " )"));

        // extract saved preferences
        prefs = getPreferences(Context.MODE_PRIVATE);
        catId = prefs.getInt("CAT_ID", R.drawable.pusheen1);

        // manually attach all the fragments we need, based on orientation
        attachFragments();
    }

    private boolean isPortrait()
    {
        Configuration o = getResources().getConfiguration();
        return o.orientation == Configuration.ORIENTATION_PORTRAIT;
    }

    private void attachFragments()
    {

        FragmentManager fmgr = getFragmentManager();

        if( isPortrait() ) {

            setContentView(R.layout.activity_portrait);

            // create and add a Picker (vertical buttons)
            pf = new PickerFragment();

            FragmentTransaction t = fmgr.beginTransaction();
            t.replace(R.id.content_frame, pf, "picker");
            t.commit();
        }
        else {  // landscape

            setContentView(R.layout.activity_landscape);

            // add both new picker and image
            FragmentTransaction t = fmgr.beginTransaction();
            pf = new PickerFragment();
            im = new ImageFragment();

            im.catId = catId;

            t.replace(R.id.content_frameA, pf, "picker");
            t.replace(R.id.content_frameB, im, "image");
            t.commit();

            // this sometimes runs *before* onCreateView of the fragment,
            //    so looking up image by id in loadPicture(..) fails to find it:
            //
            // im.loadPicture(catId);


            //  Calling
            //      fmgr.executePendingTransactions();
            //  right after commit does not actually help: although the
            //    ImageFragment gets attached right away, views would still take
            //    some time to load and may still be absent when looked for.
            //
            //  By contrast, setting the property instead and having the fragment
            //    load the picture when its views are ready in onResume() worked.
        }

    }

    @Override
    protected void onPause() {
        super.onPause();
        // save the cat's state, to start with the same cat when app is next launched in landscape
        SharedPreferences.Editor ed = prefs.edit();
        ed.putInt("CAT_ID", catId);
        ed.commit();
    }

    /**
     *    Calls to super purposefully disabled. We don't want the Views saved
     *       and restored, we want to re-create them manually. Using these super calls
     *       results in fragment duplication (see that with
     *         "abd shell dumpsys activity <package name>").
     *    So we save only what we want to save. Cf.
     *    https://www.intertech.com/Blog/saving-and-retrieving-android-instance-state-part-1/
     *    https://www.intertech.com/Blog/saving-and-retrieving-android-instance-state-part-2/
     *
     *  Compare the contents of the savedInstanceState bundle
     *     on entry to onCreate(..) when these functions are and are not called.
     */
    @Override
    public void onSaveInstanceState(Bundle outState) {
        //super.onSaveInstanceState(outState);
        Log.d("STATE", "onSaveInstanceState");

        outState.putInt("CAT_ID", catId);
    }

    // See above.
    @Override
    protected void onRestoreInstanceState(Bundle savedInstanceState) {
        //super.onRestoreInstanceState(savedInstanceState);
        Log.d("STATE", "onRestoreInstanceState" + savedInstanceState.toString());

        catId = savedInstanceState.getInt("CAT_ID");
    }


    /* This function is called in response to all three button clicks in Picker.
     *   It finds out which button was clicked by checking the ID of the View
     *   passed in, i.e., the View that accepted the click.
     */
    public void click(View v) {

        // which button?
        switch (v.getId()) {
            case R.id.button1:
                catId = R.drawable.manul1;
                break;
            case R.id.button2:
                catId = R.drawable.manul2;
                break;
            case R.id.button3:
                catId = R.drawable.manul3;
                break;
            default:
                Log.d("MAIN", "no such cat");
                catId = R.drawable.pusheen1;
        }

        // debug: Finding fragment by tag will occasionally return a fragement
        //   already removed, and waiting to be destroyed. Thus finding fragments
        //   by tag is not always useful: it may return a "dead" fragment detached
        //   from an activity; a live fragment with the same tag could be hidden.
        //   Hence this activity keeps explicit references to it fragments.
        Fragment imgByTag = getFragmentManager().findFragmentByTag("image");
        if (imgByTag != null && imgByTag.isRemoving() ) {
            Log.d("FRAG", "ImageFragment retrieved by tag is in removing stage");
        }
        // end debug

        // We want to do different things in Portrait and Landscape:
        //   portrait:  push PickerFragment on backstack, replace content frame with new ImageFragment
        //   landscape: set image in existing ImageFragment
        if (isPortrait()) {
            // there should be no active ImageFragment
            //   im will retain a pointer to a previous one called from Picker, though.

            im = new ImageFragment();
            im.catId = catId;

            FragmentTransaction t = getFragmentManager().beginTransaction();
            t.replace(R.id.content_frame, im, "image");

            // but keep the obscured PickerFragment on the backstack, so that
            //  the "Back" button brings us back to it (so we can select more cats!)
            t.addToBackStack(null);

            t.commit();

        } else {  // Landscape . Image fragment should exist and be visible
            if (im == null)
                Log.d("MAIN", "no image fragment in Landscape!");
            else if (im.isVisible())
                im.loadPicture(catId);
            else {
                Log.d("MAIN", "image fragment is not visible. This should not happen.");
            }
        }
    }
}
