/*
 * Copyright (C) 2013 The Android Open Source Project
 *
 * 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.android.settings.applications;

import android.app.Activity;
import android.app.ActivityManager;
import android.app.Fragment;
import android.app.admin.DevicePolicyManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.net.Uri;
import android.os.Bundle;
import android.os.Process;
import android.os.UserHandle;
import android.preference.PreferenceActivity;
import android.text.format.Formatter;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
import com.android.settings.R;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;

import static com.android.settings.Utils.prepareCustomPreferencesList;

public class ProcessStatsDetail extends Fragment implements Button.OnClickListener {
    private static final String TAG = "ProcessStatsDetail";

    public static final int ACTION_FORCE_STOP = 1;

    public static final String EXTRA_ENTRY = "entry";
    public static final String EXTRA_USE_USS = "use_uss";
    public static final String EXTRA_MAX_WEIGHT = "max_weight";
    public static final String EXTRA_TOTAL_TIME = "total_time";

    private PackageManager mPm;
    private DevicePolicyManager mDpm;

    private ProcStatsEntry mEntry;
    private boolean mUseUss;
    private long mMaxWeight;
    private long mTotalTime;

    private View mRootView;
    private TextView mTitleView;
    private ViewGroup mTwoButtonsPanel;
    private Button mForceStopButton;
    private Button mReportButton;
    private ViewGroup mDetailsParent;
    private ViewGroup mServicesParent;

    public static String makePercentString(Resources res, long amount, long total) {
        final double percent = (((double)amount) / total) * 100;
        return res.getString(R.string.percentage, (int) Math.round(percent));
    }

    @Override
    public void onCreate(Bundle icicle) {
        super.onCreate(icicle);
        mPm = getActivity().getPackageManager();
        mDpm = (DevicePolicyManager)getActivity().getSystemService(Context.DEVICE_POLICY_SERVICE);
        final Bundle args = getArguments();
        mEntry = (ProcStatsEntry)args.getParcelable(EXTRA_ENTRY);
        mEntry.retrieveUiData(mPm);
        mUseUss = args.getBoolean(EXTRA_USE_USS);
        mMaxWeight = args.getLong(EXTRA_MAX_WEIGHT);
        mTotalTime = args.getLong(EXTRA_TOTAL_TIME);
    }

    @Override
    public View onCreateView(
            LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        final View view = inflater.inflate(R.layout.process_stats_details, container, false);
        prepareCustomPreferencesList(container, view, view, false);

        mRootView = view;
        createDetails();
        return view;
    }

    @Override
    public void onResume() {
        super.onResume();
        checkForceStop();
    }

    @Override
    public void onPause() {
        super.onPause();
    }

    private void createDetails() {
        final double percentOfWeight = (((double)mEntry.mWeight) / mMaxWeight) * 100;

        int appLevel = (int) Math.ceil(percentOfWeight);
        String appLevelText = makePercentString(getResources(), mEntry.mDuration, mTotalTime);

        // Set all values in the header.
        final TextView summary = (TextView) mRootView.findViewById(android.R.id.summary);
        summary.setText(mEntry.mName);
        summary.setVisibility(View.VISIBLE);
        mTitleView = (TextView) mRootView.findViewById(android.R.id.title);
        mTitleView.setText(mEntry.mUiBaseLabel);
        final TextView text1 = (TextView)mRootView.findViewById(android.R.id.text1);
        text1.setText(appLevelText);
        final ProgressBar progress = (ProgressBar) mRootView.findViewById(android.R.id.progress);
        progress.setProgress(appLevel);
        final ImageView icon = (ImageView) mRootView.findViewById(android.R.id.icon);
        if (mEntry.mUiTargetApp != null) {
            icon.setImageDrawable(mEntry.mUiTargetApp.loadIcon(mPm));
        }

        mTwoButtonsPanel = (ViewGroup)mRootView.findViewById(R.id.two_buttons_panel);
        mForceStopButton = (Button)mRootView.findViewById(R.id.right_button);
        mReportButton = (Button)mRootView.findViewById(R.id.left_button);
        mForceStopButton.setEnabled(false);
        mReportButton.setVisibility(View.INVISIBLE);

        mDetailsParent = (ViewGroup)mRootView.findViewById(R.id.details);
        mServicesParent = (ViewGroup)mRootView.findViewById(R.id.services);

        fillDetailsSection();
        fillServicesSection();

        if (mEntry.mUid >= android.os.Process.FIRST_APPLICATION_UID) {
            mForceStopButton.setText(R.string.force_stop);
            mForceStopButton.setTag(ACTION_FORCE_STOP);
            mForceStopButton.setOnClickListener(this);
            mTwoButtonsPanel.setVisibility(View.VISIBLE);
        } else {
            mTwoButtonsPanel.setVisibility(View.GONE);
        }
    }

    public void onClick(View v) {
        doAction((Integer) v.getTag());
    }

    private void doAction(int action) {
        PreferenceActivity pa = (PreferenceActivity)getActivity();
        switch (action) {
            case ACTION_FORCE_STOP:
                killProcesses();
                break;
        }
    }

    private void addPackageHeaderItem(ViewGroup parent, String packageName) {
        LayoutInflater inflater = getActivity().getLayoutInflater();
        ViewGroup item = (ViewGroup) inflater.inflate(R.layout.running_processes_item,
                null);
        parent.addView(item);
        final ImageView icon = (ImageView) item.findViewById(R.id.icon);
        TextView nameView = (TextView) item.findViewById(R.id.name);
        TextView descriptionView = (TextView) item.findViewById(R.id.description);
        try {
            ApplicationInfo ai = mPm.getApplicationInfo(packageName, 0);
            icon.setImageDrawable(ai.loadIcon(mPm));
            nameView.setText(ai.loadLabel(mPm));
        } catch (PackageManager.NameNotFoundException e) {
        }
        descriptionView.setText(packageName);
    }

    private void addDetailsItem(ViewGroup parent, CharSequence label, CharSequence value) {
        LayoutInflater inflater = getActivity().getLayoutInflater();
        ViewGroup item = (ViewGroup) inflater.inflate(R.layout.power_usage_detail_item_text,
                null);
        parent.addView(item);
        TextView labelView = (TextView) item.findViewById(R.id.label);
        TextView valueView = (TextView) item.findViewById(R.id.value);
        labelView.setText(label);
        valueView.setText(value);
    }

    private void fillDetailsSection() {
        addDetailsItem(mDetailsParent, getResources().getText(R.string.process_stats_avg_ram_use),
                Formatter.formatShortFileSize(getActivity(),
                        (mUseUss ? mEntry.mAvgUss : mEntry.mAvgPss) * 1024));
        addDetailsItem(mDetailsParent, getResources().getText(R.string.process_stats_max_ram_use),
                Formatter.formatShortFileSize(getActivity(),
                        (mUseUss ? mEntry.mMaxUss : mEntry.mMaxPss) * 1024));
        addDetailsItem(mDetailsParent, getResources().getText(R.string.process_stats_run_time),
                makePercentString(getResources(), mEntry.mDuration, mTotalTime));
    }

    final static Comparator<ProcStatsEntry.Service> sServiceCompare
            = new Comparator<ProcStatsEntry.Service>() {
        @Override
        public int compare(ProcStatsEntry.Service lhs, ProcStatsEntry.Service rhs) {
            if (lhs.mDuration < rhs.mDuration) {
                return 1;
            } else if (lhs.mDuration > rhs.mDuration) {
                return -1;
            }
            return 0;
        }
    };

    final static Comparator<ArrayList<ProcStatsEntry.Service>> sServicePkgCompare
            = new Comparator<ArrayList<ProcStatsEntry.Service>>() {
        @Override
        public int compare(ArrayList<ProcStatsEntry.Service> lhs,
                ArrayList<ProcStatsEntry.Service> rhs) {
            long topLhs = lhs.size() > 0 ? lhs.get(0).mDuration : 0;
            long topRhs = rhs.size() > 0 ? rhs.get(0).mDuration : 0;
            if (topLhs < topRhs) {
                return 1;
            } else if (topLhs > topRhs) {
                return -1;
            }
            return 0;
        }
    };

    private void fillServicesSection() {
        if (mEntry.mServices.size() > 0) {
            boolean addPackageSections = false;
            // Sort it all.
            ArrayList<ArrayList<ProcStatsEntry.Service>> servicePkgs
                    = new ArrayList<ArrayList<ProcStatsEntry.Service>>();
            for (int ip=0; ip<mEntry.mServices.size(); ip++) {
                ArrayList<ProcStatsEntry.Service> services =
                        (ArrayList<ProcStatsEntry.Service>)mEntry.mServices.valueAt(ip).clone();
                Collections.sort(services, sServiceCompare);
                servicePkgs.add(services);
            }
            if (mEntry.mServices.size() > 1
                    || !mEntry.mServices.valueAt(0).get(0).mPackage.equals(mEntry.mPackage)) {
                addPackageSections = true;
                // Sort these so that the one(s) with the longest run durations are on top.
                Collections.sort(servicePkgs, sServicePkgCompare);
            }
            for (int ip=0; ip<servicePkgs.size(); ip++) {
                ArrayList<ProcStatsEntry.Service> services = servicePkgs.get(ip);
                if (addPackageSections) {
                    addPackageHeaderItem(mServicesParent, services.get(0).mPackage);
                }
                for (int is=0; is<services.size(); is++) {
                    ProcStatsEntry.Service service = services.get(is);
                    String label = service.mName;
                    int tail = label.lastIndexOf('.');
                    if (tail >= 0 && tail < (label.length()-1)) {
                        label = label.substring(tail+1);
                    }
                    long duration = service.mDuration;
                    final double percentOfTime = (((double)duration) / mTotalTime) * 100;
                    addDetailsItem(mServicesParent, label, getActivity().getResources().getString(
                            R.string.percentage, (int) Math.ceil(percentOfTime)));
                }
            }
        }
    }

    private void killProcesses() {
        ActivityManager am = (ActivityManager)getActivity().getSystemService(
                Context.ACTIVITY_SERVICE);
        am.forceStopPackage(mEntry.mUiPackage);
        checkForceStop();
    }

    private final BroadcastReceiver mCheckKillProcessesReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            mForceStopButton.setEnabled(getResultCode() != Activity.RESULT_CANCELED);
        }
    };

    private void checkForceStop() {
        if (mEntry.mUiPackage == null || mEntry.mUid < Process.FIRST_APPLICATION_UID) {
            mForceStopButton.setEnabled(false);
            return;
        }
        if (mDpm.packageHasActiveAdmins(mEntry.mUiPackage)) {
            mForceStopButton.setEnabled(false);
            return;
        }
        try {
            ApplicationInfo info = mPm.getApplicationInfo(mEntry.mUiPackage, 0);
            if ((info.flags&ApplicationInfo.FLAG_STOPPED) == 0) {
                mForceStopButton.setEnabled(true);
            }
        } catch (PackageManager.NameNotFoundException e) {
        }
        Intent intent = new Intent(Intent.ACTION_QUERY_PACKAGE_RESTART,
                Uri.fromParts("package", mEntry.mUiPackage, null));
        intent.putExtra(Intent.EXTRA_PACKAGES, new String[] { mEntry.mUiPackage });
        intent.putExtra(Intent.EXTRA_UID, mEntry.mUid);
        intent.putExtra(Intent.EXTRA_USER_HANDLE, UserHandle.getUserId(mEntry.mUid));
        getActivity().sendOrderedBroadcast(intent, null, mCheckKillProcessesReceiver, null,
                Activity.RESULT_CANCELED, null, null);
    }
}
