# Copyright 2018 - 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.
r"""Create args.

Defines the create arg parser that holds create specific args.
"""

import argparse
import logging
import os
import posixpath as remote_path

from acloud import errors
from acloud.create import create_common
from acloud.internal import constants
from acloud.internal.lib import utils

logger = logging.getLogger(__name__)
_DEFAULT_GPU = "default"
CMD_CREATE = "create"


# TODO: Add this into main create args once create_cf/gf is deprecated.
# pylint: disable=too-many-statements
def AddCommonCreateArgs(parser):
    """Adds arguments common to create parsers.

    Args:
        parser: ArgumentParser object, used to parse flags.
    """
    parser.add_argument(
        "--num",
        type=int,
        dest="num",
        required=False,
        default=1,
        help="Number of instances to create.")
    parser.add_argument(
        "--serial-log-file",
        type=str,
        dest="serial_log_file",
        required=False,
        help="Path to a *tar.gz file where serial logs will be saved "
             "when a device fails on boot.")
    parser.add_argument(
        "--autoconnect",
        type=str,
        nargs="?",
        const=constants.INS_KEY_WEBRTC,
        dest="autoconnect",
        required=False,
        choices=[constants.INS_KEY_VNC, constants.INS_KEY_ADB,
                 constants.INS_KEY_WEBRTC],
        help="Determines to establish a tunnel forwarding adb/vnc and "
             "launch VNC/webrtc. Establish a tunnel forwarding adb and vnc "
             "then launch vnc if --autoconnect vnc is provided. Establish a "
             "tunnel forwarding adb if --autoconnect adb is provided. "
             "Establish a tunnel forwarding adb and auto-launch on the browser "
             "if --autoconnect webrtc is provided. For local goldfish "
             "instance, create a window.")
    parser.add_argument(
        "--no-autoconnect",
        action="store_false",
        dest="autoconnect",
        required=False,
        help="Will not automatically create ssh tunnels forwarding adb & vnc "
             "when instance created.")
    parser.set_defaults(autoconnect=constants.INS_KEY_WEBRTC)
    parser.add_argument(
        "--unlock",
        action="store_true",
        dest="unlock_screen",
        required=False,
        default=False,
        help="This can unlock screen after invoke vnc client.")
    parser.add_argument(
        "--report-internal-ip",
        action="store_true",
        dest="report_internal_ip",
        required=False,
        help="Report internal ip of the created instance instead of external "
             "ip. Using the internal ip is used when connecting from another "
             "GCE instance.")
    parser.add_argument(
        "--disable-external-ip",
        action="store_true",
        dest="disable_external_ip",
        required=False,
        help="Disable the external ip of the created instance.")
    parser.add_argument(
        "--extra-files",
        nargs='+',
        type=str,
        dest="extra_files",
        required=False,
        help="Upload the extra files into GCE instance. e.g. "
             "/path/to/file_in_local,/path/to/file_in_gce")
    parser.add_argument(
        "--network",
        type=str,
        dest="network",
        required=False,
        help="Set the network the GCE instance will utilize.")
    parser.add_argument(
        "--skip-pre-run-check",
        action="store_true",
        dest="skip_pre_run_check",
        required=False,
        help="Skip the pre-run check.")
    parser.add_argument(
        "--force-sync",
        action="store_true",
        dest="force_sync",
        required=False,
        help="Force to sync image files from Android Build servers even if "
             "they are already existed for local instance mode.")
    parser.add_argument(
        "--boot-timeout",
        dest="boot_timeout_secs",
        type=int,
        required=False,
        help="The maximum time in seconds used to wait for the AVD to download "
             "artifacts and boot.")
    parser.add_argument(
        "--wait-for-ins-stable",
        dest="ins_timeout_secs",
        type=int,
        required=False,
        help="The maximum time in seconds used to wait for the instance boot "
             "up. The default value to wait for instance up time is 300 secs.")
    parser.add_argument(
        "--build-target",
        type=str,
        dest="build_target",
        help="Android build target, e.g. aosp_cf_x86_64_phone-userdebug, "
             "or short names: phone, tablet, or tablet_mobile.")
    parser.add_argument(
        "--branch",
        type=str,
        dest="branch",
        help="Android branch, e.g. mnc-dev or git_mnc-dev")
    parser.add_argument(
        "--build-id",
        type=str,
        dest="build_id",
        help="Android build id, e.g. 2145099, P2804227")
    parser.add_argument(
        "--bootloader-branch",
        type=str,
        dest="bootloader_branch",
        help="'cuttlefish only' Branch to consume the bootloader from.",
        required=False)
    parser.add_argument(
        "--bootloader-build-id",
        type=str,
        dest="bootloader_build_id",
        help="'cuttlefish only' Bootloader build id, e.g. P2804227",
        required=False)
    parser.add_argument(
        "--bootloader-build-target",
        type=str,
        dest="bootloader_build_target",
        help="'cuttlefish only' Bootloader build target.",
        required=False)
    parser.add_argument(
        "--android-efi-loader-build-id",
        type=str,
        dest="android_efi_loader_build_id",
        help="'cuttlefish only' Android EFI loader build id, e.g. P2804227",
        required=False)
    parser.add_argument(
        "--android-efi-loader-artifact",
        type=str,
        dest="android_efi_loader_artifact",
        help="'cuttlefish only' Android EFI loader artifact name, e.g. gbl_aarch64.efi",
        required=False)
    parser.add_argument(
        "--kernel-build-id",
        type=str,
        dest="kernel_build_id",
        required=False,
        help="Android kernel build id, e.g. 4586590. This is to test a new"
        " kernel build with a particular Android build (--build-id). If neither"
        " kernel-branch nor kernel-build-id are specified, the kernel that's"
        " bundled with the Android build would be used.")
    parser.add_argument(
        "--kernel-branch",
        type=str,
        dest="kernel_branch",
        required=False,
        help="Android kernel build branch name, e.g."
        " kernel-common-android-4.14. This is to test a new kernel build with a"
        " particular Android build (--build-id). If specified without"
        " specifying kernel-build-id, the last green build in the branch will"
        " be used. If neither kernel-branch nor kernel-build-id are specified,"
        " the kernel that's bundled with the Android build would be used.")
    parser.add_argument(
        "--kernel-build-target",
        type=str,
        dest="kernel_build_target",
        default="kernel",
        help="Kernel build target, specify if different from 'kernel'")
    parser.add_argument(
        "--boot-build-id",
        type=str,
        dest="boot_build_id",
        required=False,
        help="Boot image build ID, e.g., 8747889, 8748012.")
    parser.add_argument(
        "--boot-branch",
        type=str,
        dest="boot_branch",
        required=False,
        help="Boot image branch, e.g., aosp-gki13-boot-release, aosp-master.")
    parser.add_argument(
        "--boot-build-target",
        type=str,
        dest="boot_build_target",
        required=False,
        help="Boot image build target, "
        "e.g., gki_x86_64-userdebug, aosp_cf_x86_64_phone-userdebug.")
    parser.add_argument(
        "--boot-artifact",
        type=str,
        dest="boot_artifact",
        required=False,
        help="The name of the boot image to be retrieved from Android build, "
        "e.g., boot-5.10.img, boot.img.")
    parser.add_argument(
        "--ota-branch",
        type=str,
        dest="ota_branch",
        required=False,
        help="'cuttlefish only' OTA tools branch name. e.g. aosp-master")
    parser.add_argument(
        "--ota-build-id",
        type=str,
        dest="ota_build_id",
        required=False,
        help="'cuttlefish only' OTA tools build id, e.g. 2145099, P2804227")
    parser.add_argument(
        "--ota-build-target",
        type=str,
        dest="ota_build_target",
        required=False,
        help="'cuttlefish only' OTA tools build target, e.g. "
        "cf_x86_64_phone-userdebug.")
    parser.add_argument(
        "--host-package-branch", "--host_package_branch",
        type=str,
        dest="host_package_branch",
        required=False,
        help="'cuttlefish and trusty only' Host package branch name. e.g. "
        "aosp-main")
    parser.add_argument(
        "--host-package-build-id", "--host_package_build_id",
        type=str,
        dest="host_package_build_id",
        required=False,
        help="'cuttlefish and trusty only' Host package build id, e.g. "
        "2145099, P2804227")
    parser.add_argument(
        "--host-package-build-target", "--host_package_build_target",
        type=str,
        dest="host_package_build_target",
        required=False,
        help="'cuttlefish and trusty only' Host package build target, e.g. "
        "cf_x86_64_phone-userdebug.")
    parser.add_argument(
        "--system-branch",
        type=str,
        dest="system_branch",
        help="'cuttlefish only' Branch to consume the system image (system.img) "
        "from, will default to what is defined by --branch. "
        "That feature allows to (automatically) test various combinations "
        "of vendor.img (CF, e.g.) and system images (GSI, e.g.). ",
        required=False)
    parser.add_argument(
        "--system-build-id",
        type=str,
        dest="system_build_id",
        help="'cuttlefish only' System image build id, e.g. 2145099, P2804227",
        required=False)
    parser.add_argument(
        "--system-build-target",
        type=str,
        dest="system_build_target",
        help="'cuttlefish only' System image build target, specify if different "
        "from --build-target",
        required=False)
    parser.add_argument(
        "--launch-args",
        type=str,
        dest="launch_args",
        help="'cuttlefish only' Add extra args to launch_cvd command.",
        required=False)
    parser.add_argument(
        "--pet-name",
        "--webrtc_device_id",
        type=str,
        dest="webrtc_device_id",
        help="'cuttlefish only' Give the pet name of the instance.",
        required=False)
    parser.add_argument(
        "--gce-metadata",
        type=str,
        dest="gce_metadata",
        default=None,
        help="'GCE instance only' Record data into GCE instance metadata with "
        "key-value pair format. e.g. id:12,name:unknown.")
    parser.add_argument(
        "--fetch_cvd-build-id",
        type=str,
        dest="fetch_cvd_build_id",
        required=False,
        help="'cuttlefish only' Build id of fetch_cvd, e.g. 2145099, P2804227")
    # TODO(146314062): Remove --multi-stage-launch after infra don't use this
    # args.
    parser.add_argument(
        "--multi-stage-launch",
        dest="multi_stage_launch",
        action="store_true",
        required=False,
        default=True,
        help="Enable the multi-stage cuttlefish launch.")
    parser.add_argument(
        "--no-multi-stage-launch",
        dest="multi_stage_launch",
        action="store_false",
        required=False,
        default=None,
        help="Disable the multi-stage cuttlefish launch.")
    parser.add_argument(
        "--no-pull-log",
        dest="no_pull_log",
        action="store_true",
        required=False,
        default=None,
        help="Disable auto download logs when AVD booting up failed.")
    parser.add_argument(
        "--no-mkcert",
        dest="mkcert",
        action="store_false",
        required=False,
        default=True,
        help="Disable mkcert setup process on the host.")
    # TODO(147335651): Add gpu in user config.
    # TODO(147335651): Support "--gpu" without giving any value.
    parser.add_argument(
        "--gpu",
        type=str,
        const=_DEFAULT_GPU,
        nargs="?",
        dest="gpu",
        required=False,
        default=None,
        help="GPU accelerator to use if any. e.g. nvidia-tesla-k80. For local "
             "instances, this arg without assigning any value is to enable "
             "local gpu support.")
    parser.add_argument(
        "--num-avds-per-instance",
        "--num-instances",
        "--num_instances",
        type=int,
        dest="num_avds_per_instance",
        required=False,
        default=1,
        help="'cuttlefish only' Create multiple cuttlefish AVDs in one local "
             "instance.")
    parser.add_argument(
        "--connect-hostname",
        action="store_true",
        dest="connect_hostname",
        required=False,
        default=False,
        help="Ssh connects to the GCE instance with hostname.")
    parser.add_argument(
        "--gce-only",
        action="store_true",
        dest="gce_only",
        required=False,
        default=False,
        help="Only create the GCE instance. It won't create virtual devices.")
    # Hide following args for users, it is only used in infra.
    parser.add_argument(
        "--local-instance-dir",
        dest="local_instance_dir",
        required=False,
        help=argparse.SUPPRESS)
    parser.add_argument(
        "--remote-image-dir",
        dest="remote_image_dir",
        required=False,
        # 'cuttlefish remote host only' Upload images and cvd host package to
        # the remote directory instead of the instance's own directory. If the
        # directory has been initialized, acloud ignores the image arguments
        # given by command line and reuses the images in the directory.
        help=argparse.SUPPRESS)
    parser.add_argument(
        "--oxygen",
        action="store_true",
        dest="oxygen",
        required=False,
        help=argparse.SUPPRESS)
    parser.add_argument(
        "--zone",
        type=str,
        dest="zone",
        required=False,
        help=argparse.SUPPRESS)

    # TODO(b/118439885): Old arg formats to support transition, delete when
    # transistion is done.
    parser.add_argument(
        "--serial_log_file",
        type=str,
        dest="serial_log_file",
        required=False,
        help=argparse.SUPPRESS)
    parser.add_argument(
        "--build_id",
        type=str,
        dest="build_id",
        required=False,
        help=argparse.SUPPRESS)
    parser.add_argument(
        "--build_target",
        type=str,
        dest="build_target",
        required=False,
        help=argparse.SUPPRESS)
    parser.add_argument(
        "--system_branch",
        type=str,
        dest="system_branch",
        required=False,
        help=argparse.SUPPRESS)
    parser.add_argument(
        "--system_build_id",
        type=str,
        dest="system_build_id",
        required=False,
        help=argparse.SUPPRESS)
    parser.add_argument(
        "--system_build_target",
        type=str,
        dest="system_build_target",
        required=False,
        help=argparse.SUPPRESS)
    parser.add_argument(
        "--kernel_build_id",
        type=str,
        dest="kernel_build_id",
        required=False,
        help=argparse.SUPPRESS)
    parser.add_argument(
        "--kernel_branch",
        type=str,
        dest="kernel_branch",
        required=False,
        help=argparse.SUPPRESS)
    parser.add_argument(
        "--kernel_build_target",
        type=str,
        dest="kernel_build_target",
        default="kernel",
        help=argparse.SUPPRESS)
    parser.add_argument(
        "--bootloader_branch",
        type=str,
        dest="bootloader_branch",
        help=argparse.SUPPRESS,
        required=False)
    parser.add_argument(
        "--bootloader_build_id",
        type=str,
        dest="bootloader_build_id",
        help=argparse.SUPPRESS,
        required=False)
    parser.add_argument(
        "--bootloader_build_target",
        type=str,
        dest="bootloader_build_target",
        help=argparse.SUPPRESS,
        required=False)
    parser.add_argument(
        "--fetch_cvd_build_id",
        type=str,
        dest="fetch_cvd_build_id",
        help=argparse.SUPPRESS,
        required=False)
    parser.add_argument(
        "--remote-fetch",
        action="store_true",
        dest="remote_fetch",
        required=False,
        default=None,
        help="'cuttlefish only' Fetch artifacts in remote host.")
    parser.add_argument(
        "--fetch-cvd-wrapper",
        dest="fetch_cvd_wrapper",
        type=str,
        required=False,
        help="'cuttlefish only' Fetch artifacts in remote host by a"
        " provided static executable fetch cvd wrapper file. "
        " (Still in experiment, this flag only works on lab hosts"
        " with special setup.)")


def GetCreateArgParser(subparser):
    """Return the create arg parser.

    Args:
       subparser: argparse.ArgumentParser that is attached to main acloud cmd.

    Returns:
        argparse.ArgumentParser with create options defined.
    """
    create_parser = subparser.add_parser(CMD_CREATE)
    create_parser.required = False
    create_parser.set_defaults(which=CMD_CREATE)
    # Use default=None to distinguish remote instance or local. The instance
    # type will be remote if the arg is not provided.
    create_parser.add_argument(
        "--local-instance",
        type=_PositiveInteger,
        const=0,
        metavar="ID",
        nargs="?",
        dest="local_instance",
        required=False,
        help="Create a local AVD instance using the resources associated with "
             "the ID. Choose an unused ID automatically if the value is "
             "not specified (primarily for infra usage).")
    create_parser.add_argument(
        "--adb-port", "-p",
        type=int,
        default=None,
        dest="adb_port",
        required=False,
        help="Specify port for adb forwarding.")
    create_parser.add_argument(
        "--base-instance-num",
        type=int,
        default=None,
        dest="base_instance_num",
        required=False,
        help="'cuttlefish only' The instance number of the created device.")
    create_parser.add_argument(
        "--avd-type",
        type=str,
        dest="avd_type",
        default=constants.TYPE_CF,
        choices=[constants.TYPE_GCE, constants.TYPE_CF, constants.TYPE_GF, constants.TYPE_CHEEPS,
                 constants.TYPE_FVP, constants.TYPE_TRUSTY],
        help="Android Virtual Device type (default %s)." % constants.TYPE_CF)
    create_parser.add_argument(
        "--config", "--flavor",
        type=str,
        dest="flavor",
        help="The device flavor of the AVD (default %s). e.g. phone, tv, foldable."
        % constants.FLAVOR_PHONE)
    create_parser.add_argument(
        "--local-image",
        const=constants.FIND_IN_BUILD_ENV,
        type=str,
        dest="local_image",
        nargs="?",
        required=False,
        help="Use the locally built image for the AVD. Look for the image "
        "artifact in $ANDROID_PRODUCT_OUT if no args value is provided."
        "e.g --local-image or --local-image /path/to/dir or --local-image "
        "/path/to/file")
    create_parser.add_argument(
        "--local-kernel-image", "--local-boot-image",
        const=constants.FIND_IN_BUILD_ENV,
        type=str,
        dest="local_kernel_image",
        nargs="?",
        required=False,
        help="Use the locally built kernel and ramdisk for the AVD. Look "
        "for boot.img, vendor_boot.img, kernel, initramfs.img, etc. if the "
        "argument is a directory. Look for the images in $ANDROID_PRODUCT_OUT "
        "if no argument is provided. e.g., --local-kernel-image, "
        "--local-kernel-image /path/to/dir, or --local-kernel-image "
        "/path/to/boot.img")
    create_parser.add_argument(
        "--local-system-image",
        const=constants.FIND_IN_BUILD_ENV,
        type=str,
        dest="local_system_image",
        nargs="?",
        required=False,
        help="Use the locally built system images for the AVD. Look for the "
        "images in $ANDROID_PRODUCT_OUT if no args value is provided. "
        "e.g., --local-system-image, --local-system-image /path/to/dir, or "
        "--local-system-image /path/to/img")
    create_parser.add_argument(
        "--local-system_dlkm-image",
        const=constants.FIND_IN_BUILD_ENV,
        type=str,
        dest="local_system_dlkm_image",
        nargs="?",
        required=False,
        help="`remote host only` Use the locally built system_dlkm image for "
        "the AVD. Look for the image in $ANDROID_PRODUCT_OUT if no args value "
        "is provided.")
    create_parser.add_argument(
        "--local-vendor-image",
        const=constants.FIND_IN_BUILD_ENV,
        type=str,
        dest="local_vendor_image",
        nargs="?",
        required=False,
        help="'cuttlefish only' Use the locally built vendor images for the "
        "AVD. Look for vendor.img, vendor_dlkm.img, odm.img, and odm_dlkm.img "
        "if the argument is a directory. Look for the images in "
        "$ANDROID_PRODUCT_OUT if no argument is provided. e.g., "
        "--local-vendor-image, or --local-vendor-image /path/to/dir")
    create_parser.add_argument(
        "--local-vendor_boot-image", "--local-vendor-boot-image",
        const=constants.FIND_IN_BUILD_ENV,
        type=str,
        dest="local_vendor_boot_image",
        nargs="?",
        required=False,
        help="'cuttlefish only' Use the locally built vendor boot image for "
        "the AVD. Look for the vendor_boot.img in $ANDROID_PRODUCT_OUT "
        "if no argument is provided. e.g., --local-vendor-boot-image, or "
        "--local-vendor-boot-image /path/to/dir, or "
        "--local-vendor-boot-image /path/to/img")
    create_parser.add_argument(
        "--local-tool",
        type=str,
        dest="local_tool",
        action="append",
        default=[],
        required=False,
        help="Use the tools in the specified directory to create local "
        "instances. The directory structure follows $ANDROID_SOONG_HOST_OUT "
        "or $ANDROID_EMULATOR_PREBUILTS.")
    create_parser.add_argument(
        "--cvd-host-package",
        type=str,
        dest="cvd_host_package",
        required=False,
        help="Use the specified path of the cvd host package to create "
        "instances. e.g. /path/cvd-host_package_v1.tar.gz")
    create_parser.add_argument(
        "--image-download-dir",
        type=str,
        dest="image_download_dir",
        required=False,
        help="Define remote image download directory, e.g. /usr/local/dl.")
    create_parser.add_argument(
        "--yes", "-y",
        action="store_true",
        dest="no_prompt",
        required=False,
        help=("Automatic yes to prompts. Assume 'yes' as answer to all prompts "
              "and run non-interactively."))
    create_parser.add_argument(
        "--reuse-gce",
        type=str,
        const=constants.SELECT_ONE_GCE_INSTANCE,
        nargs="?",
        dest="reuse_gce",
        required=False,
        help="'cuttlefish only' This can help users use their own instance. "
        "Reusing specific gce instance if --reuse-gce [instance_name] is "
        "provided. Select one gce instance to reuse if --reuse-gce is "
        "provided.")
    create_parser.add_argument(
        "--openwrt",
        action="store_true",
        dest="openwrt",
        required=False,
        help="'cuttlefish only' Create OpenWrt device when launching cuttlefish "
        "device.")
    create_parser.add_argument(
        "--use-launch_cvd",
        action="store_true",
        dest="use_launch_cvd",
        required=False,
        help="'cuttlefish only' Use launch_cvd to create cuttlefish devices.")
    create_parser.add_argument(
        "--host",
        type=str,
        dest="remote_host",
        default=None,
        help="'cuttlefish only' Provide host name to clean up the remote host. "
        "For example: '--host 1.1.1.1'")
    create_parser.add_argument(
        "--host-user",
        type=str,
        dest="host_user",
        default=constants.GCE_USER,
        help="'remote host only' Provide host user for logging in to the host. "
        "The default value is vsoc-01. For example: '--host 1.1.1.1 --host-user "
        "vsoc-02'")
    create_parser.add_argument(
        "--host-ssh-private-key-path",
        type=str,
        dest="host_ssh_private_key_path",
        default=None,
        help="'remote host only' Provide host key for login on on this host.")
    # User should not specify --spec and --hw_property at the same time.
    hw_spec_group = create_parser.add_mutually_exclusive_group()
    hw_spec_group.add_argument(
        "--hw-property",
        type=str,
        dest="hw_property",
        required=False,
        help="Supported HW properties and example values: %s" %
        constants.HW_PROPERTIES_CMD_EXAMPLE)
    hw_spec_group.add_argument(
        "--spec",
        type=str,
        dest="spec",
        required=False,
        choices=constants.SPEC_NAMES,
        help="The name of a pre-configured device spec that we are "
        "going to use.")
    create_parser.add_argument(
        "--disk-type",
        type=str,
        dest="disk_type",
        required=False,
        help="This is used to customize the GCE instance disk type, the "
        "default disk type is from the stable host image. Use pd-ssd or "
        "pd-standard to specify instance disk type.")
    create_parser.add_argument(
        "--stable-host-image-name",
        type=str,
        dest="stable_host_image_name",
        required=False,
        default=None,
        help=("'cuttlefish only' The Cuttlefish host image from which instances "
              "are launched. If specified here, the value set in Acloud config "
              "file will be overridden."))

    # Arguments for goldfish type.
    create_parser.add_argument(
        "--emulator-build-id",
        type=str,
        dest="emulator_build_id",
        required=False,
        help="'goldfish only' Emulator build ID used to run the images. "
        "e.g. 4669466.")
    create_parser.add_argument(
        "--emulator-build-target",
        dest="emulator_build_target",
        required=False,
        help="'goldfish remote host only' Emulator build target used to run "
        "the images. e.g. emulator-linux_x64_nolocationui.")
    create_parser.add_argument(
        "--emulator-zip",
        dest="emulator_zip",
        required=False,
        help="'goldfish remote host only' Emulator zip used to run the "
        "images. e.g., /path/sdk-repo-linux-emulator-1234567.zip.")

    # Arguments for cheeps type.
    create_parser.add_argument(
        "--stable-cheeps-host-image-name",
        type=str,
        dest="stable_cheeps_host_image_name",
        required=False,
        default=None,
        help=("'cheeps only' The Cheeps host image from which instances are "
              "launched. If specified here, the value set in Acloud config "
              "file will be overridden."))
    create_parser.add_argument(
        "--stable-cheeps-host-image-project",
        type=str,
        dest="stable_cheeps_host_image_project",
        required=False,
        default=None,
        help=("'cheeps only' The project hosting the specified Cheeps host "
              "image. If specified here, the value set in Acloud config file "
              "will be overridden."))
    create_parser.add_argument(
        "--user",
        type=str,
        dest="username",
        required=False,
        default=None,
        help="'cheeps only' username to log in to Chrome OS as.")
    create_parser.add_argument(
        "--password",
        type=str,
        dest="password",
        required=False,
        default=None,
        help="'cheeps only' password to log in to Chrome OS with.")
    create_parser.add_argument(
        "--betty-image",
        type=str,
        dest="cheeps_betty_image",
        required=False,
        default=None,
        help=("'cheeps only' The L1 betty version to use. Only makes sense "
              "when launching a controller image with "
              "stable-cheeps-host-image"))
    create_parser.add_argument(
        "--cheeps-feature",
        type=str,
        dest="cheeps_features",
        required=False,
        action="append",
        default=[],
        help=("'cheeps only' Cheeps feature to enable. Can be repeated."))

    # Arguments for trusty type
    create_parser.add_argument(
        "--trusty-host-package",
        type=str,
        dest="trusty_host_package",
        required=False,
        help="Use the specified path of the trusty host package to create "
        "instances. e.g. /path/trusty-host_package.tar.gz")
    create_parser.add_argument(
        "--local-trusty-image",
        type=str,
        dest="local_trusty_image",
        required=False,
        help="'trusty only' Use the specified path for the locally built "
        "trusty emulator images package, built with "
        "PACKAGE_TRUSTY_IMAGE_TARBALL=true in the Trusty build. E.g., "
        "/path/trusty_image_package.tar.gz")
    create_parser.add_argument(
        "--trusty-build-id",
        type=str,
        dest="trusty_build_id",
        required=False,
        help="Trusty image package build ID, e.g., 8747889, 8748012.")
    create_parser.add_argument(
        "--trusty-branch",
        type=str,
        dest="trusty_branch",
        required=False,
        help="Trusty image package branch, e.g., aosp-trusty-master.")
    create_parser.add_argument(
        "--trusty-build-target",
        type=str,
        dest="trusty_build_target",
        required=False,
        help="Trusty image package build target, "
        "e.g., qemu_generic_arm64_test_debug.")

    AddCommonCreateArgs(create_parser)
    return create_parser


def _PositiveInteger(arg):
    """Convert an argument from a string to a positive integer."""
    try:
        value = int(arg)
    except ValueError as e:
        raise argparse.ArgumentTypeError(arg + " is not an integer.") from e
    if value <= 0:
        raise argparse.ArgumentTypeError(arg + " is not positive.")
    return value


def _VerifyLocalArgs(args):
    """Verify args starting with --local.

    Args:
        args: Namespace object from argparse.parse_args.

    Raises:
        errors.CheckPathError: Image path doesn't exist.
        errors.UnsupportedCreateArgs: The specified avd type does not support
                                      a provided argument.
        errors.UnsupportedLocalInstanceId: Local instance ID is invalid.
    """
    if args.local_image and not os.path.exists(args.local_image):
        raise errors.CheckPathError(
            "Specified path doesn't exist: %s" % args.local_image)

    if args.local_instance_dir and not os.path.exists(args.local_instance_dir):
        raise errors.CheckPathError(
            "Specified path doesn't exist: %s" % args.local_instance_dir)

    if not (args.local_system_image is None or
            args.avd_type in (constants.TYPE_CF, constants.TYPE_GF)):
        raise errors.UnsupportedCreateArgs("%s instance does not support "
                                           "--local-system-image" %
                                           args.avd_type)
    # TODO(b/179340595): To support local image remote instance with kernel build.
    if args.local_instance is None and args.local_image is not None and (
            args.kernel_branch or args.kernel_build_id):
        raise errors.UnsupportedCreateArgs(
            "Acloud didn't support local image with specific kernel. "
            "Please download the specific kernel and put it into "
            "your local image folder: '%s'." % (
            args.local_image if args.local_image else
            utils.GetBuildEnvironmentVariable(constants.ENV_ANDROID_PRODUCT_OUT)))

    if (args.local_system_image and
            not os.path.exists(args.local_system_image)):
        raise errors.CheckPathError(
            "Specified path doesn't exist: %s" % args.local_system_image)

    for tool_dir in args.local_tool:
        if not os.path.exists(tool_dir):
            raise errors.CheckPathError(
                "Specified path doesn't exist: %s" % tool_dir)


def _VerifyHostArgs(args):
    """Verify args starting with --host.

    Args:
        args: Namespace object from argparse.parse_args.

    Raises:
        errors.UnsupportedCreateArgs: When a create arg is specified but
                                      unsupported for remote host mode.
    """
    if args.remote_host and args.local_instance is not None:
        raise errors.UnsupportedCreateArgs(
            "--host is not supported for local instance.")

    if args.remote_host and args.num > 1:
        raise errors.UnsupportedCreateArgs(
            "--num is not supported for remote host.")

    if args.host_user != constants.GCE_USER and args.remote_host is None:
        raise errors.UnsupportedCreateArgs(
            "--host-user is only supported for remote host.")

    if args.host_ssh_private_key_path and args.remote_host is None:
        raise errors.UnsupportedCreateArgs(
            "--host-ssh-private-key-path is only supported for remote host.")

    if args.remote_image_dir:
        if args.remote_host is None:
            raise errors.UnsupportedCreateArgs(
                "--remote-image-dir is only supported for remote host.")
        if remote_path.basename(
                remote_path.normpath(args.remote_image_dir)) in ("..", "."):
            raise errors.UnsupportedCreateArgs(
                "--remote-image-dir must not include the working directory.")


def _VerifyGoldfishArgs(args):
    """Verify goldfish args.

    Args:
        args: Namespace object from argparse.parse_args.

    Raises:
        errors.UnsupportedCreateArgs: When a create arg is specified but
                                      unsupported for goldfish.
    """
    goldfish_only_flags = [
        args.emulator_build_id,
        args.emulator_build_target,
        args.emulator_zip
    ]
    if args.avd_type != constants.TYPE_GF and any(goldfish_only_flags):
        raise errors.UnsupportedCreateArgs(
            f"--emulator-* is only valid with avd_type == {constants.TYPE_GF}")

    # Exclude kernel_build_target because the default value isn't empty.
    remote_kernel_flags = [
        args.kernel_build_id,
        args.kernel_branch,
    ]
    if args.avd_type == constants.TYPE_GF and any(remote_kernel_flags):
        raise errors.UnsupportedCreateArgs(
            "--kernel-* is not supported for goldfish.")

    remote_boot_flags = [
        args.boot_build_id,
        args.boot_build_target,
        args.boot_branch,
        args.boot_artifact,
    ]
    if (args.avd_type == constants.TYPE_GF and any(remote_boot_flags) and
            not all(remote_boot_flags)):
        raise errors.UnsupportedCreateArgs(
            "Either none or all of --boot-branch, --boot-build-target, "
            "--boot-build-id, and --boot-artifact must be specified for "
            "goldfish.")

    remote_system_flags = [
        args.system_build_target,
        args.system_build_id,
        args.system_branch,
    ]
    if (args.avd_type == constants.TYPE_GF and any(remote_system_flags) and
            not all(remote_system_flags)):
        raise errors.UnsupportedCreateArgs(
            "Either none or all of --system-branch, --system-build-target, "
            "and --system-build-id must be specified for goldfish.")

    remote_host_only_flags = remote_boot_flags + remote_system_flags
    if args.avd_type == constants.TYPE_GF and args.remote_host is None and any(
            remote_host_only_flags):
        raise errors.UnsupportedCreateArgs(
            "--boot-* and --system-* for goldfish are only supported for "
            "remote host.")


def _VerifyTrustyArgs(args):
    """Verify trusty args.

    Args:
        args: Namespace object from argparse.parse_args.

    Raises:
        errors.UnsupportedCreateArgs: When specified arguments are
                                      unsupported for trusty.
        errors.CheckPathError: A specified local path does not exist.
    """
    if args.avd_type != constants.TYPE_TRUSTY:
        if args.local_trusty_image:
            raise errors.UnsupportedCreateArgs(
                "--local-trusty-image is only valid with "
                f"avd_type == {constants.TYPE_TRUSTY}")
        if args.trusty_host_package:
            raise errors.UnsupportedCreateArgs(
                "--trusty-host-package is only valid with "
                f"avd_type == {constants.TYPE_TRUSTY}")
        # Only check these args if AVD type is Trusty
        return

    for arg_type, unsupported_args in [
        (
            "--boot-*",
            [
                args.boot_build_id,
                args.boot_build_target,
                args.boot_branch,
                args.boot_artifact,
            ],
        ),
        (
            "--bootloader-*",
            [
                args.bootloader_build_id,
                args.bootloader_build_target,
                args.bootloader_branch,
            ],
        ),
        (
            "--android-efi-loader-*",
            [
                args.android_efi_loader_build_id,
                args.android_efi_loader_artifact,
            ],
        ),
        (
            "--ota-*",
            [
                args.ota_branch,
                args.ota_build_target,
                args.ota_build_id,
            ],
        ),
    ]:
        if any(unsupported_args):
            raise errors.UnsupportedCreateArgs(
                f"{arg_type} is not supported for Trusty."
            )

    if args.local_image is None and not args.build_target:
        raise errors.UnsupportedCreateArgs(
            "Trusty android build target not provided and cannot be "
            "auto-detected, use --build-target to specify a build target, "
            "e.g. qemu_trusty_arm64-trunk_staging-userdebug")
    if args.local_trusty_image:
        if not os.path.exists(args.local_trusty_image):
            raise errors.CheckPathError(
                f"Specified path doesn't exist: {args.local_trusty_image}")
    if args.trusty_host_package:
        if not os.path.exists(args.trusty_host_package):
            raise errors.CheckPathError(
                f"Specified path doesn't exist: {args.trusty_host_package}")


def VerifyArgs(args):
    """Verify args.

    Args:
        args: Namespace object from argparse.parse_args.

    Raises:
        errors.UnsupportedMultiAdbPort: multi adb port doesn't support.
        errors.UnsupportedCreateArgs: When a create arg is specified but
                                      unsupported for a particular avd type.
                                      (e.g. --system-build-id for gf)
    """
    # Verify that user specified flavor name is in support list.
    # We don't use argparse's builtin validation because we need to be able to
    # tell when a user doesn't specify a flavor.
    if args.flavor and args.flavor not in constants.ALL_FLAVORS:
        logger.debug("Flavor[%s] isn't in default support list: %s",
                     args.flavor, constants.ALL_FLAVORS)

    if args.avd_type not in (constants.TYPE_CF, constants.TYPE_GF):
        if args.system_branch or args.system_build_id or args.system_build_target:
            raise errors.UnsupportedCreateArgs(
                "--system-* args are not supported for AVD type: %s"
                % args.avd_type)

    if args.num > 1 and args.adb_port:
        raise errors.UnsupportedMultiAdbPort(
            "--adb-port is not supported for multi-devices.")

    if args.num > 1 and args.local_instance is not None:
        raise errors.UnsupportedCreateArgs(
            "--num is not supported for local instance.")

    if args.local_instance is None and args.gpu == _DEFAULT_GPU:
        raise errors.UnsupportedCreateArgs(
            "Please assign one gpu model for GCE instance. Reference: "
            "https://cloud.google.com/compute/docs/gpus")

    if args.adb_port:
        utils.CheckPortFree(args.adb_port)

    hw_properties = create_common.ParseKeyValuePairArgs(args.hw_property)
    for key in hw_properties:
        if key not in constants.HW_PROPERTIES:
            raise errors.InvalidHWPropertyError(
                "[%s] is an invalid hw property, supported values are:%s. "
                % (key, constants.HW_PROPERTIES))

    cheeps_only_flags = [args.stable_cheeps_host_image_name,
                         args.stable_cheeps_host_image_project,
                         args.username,
                         args.password,
                         args.cheeps_betty_image,
                         args.cheeps_features]
    if args.avd_type != constants.TYPE_CHEEPS and any(cheeps_only_flags):
        raise errors.UnsupportedCreateArgs(
            "--stable-cheeps-*, --betty-image, --cheeps-feature, --username "
            "and --password are only valid with avd_type == %s"
            % constants.TYPE_CHEEPS)
    if (args.username or args.password) and not (args.username and args.password):
        raise ValueError("--username and --password must both be set")
    if not args.autoconnect and args.unlock_screen:
        raise ValueError("--no-autoconnect and --unlock couldn't be "
                         "passed in together.")

    _VerifyGoldfishArgs(args)
    _VerifyTrustyArgs(args)
    _VerifyLocalArgs(args)
    _VerifyHostArgs(args)
