/*
 * Copyright (C) 2011 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.chimpchat.adb;

import com.google.common.collect.Lists;

import com.android.SdkConstants;
import com.android.chimpchat.core.IChimpBackend;
import com.android.chimpchat.core.IChimpDevice;
import com.android.ddmlib.AndroidDebugBridge;
import com.android.ddmlib.IDevice;

import java.io.File;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Pattern;

/**
 * Backend implementation that works over ADB to talk to the device.
 */
public class AdbBackend implements IChimpBackend {
    private static Logger LOG = Logger.getLogger(AdbBackend.class.getCanonicalName());
    // How long to wait each time we check for the device to be connected.
    private static final int CONNECTION_ITERATION_TIMEOUT_MS = 200;
    private final List<IChimpDevice> devices = Lists.newArrayList();
    private final AndroidDebugBridge bridge;
    private final boolean initAdb;

    /**
     * Constructs an AdbBackend with default options.
     */
    public AdbBackend() {
        this(null, false);
    }

    /**
     * Constructs an AdbBackend.
     *
     * @param adbLocation The location of the adb binary. If null, AdbBackend will
     *     attempt to find the binary by itself.
     * @param noInitAdb If true, AdbBackend will not initialize AndroidDebugBridge.
     */
    public AdbBackend(String adbLocation, boolean noInitAdb) {
        this.initAdb = !noInitAdb;

        // [try to] ensure ADB is running
        if (adbLocation == null) {
            adbLocation = findAdb();
        }

        if (initAdb) {
            AndroidDebugBridge.init(false /* debugger support */);
        }

        bridge = AndroidDebugBridge.createBridge(
                adbLocation, true /* forceNewBridge */);
    }

    private String findAdb() {
        String mrParentLocation =
            System.getProperty("com.android.monkeyrunner.bindir"); //$NON-NLS-1$


        // in the new SDK, adb is in the platform-tools, but when run from the command line
        // in the Android source tree, then adb is next to monkeyrunner.
        if (mrParentLocation != null && mrParentLocation.length() != 0) {
            // check if there's a platform-tools folder
            File platformTools = new File(new File(mrParentLocation).getParent(),
                    SdkConstants.FD_PLATFORM_TOOLS);
            if (platformTools.isDirectory()) {
                return platformTools.getAbsolutePath() + File.separator + SdkConstants.FN_ADB;
            }

            return mrParentLocation + File.separator + SdkConstants.FN_ADB;
        }

        return SdkConstants.FN_ADB;
    }

    /**
     * Checks the attached devices looking for one whose device id matches the specified regex.
     *
     * @param deviceIdRegex the regular expression to match against
     * @return the Device (if found), or null (if not found).
     */
    private IDevice findAttachedDevice(String deviceIdRegex) {
        Pattern pattern = Pattern.compile(deviceIdRegex);
        for (IDevice device : bridge.getDevices()) {
            String serialNumber = device.getSerialNumber();
            if (pattern.matcher(serialNumber).matches()) {
                return device;
            }
        }
        return null;
    }

    @Override
    public IChimpDevice waitForConnection() {
        return waitForConnection(Integer.MAX_VALUE, ".*");
    }

    @Override
    public IChimpDevice waitForConnection(long timeoutMs, String deviceIdRegex) {
        do {
            IDevice device = findAttachedDevice(deviceIdRegex);
            // Only return the device when it is online
            if (device != null && device.getState() == IDevice.DeviceState.ONLINE) {
                IChimpDevice chimpDevice = new AdbChimpDevice(device);
                devices.add(chimpDevice);
                return chimpDevice;
            }

            try {
                Thread.sleep(CONNECTION_ITERATION_TIMEOUT_MS);
            } catch (InterruptedException e) {
                LOG.log(Level.SEVERE, "Error sleeping", e);
            }
            timeoutMs -= CONNECTION_ITERATION_TIMEOUT_MS;
        } while (timeoutMs > 0);

        // Timeout.  Give up.
        return null;
    }

    @Override
    public void shutdown() {
        for (IChimpDevice device : devices) {
            device.dispose();
        }
        if (initAdb) {
            AndroidDebugBridge.terminate();
        }
    }
}
