/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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 org.netbeans.modules.cordova.platforms.android;

import java.io.File;
import java.io.IOException;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Logger;
import javax.swing.SwingUtilities;
import org.netbeans.api.options.OptionsDisplayer;
import org.netbeans.api.progress.ProgressUtils;
import org.netbeans.api.project.Project;
import org.netbeans.modules.cordova.platforms.spi.BuildPerformer;
import org.netbeans.modules.cordova.platforms.spi.Device;
import org.netbeans.modules.web.browser.api.WebBrowser;
import org.netbeans.modules.web.browser.spi.ProjectBrowserProvider;
import org.netbeans.spi.project.ActionProvider;
import static org.netbeans.spi.project.ActionProvider.COMMAND_BUILD;
import org.openide.DialogDescriptor;
import org.openide.DialogDisplayer;
import org.openide.NotifyDescriptor;
import org.openide.awt.StatusDisplayer;
import org.openide.filesystems.FileObject;
import org.openide.filesystems.FileUtil;
import org.openide.util.Exceptions;
import org.openide.util.Lookup;
import org.openide.util.NbBundle;
import org.openide.util.RequestProcessor;
import org.openide.util.Utilities;

/**
 *
 * @author Jan Becicka
 */
@NbBundle.Messages({
    "ERR_Title=Error",
    "LBL_CheckingDevice=Connecting to android device...",
    
    "ERR_WebDebug=Cannot connect to Chrome.\nMake sure, that:\n"
        + "\u2022 Device is attached\n"
        + "\u2022 USB Debugging is enabled on your device\n"
        + "\u2022 Your computer and Android device are connected to the same WiFi network",        
    
    "ERR_NO_Cordova=NetBeans cannot find cordova or git on your PATH. Please install cordova and git.\n" +
            "NetBeans might require restart for changes to take effect.\n"
})
/**
 * Cordova Action Provider. Invokes cordova build.
 * @author Jan Becicka
 */
public class AndroidActionProvider implements ActionProvider {

    private final Project p;
    private static final Logger LOGGER = Logger.getLogger(AndroidActionProvider.class.getName());

    public AndroidActionProvider(Project p) {
        this.p = p;
    }
    
    @Override
    public String[] getSupportedActions() {
        return new String[]{
                    COMMAND_BUILD,
                    COMMAND_CLEAN,
                    COMMAND_RUN,
                    COMMAND_RUN_SINGLE,
                    COMMAND_REBUILD
                };
    }

    @NbBundle.Messages({
        "LBL_AvdManager=AVD Manager",
        "ERR_NO_JDK=NetBeans is currently running on JRE. Cordova Android build requires JDK. Please run NetBeans on JDK to continue."
    })
    @Override
    public void invokeAction(String command, final Lookup context) throws IllegalArgumentException {
        final BuildPerformer build = Lookup.getDefault().lookup(BuildPerformer.class);
        String checkAndroid = checkAndroid();
        if (checkAndroid != null) {
            NotifyDescriptor not = new NotifyDescriptor(
                    checkAndroid,
                    Bundle.ERR_Title(),
                    NotifyDescriptor.OK_CANCEL_OPTION,
                    NotifyDescriptor.ERROR_MESSAGE,
                    null,
                    null);
            Object value = DialogDisplayer.getDefault().notify(not);
            if (NotifyDescriptor.CANCEL_OPTION != value) {
                OptionsDisplayer.getDefault().open("Html5/MobilePlatforms"); // NOI18N
            }
            return;
        }

        // Quick fix for issue when IDE is running on JRE, Android build fails because it needs javac
        String jdkHome = System.getProperty("jdk.home"); //NOI18N
        NotifyDescriptor.Message notJDK = new DialogDescriptor.Message(
                Bundle.ERR_NO_JDK(),
                DialogDescriptor.ERROR_MESSAGE);
        if (jdkHome == null || jdkHome.isEmpty()) {
            DialogDisplayer.getDefault().notify(notJDK);
            return;
        } else {
            FileObject jdkHomeFO = FileUtil.toFileObject(new File(jdkHome, "bin")); //NOI18N
            String javacFileName = Utilities.isWindows() ? "javac.exe" : "javac"; //NOI18N
            FileObject javacFO = jdkHomeFO.getFileObject(javacFileName);
            if (javacFO == null) {
                DialogDisplayer.getDefault().notify(notJDK);
                return;
            }
        }

        if (COMMAND_BUILD.equals(command) || COMMAND_CLEAN.equals(command) || COMMAND_REBUILD.equals(command)) {
            try {
                switch (command) {
                    case COMMAND_BUILD:
                        build.perform(BuildPerformer.BUILD_ANDROID, p);
                        break;
                    case COMMAND_CLEAN:
                        build.perform(BuildPerformer.CLEAN_ANDROID, p);
                        break;
                    case COMMAND_REBUILD:
                        build.perform(BuildPerformer.REBUILD_ANDROID, p);
                        break;
                }
            } catch (UnsupportedOperationException ex) {
                NotifyDescriptor.Message not = new DialogDescriptor.Message(
                        Bundle.ERR_NO_Cordova(),
                        DialogDescriptor.ERROR_MESSAGE);
                Object value = DialogDisplayer.getDefault().notify(not);
                return;
            }
        } else if (COMMAND_RUN.equals(command) || COMMAND_RUN_SINGLE.equals(command)) {
            ProgressUtils.runOffEventDispatchThread(new Runnable() {
                @Override
                public void run() {
                    String checkDevices = checkDevices(p);                    
                    while (checkDevices != null) {
                        NotifyDescriptor not = new NotifyDescriptor(
                                checkDevices,
                                Bundle.ERR_Title(),
                                NotifyDescriptor.DEFAULT_OPTION,
                                NotifyDescriptor.ERROR_MESSAGE,
                                checkDevices.equals(Bundle.ERR_RunAndroidEmulator())
                                        ? new Object[]{
                                    DialogDescriptor.OK_OPTION,
                                    DialogDescriptor.CANCEL_OPTION,
                                    Bundle.LBL_AvdManager()
                                } : null,
                                null);
                        Object value = DialogDisplayer.getDefault().notify(not);
                        if (NotifyDescriptor.CANCEL_OPTION == value || checkDevices.equals(Bundle.ERR_AdbNotFound())) {
                            return;
                        } else if (Bundle.LBL_AvdManager().equals(value)) {
                            RequestProcessor.getDefault().post(new Runnable() {
                                @Override
                                public void run() {
                                    AndroidPlatform.getDefault().manageDevices();
                                }
                            });
                            return;
                        } else {
                            checkDevices = checkDevices(p);
                        }
                    }
                    SwingUtilities.invokeLater(new Runnable() {
                        @Override
                        public void run() {
                            try {
                                build.perform(BuildPerformer.RUN_ANDROID, p);
                            } catch (UnsupportedOperationException ex) {
                                NotifyDescriptor not = new NotifyDescriptor(
                                        Bundle.ERR_NO_Cordova(),
                                        Bundle.ERR_Title(),
                                        NotifyDescriptor.OK_CANCEL_OPTION,
                                        NotifyDescriptor.ERROR_MESSAGE,
                                        null,
                                        null);
                                Object value = DialogDisplayer.getDefault().notify(not);
                                if (NotifyDescriptor.CANCEL_OPTION != value) {
                                    OptionsDisplayer.getDefault().open("Html5/MobilePlatforms"); // NOI18N
                                }
                                return;
                            } catch (IllegalStateException ex) {
                                StatusDisplayer.getDefault().setStatusText(ex.getMessage());
                            }
                        }
                    });
                }
            }, Bundle.LBL_CheckingDevice(), new AtomicBoolean(), false);
        }
    }

    @Override
    public boolean isActionEnabled(String command, Lookup context) throws IllegalArgumentException {
        return true;
    }

    @NbBundle.Messages({
        "ERR_ConnectAndroidDevice=Please connect Android device and make sure that:\n"
        + "\u2022 USB Debugging is enabled on your device\n"
        + "\u2022 Your computer and Android device are connected to the same WiFi network",
        "ERR_RunAndroidEmulator=Please run Android Emulator.",
        "ERR_Unknown=Unknown Error.",
        "ERR_AdbNotFound=ADB not found. Please make sure that:\n"
        + "\u2022 Android SDK has been installed correctly,\n"
        + "\u2022 Close Android SDK Manager, if it's opened."
    })
    static String checkDevices(Project p) {
        if (!AndroidPlatform.getDefault().adbCommandExists()) {
            return Bundle.ERR_AdbNotFound();
        }
        ProjectBrowserProvider provider = p.getLookup().lookup(ProjectBrowserProvider.class);
        WebBrowser activeConfiguration = provider.getActiveBrowser();
        try {
            if (activeConfiguration.getId().endsWith("_1")) { //NOI18N
                for (Device dev : AndroidPlatform.getDefault().getConnectedDevices()) {
                    if (!dev.isEmulator()) {
                        return null;
                    }
                }
                return Bundle.ERR_ConnectAndroidDevice();
            } else {
                for (Device dev : AndroidPlatform.getDefault().getConnectedDevices()) {
                    if (dev.isEmulator()) {
                        return null;
                    }
                }
                return Bundle.ERR_RunAndroidEmulator();
            }
        } catch (IOException iOException) {
            Exceptions.printStackTrace(iOException);
        }
        return Bundle.ERR_Unknown();
    }
    
    @NbBundle.Messages("ERR_AndroidNotConfigured=Android Platform is not configured.\nConfigure?")
    static String checkAndroid() {
        if (!AndroidPlatform.getDefault().isReady()) {
            return Bundle.ERR_AndroidNotConfigured();
        }
        return null;
    }
    
}
