/*
* Copyright (C) 2014 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.
*/

#include "stdafx.h"
#include "FindJava2.h"
#include "utils.h"
#include "JavaFinder.h"
#include "FindJava2Dlg.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

// The one and only MFC application object
class CFindJava2App : public CWinApp {
public:
    CFindJava2App() {
    }

    // Set CWinApp default registry key. Must be consistent with all apps using findjava2.
    void initRegistryKey() {
        SetRegistryKey(_T("Android-FindJava2"));
    }
};

CFindJava2App theApp;

using namespace std;

int _tmain(int argc, TCHAR* argv[], TCHAR* envp[]) {

    // Init utils; use default app name based on VERSIONINFO.FileDescription
    initUtils(NULL);

    // initialize MFC and print and error on failure
    HMODULE hModule = ::GetModuleHandle(NULL);
    if (hModule == NULL) {
        displayLastError(_T("Fatal Error: "));
        return -2;
    }
    if (!AfxWinInit(hModule, NULL, ::GetCommandLine(), 0)) {
        displayLastError(_T("Fatal Error: "));
        return -3;
    }

    theApp.initRegistryKey();

    gIsConsole = true; // tell utils to to print errors to stderr
    gIsDebug = (getenv("ANDROID_SDKMAN_DEBUG") != NULL);

    // Parse command line
    bool doTests = false;
    bool doShortPath = false;
    bool doVersion = false;
    bool doJavaW = false;
    bool doForceUi = false;
    bool doJava1_7 = false;

    for (int i = 1; i < argc; i++) {
        if (_tcsnccmp(argv[i], _T("-t"), 2) == 0) {
            doTests = true;

        } else if (_tcsnccmp(argv[i], _T("-d"), 2) == 0) {
            gIsDebug = true;

        } else if (_tcsnccmp(argv[i], _T("-s"), 2) == 0) {
            doShortPath = true;

        } else if (_tcsnccmp(argv[i], _T("-v"), 2) == 0) {
            doVersion = true;

        } else if (_tcsnccmp(argv[i], _T("-f"), 2) == 0) {
            doForceUi = true;

        } else if (_tcsnccmp(argv[i], _T("-7"), 2) == 0) {
            doJava1_7 = true;

        } else if (_tcscmp(argv[i], _T("-w")) == 0 || _tcscmp(argv[i], _T("-javaw")) == 0) {
            doJavaW = true;

        } else {
            printf(
                "Outputs the path of the first Java.exe found on the local system.\n"
                "Returns code 0 when found, 1 when not found.\n"
                "Options:\n"
                "-h / -help   : This help.\n"
                "-t / -test   : Internal test.\n"
                "-f / -force  : Force UI selection.\n"
                "-7           : Java 1.7 minimum instead of 1.6.\n"
                "-s / -short  : Print path in short DOS form.\n"
                "-w / -javaw  : Search a matching javaw.exe; defaults to java.exe if not found.\n"
                "-v / -version: Only prints the Java version found.\n"
                );
            return 2;
        }
    }


    CJavaFinder javaFinder(JAVA_VERS_TO_INT(1, doJava1_7 ? 7 : 6));
    CJavaPath javaPath = javaFinder.getRegistryPath();

    if (doTests) {
        std::set<CJavaPath> paths;
        javaFinder.findJavaPaths(&paths);
        bool regPrinted = false;
        for (const CJavaPath &p : paths) {
            bool isReg = (p == javaPath);
            if (isReg) {
                regPrinted = true;
            }
            _tprintf(_T("%c [%s] %s\n"), isReg ? '*' : ' ', p.getVersion(), p.mPath);
        }

        if (!regPrinted && !javaPath.isEmpty()) {
            const CJavaPath &p = javaPath;
            _tprintf(_T("* [%s] %s\n"), p.getVersion(), p.mPath);
        }
        return 0;
    }

    if (doForceUi || javaPath.isEmpty()) {
        CFindJava2Dlg dlg;
        dlg.setJavaFinder(&javaFinder);
        INT_PTR nResponse = dlg.DoModal();

        if (nResponse == IDOK) {
            // Get java path selected by user and save into registry for later re-use
            javaPath = dlg.getSelectedPath();
            javaFinder.setRegistryPath(javaPath);
        } else if (nResponse == -1) {   // MFC boilerplate
            TRACE(traceAppMsg, 0, "Warning: dialog creation failed, so application is terminating unexpectedly.\n");
            return 1;
        }
    }

    if (javaPath.isEmpty()) {
        fprintf(stderr, "No java.exe path found");
        return 1;
    }

    if (doShortPath) {
        PVOID oldWow64Value = disableWow64FsRedirection();
        if (!javaPath.toShortPath()) {
            revertWow64FsRedirection(&oldWow64Value);
            _ftprintf(stderr,
                    _T("Failed to convert path to a short DOS path: %s\n"),
                    javaPath.mPath);
            return 1;
        }
        revertWow64FsRedirection(&oldWow64Value);
    }

    if (doVersion) {
        // Print version found. We already have the version as an integer
        // so we don't need to run java -version a second time.
        _tprintf(_T("%s"), javaPath.getVersion());
        return 0;
    }

    if (doJavaW) {
        // Try to find a javaw.exe instead of java.exe at the same location.
        CPath javawPath = javaPath.mPath;
        javawPath.RemoveFileSpec();
        javawPath.Append(_T("javaw.exe"));
        javawPath.Canonicalize();

        // Only accept it if we can actually find the exec
        PVOID oldWow64Value = disableWow64FsRedirection();
        bool exists = javawPath.FileExists() == TRUE; // skip BOOL-to-bool warning
        revertWow64FsRedirection(&oldWow64Value);

        if (!exists) {
            _ftprintf(stderr,
                    _T("Failed to find javaw at: %s\n"),
                    javawPath);
            return 1;
        }

        javaPath.mPath = javawPath;
    }

    // Print java.exe path found
    _tprintf(_T("%s"), javaPath.mPath);
    return 0;

}