// Path: src/main/java/com/github/xsavikx/androidscreencast/api/file/FileInfo.java
// @Singleton
// public final class FileInfo {
// 
//     public AndroidDeviceImpl device;
//     public String path;
//     public String attribs;
//     public boolean directory;
//     public String name;
// 
//     @Inject
//     public FileInfo() {
//     }
// 
//     public File downloadTemporary() {
//         try {
//             File tempFile = File.createTempFile("android", name);
//             device.pullFile(path + name, tempFile);
//             tempFile.deleteOnExit();
//             return tempFile;
//         } catch (IOException ex) {
//             throw new IORuntimeException(ex);
//         }
//     }
// 
//     @Override
//     public String toString() {
//         return name;
//     }
// }
// 
// Path: src/main/java/com/github/xsavikx/androidscreencast/api/injector/OutputStreamShellOutputReceiver.java
// public final class OutputStreamShellOutputReceiver implements IShellOutputReceiver {
// 
//     private final OutputStream os;
// 
//     public OutputStreamShellOutputReceiver(OutputStream os) {
//         this.os = os;
//     }
// 
//     @Override
//     public void addOutput(byte[] buf, int off, int len) {
//         try {
//             os.write(buf, off, len);
//         } catch (IOException e) {
//             throw new IORuntimeException(e);
//         }
//     }
// 
//     @Override
//     public void flush() {
//         try {
//             os.flush();
//         } catch (IOException e) {
//             throw new IORuntimeException(e);
//         }
//     }
// 
//     @Override
//     public boolean isCancelled() {
//         return false;
//     }
// }
// 
// Path: src/main/java/com/github/xsavikx/androidscreencast/exception/AndroidScreenCastRuntimeException.java
// public class AndroidScreenCastRuntimeException extends RuntimeException {
// 
//     private String additionalInformation;
// 
//     public AndroidScreenCastRuntimeException() {
//     }
// 
//     public AndroidScreenCastRuntimeException(String message) {
//         super(message);
//     }
// 
// 
//     public AndroidScreenCastRuntimeException(String message, String additionalInformation) {
//         super(message);
//         this.additionalInformation = additionalInformation;
//     }
// 
//     public AndroidScreenCastRuntimeException(String message, Throwable cause) {
//         super(message, cause);
//     }
// 
//     public AndroidScreenCastRuntimeException(Throwable cause) {
//         super(cause);
//     }
// 
//     public AndroidScreenCastRuntimeException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
//         super(message, cause, enableSuppression, writableStackTrace);
//     }
// 
//     public String getAdditionalInformation() {
//         return additionalInformation;
//     }
// }
// 
// Path: src/main/java/com/github/xsavikx/androidscreencast/exception/ExecuteCommandException.java
// public final class ExecuteCommandException extends AndroidScreenCastRuntimeException {
// 
//     public ExecuteCommandException(String command) {
//         super(String.format("Cannot execute command '%s'.", command));
//     }
// }


import java.util.ArrayList;
import java.util.List;
import static org.slf4j.LoggerFactory.getLogger;
import com.android.ddmlib.IDevice;
import com.android.ddmlib.SyncService;
import com.github.xsavikx.androidscreencast.api.file.FileInfo;
import com.github.xsavikx.androidscreencast.api.injector.OutputStreamShellOutputReceiver;
import com.github.xsavikx.androidscreencast.exception.AndroidScreenCastRuntimeException;
import com.github.xsavikx.androidscreencast.exception.ExecuteCommandException;
import org.slf4j.Logger;
import javax.inject.Inject;
import javax.inject.Singleton;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.nio.charset.StandardCharsets;
/*
 * Copyright 2020 Yurii Serhiichuk
 *
 * 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.github.xsavikx.androidscreencast.api;




@Singleton
public final class AndroidDeviceImpl implements AndroidDevice {

    private final IDevice device;

    @Inject
    public AndroidDeviceImpl(final IDevice device) {
        this.device = device;
    }

    @Override
    public String executeCommand(final String cmd) {
        log().debug("Executing command: `{}`.", cmd);
        try (final ByteArrayOutputStream bos = new ByteArrayOutputStream()) {
            device.executeShellCommand(cmd, new OutputStreamShellOutputReceiver(bos));
            final String result = new String(bos.toByteArray(), StandardCharsets.UTF_8);
            log().debug("Command `{}` executed with result: `{}`.", cmd, result);
            return result;
        } catch (final Exception ex) {
            log().error("Unable to execute command `{}`.", cmd, ex);
            throw new ExecuteCommandException(cmd);
        }
    }

    @Override
    public List<FileInfo> list(final String path) {
        log().debug("Listing files under path: `{}`.", path);
        try {
            final String s = executeCommand("ls -l " + path);
            final String[] entries = s.split("\r\n");
// Path: src/main/java/com/github/xsavikx/androidscreencast/api/file/FileInfo.java
// @Singleton
// public final class FileInfo {
// 
//     public AndroidDeviceImpl device;
//     public String path;
//     public String attribs;
//     public boolean directory;
//     public String name;
// 
//     @Inject
//     public FileInfo() {
//     }
// 
//     public File downloadTemporary() {
//         try {
//             File tempFile = File.createTempFile("android", name);
//             device.pullFile(path + name, tempFile);
//             tempFile.deleteOnExit();
//             return tempFile;
//         } catch (IOException ex) {
//             throw new IORuntimeException(ex);
//         }
//     }
// 
//     @Override
//     public String toString() {
//         return name;
//     }
// }
// 
// Path: src/main/java/com/github/xsavikx/androidscreencast/api/injector/OutputStreamShellOutputReceiver.java
// public final class OutputStreamShellOutputReceiver implements IShellOutputReceiver {
// 
//     private final OutputStream os;
// 
//     public OutputStreamShellOutputReceiver(OutputStream os) {
//         this.os = os;
//     }
// 
//     @Override
//     public void addOutput(byte[] buf, int off, int len) {
//         try {
//             os.write(buf, off, len);
//         } catch (IOException e) {
//             throw new IORuntimeException(e);
//         }
//     }
// 
//     @Override
//     public void flush() {
//         try {
//             os.flush();
//         } catch (IOException e) {
//             throw new IORuntimeException(e);
//         }
//     }
// 
//     @Override
//     public boolean isCancelled() {
//         return false;
//     }
// }
// 
// Path: src/main/java/com/github/xsavikx/androidscreencast/exception/AndroidScreenCastRuntimeException.java
// public class AndroidScreenCastRuntimeException extends RuntimeException {
// 
//     private String additionalInformation;
// 
//     public AndroidScreenCastRuntimeException() {
//     }
// 
//     public AndroidScreenCastRuntimeException(String message) {
//         super(message);
//     }
// 
// 
//     public AndroidScreenCastRuntimeException(String message, String additionalInformation) {
//         super(message);
//         this.additionalInformation = additionalInformation;
//     }
// 
//     public AndroidScreenCastRuntimeException(String message, Throwable cause) {
//         super(message, cause);
//     }
// 
//     public AndroidScreenCastRuntimeException(Throwable cause) {
//         super(cause);
//     }
// 
//     public AndroidScreenCastRuntimeException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
//         super(message, cause, enableSuppression, writableStackTrace);
//     }
// 
//     public String getAdditionalInformation() {
//         return additionalInformation;
//     }
// }
// 
// Path: src/main/java/com/github/xsavikx/androidscreencast/exception/ExecuteCommandException.java
// public final class ExecuteCommandException extends AndroidScreenCastRuntimeException {
// 
//     public ExecuteCommandException(String command) {
//         super(String.format("Cannot execute command '%s'.", command));
//     }
// }


// Path: src/main/java/com/github/xsavikx/androidscreencast/api/AndroidDeviceImpl.java
import java.util.ArrayList;
import java.util.List;
import static org.slf4j.LoggerFactory.getLogger;
import com.android.ddmlib.IDevice;
import com.android.ddmlib.SyncService;
import com.github.xsavikx.androidscreencast.api.file.FileInfo;
import com.github.xsavikx.androidscreencast.api.injector.OutputStreamShellOutputReceiver;
import com.github.xsavikx.androidscreencast.exception.AndroidScreenCastRuntimeException;
import com.github.xsavikx.androidscreencast.exception.ExecuteCommandException;
import org.slf4j.Logger;
import javax.inject.Inject;
import javax.inject.Singleton;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.nio.charset.StandardCharsets;

/*
 * Copyright 2020 Yurii Serhiichuk
 *
 * 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.github.xsavikx.androidscreencast.api;




@Singleton
public final class AndroidDeviceImpl implements AndroidDevice {

    private final IDevice device;

    @Inject
    public AndroidDeviceImpl(final IDevice device) {
        this.device = device;
    }

    @Override
    public String executeCommand(final String cmd) {
        log().debug("Executing command: `{}`.", cmd);
        try (final ByteArrayOutputStream bos = new ByteArrayOutputStream()) {
            device.executeShellCommand(cmd, new OutputStreamShellOutputReceiver(bos));
            final String result = new String(bos.toByteArray(), StandardCharsets.UTF_8);
            log().debug("Command `{}` executed with result: `{}`.", cmd, result);
            return result;
        } catch (final Exception ex) {
            log().error("Unable to execute command `{}`.", cmd, ex);
            throw new ExecuteCommandException(cmd);
        }
    }

    @Override
    public List<FileInfo> list(final String path) {
        log().debug("Listing files under path: `{}`.", path);
        try {
            final String s = executeCommand("ls -l " + path);
            final String[] entries = s.split("\r\n");
            final List<FileInfo> fileInfos = new ArrayList<>();