/*
**
** Copyright 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.commands.wm;

import android.content.Context;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
import android.util.AndroidException;
import android.util.DisplayMetrics;
import android.system.Os;
import android.view.Display;
import android.view.IWindowManager;
import com.android.internal.os.BaseCommand;

import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.DataInputStream;
import java.io.PrintStream;
import java.lang.Runtime;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Wm extends BaseCommand {

    private IWindowManager mWm;

    /**
     * Command-line entry point.
     *
     * @param args The command-line arguments
     */
    public static void main(String[] args) {
        (new Wm()).run(args);
    }

    @Override
    public void onShowUsage(PrintStream out) {
        out.println(
                "usage: wm [subcommand] [options]\n" +
                "       wm size [reset|WxH|WdpxHdp]\n" +
                "       wm density [reset|DENSITY]\n" +
                "       wm overscan [reset|LEFT,TOP,RIGHT,BOTTOM]\n" +
                "       wm scaling [off|auto]\n" +
                "       wm screen-capture [userId] [true|false]\n" +
                "\n" +
                "wm size: return or override display size.\n" +
                "         width and height in pixels unless suffixed with 'dp'.\n" +
                "\n" +
                "wm density: override display density.\n" +
                "\n" +
                "wm overscan: set overscan area for display.\n" +
                "\n" +
                "wm scaling: set display scaling mode.\n" +
                "\n" +
                "wm screen-capture: enable/disable screen capture.\n" +
                "\n" +
                "wm dismiss-keyguard: dismiss the keyguard, prompting the user for auth if " +
                "necessary.\n" +
                "\n" +
                "wm surface-trace: log surface commands to stdout in a binary format.\n"
                );
    }

    @Override
    public void onRun() throws Exception {
        mWm = IWindowManager.Stub.asInterface(ServiceManager.checkService(
                        Context.WINDOW_SERVICE));
        if (mWm == null) {
            System.err.println(NO_SYSTEM_ERROR_CODE);
            throw new AndroidException("Can't connect to window manager; is the system running?");
        }

        String op = nextArgRequired();

        if (op.equals("size")) {
            runDisplaySize();
        } else if (op.equals("density")) {
            runDisplayDensity();
        } else if (op.equals("overscan")) {
            runDisplayOverscan();
        } else if (op.equals("scaling")) {
            runDisplayScaling();
        } else if (op.equals("screen-capture")) {
            runSetScreenCapture();
        } else if (op.equals("dismiss-keyguard")) {
            runDismissKeyguard();
        } else if (op.equals("surface-trace")) {
            runSurfaceTrace();
        } else {
            showError("Error: unknown command '" + op + "'");
            return;
        }
    }

    private void runSurfaceTrace() throws Exception {
        ParcelFileDescriptor pfd = ParcelFileDescriptor.dup(FileDescriptor.out);
        mWm.enableSurfaceTrace(pfd);

        try {
            // No one is going to wake us up, we are just waiting on SIGINT. Otherwise
            // the WM can happily continue writing to our stdout.
            synchronized (this) {
                this.wait();
            }
        } finally {
            mWm.disableSurfaceTrace();
        }
    }

    private void runSetScreenCapture() throws Exception {
        String userIdStr = nextArg();
        String enableStr = nextArg();
        int userId;
        boolean disable;

        try {
            userId = Integer.parseInt(userIdStr);
        } catch (NumberFormatException e) {
            System.err.println("Error: bad number " + e);
            return;
        }

        disable = !Boolean.parseBoolean(enableStr);

        try {
            mWm.setScreenCaptureDisabled(userId, disable);
        } catch (RemoteException e) {
            System.err.println("Error: Can't set screen capture " + e);
        }
    }

    private void runDisplaySize() throws Exception {
        String size = nextArg();
        int w, h;
        if (size == null) {
            Point initialSize = new Point();
            Point baseSize = new Point();
            try {
                mWm.getInitialDisplaySize(Display.DEFAULT_DISPLAY, initialSize);
                mWm.getBaseDisplaySize(Display.DEFAULT_DISPLAY, baseSize);
                System.out.println("Physical size: " + initialSize.x + "x" + initialSize.y);
                if (!initialSize.equals(baseSize)) {
                    System.out.println("Override size: " + baseSize.x + "x" + baseSize.y);
                }
            } catch (RemoteException e) {
            }
            return;
        } else if ("reset".equals(size)) {
            w = h = -1;
        } else {
            int div = size.indexOf('x');
            if (div <= 0 || div >= (size.length()-1)) {
                System.err.println("Error: bad size " + size);
                return;
            }
            String wstr = size.substring(0, div);
            String hstr = size.substring(div+1);
            try {
                w = parseDimension(wstr);
                h = parseDimension(hstr);
            } catch (NumberFormatException e) {
                System.err.println("Error: bad number " + e);
                return;
            }
        }

        try {
            if (w >= 0 && h >= 0) {
                // TODO(multidisplay): For now Configuration only applies to main screen.
                mWm.setForcedDisplaySize(Display.DEFAULT_DISPLAY, w, h);
            } else {
                mWm.clearForcedDisplaySize(Display.DEFAULT_DISPLAY);
            }
        } catch (RemoteException e) {
        }
    }

    private void runDisplayDensity() throws Exception {
        String densityStr = nextArg();
        int density;
        if (densityStr == null) {
            try {
                int initialDensity = mWm.getInitialDisplayDensity(Display.DEFAULT_DISPLAY);
                int baseDensity = mWm.getBaseDisplayDensity(Display.DEFAULT_DISPLAY);
                System.out.println("Physical density: " + initialDensity);
                if (initialDensity != baseDensity) {
                    System.out.println("Override density: " + baseDensity);
                }
            } catch (RemoteException e) {
            }
            return;
        } else if ("reset".equals(densityStr)) {
            density = -1;
        } else {
            try {
                density = Integer.parseInt(densityStr);
            } catch (NumberFormatException e) {
                System.err.println("Error: bad number " + e);
                return;
            }
            if (density < 72) {
                System.err.println("Error: density must be >= 72");
                return;
            }
        }

        try {
            if (density > 0) {
                // TODO(multidisplay): For now Configuration only applies to main screen.
                mWm.setForcedDisplayDensityForUser(Display.DEFAULT_DISPLAY, density,
                        UserHandle.USER_CURRENT);
            } else {
                mWm.clearForcedDisplayDensityForUser(Display.DEFAULT_DISPLAY,
                        UserHandle.USER_CURRENT);
            }
        } catch (RemoteException e) {
        }
    }

    private void runDisplayOverscan() throws Exception {
        String overscanStr = nextArgRequired();
        Rect rect = new Rect();
        if ("reset".equals(overscanStr)) {
            rect.set(0, 0, 0, 0);
        } else {
            final Pattern FLATTENED_PATTERN = Pattern.compile(
                    "(-?\\d+),(-?\\d+),(-?\\d+),(-?\\d+)");
            Matcher matcher = FLATTENED_PATTERN.matcher(overscanStr);
            if (!matcher.matches()) {
                System.err.println("Error: bad rectangle arg: " + overscanStr);
                return;
            }
            rect.left = Integer.parseInt(matcher.group(1));
            rect.top = Integer.parseInt(matcher.group(2));
            rect.right = Integer.parseInt(matcher.group(3));
            rect.bottom = Integer.parseInt(matcher.group(4));
        }

        try {
            mWm.setOverscan(Display.DEFAULT_DISPLAY, rect.left, rect.top, rect.right, rect.bottom);
        } catch (RemoteException e) {
        }
    }

    private void runDisplayScaling() throws Exception {
        String scalingStr = nextArgRequired();
        if ("auto".equals(scalingStr)) {
            mWm.setForcedDisplayScalingMode(Display.DEFAULT_DISPLAY, 0);
        } else if ("off".equals(scalingStr)) {
            mWm.setForcedDisplayScalingMode(Display.DEFAULT_DISPLAY, 1);
        } else {
            System.err.println("Error: scaling must be 'auto' or 'off'");
        }
    }

    private void runDismissKeyguard() throws Exception {
        mWm.dismissKeyguard(null /* callback */);
    }

    private int parseDimension(String s) throws NumberFormatException {
        if (s.endsWith("px")) {
            return Integer.parseInt(s.substring(0, s.length() - 2));
        }
        if (s.endsWith("dp")) {
            int density;
            try {
                density = mWm.getBaseDisplayDensity(Display.DEFAULT_DISPLAY);
            } catch (RemoteException e) {
                density = DisplayMetrics.DENSITY_DEFAULT;
            }
            return Integer.parseInt(s.substring(0, s.length() - 2)) * density /
                    DisplayMetrics.DENSITY_DEFAULT;
        }
        return Integer.parseInt(s);
    }
}
