/**
 * Copyright 2011-2015 John Ericksen
 *
 * 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 org.androidtransfuse.util;

import org.androidtransfuse.TransfuseAnalysisException;

import javax.annotation.processing.Filer;
import javax.inject.Inject;
import javax.inject.Named;
import javax.tools.JavaFileObject;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;

/**
 * Copied respectfully from the AndroidAnnotations project:
 * com.googlecode.androidannotations.helper.AndroidManifestFinder
 *
 * Original Header:
 *
 * Copyright (C) 2010-2011 eBusiness Information, Excilys Group
 *
 * 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.
 *
 * @author John Ericksen
 */
public class ManifestLocator {
	public static final String ANDROID_MANIFEST_FILE_OPTION = "androidManifestFile";

    private static final int MAX_PARENTS_FROM_SOURCE_FOLDER = 10;

    private final String androidManifestFilePath;
    private final Filer filer;
    private final Logger log;

    @Inject
    public ManifestLocator(Filer filer, Logger log, @Named(ANDROID_MANIFEST_FILE_OPTION) String androidManifestFilePath) {
        this.filer = filer;
        this.log = log;
        this.androidManifestFilePath = androidManifestFilePath;
    }

    public File findManifest() {
        File androidManifestFile;

        try {
            if (androidManifestFilePath != null) {
                androidManifestFile = new File(androidManifestFilePath);

                if (!androidManifestFile.exists()) {
                    log.error("Could not find the AndroidManifest.xml file specified by option " + ANDROID_MANIFEST_FILE_OPTION + " [" + androidManifestFilePath + "]");
                } else {
                    log.debug("AndroidManifest.xml file found: " + androidManifestFile.toString());
                }
            } else {
                // Backwards compatibility
                androidManifestFile = findManifestFileThrowing();
            }
        } catch (URISyntaxException e) {
            log.error("URISyntaxException while trying to load manifest", e);
            throw new TransfuseAnalysisException("URISyntaxException while trying to load manifest", e);
        } catch (IOException e) {
            log.error("IOException while trying to load manifest", e);
            throw new TransfuseAnalysisException("IOException while trying to load manifest", e);
        }

        return androidManifestFile;
    }


    /**
     * We use a dirty trick to find the AndroidManifest.xml file, since it's not
     * available in the classpath. The idea is quite simple : create a fake
     * class file, retrieve its URI, and start going up in parent folders to
     * find the AndroidManifest.xml file. Any better solution will be
     * appreciated.
     */
    private File findManifestFileThrowing() throws IOException, URISyntaxException {
        JavaFileObject dummySourceFile = filer.createSourceFile("dummy" + System.currentTimeMillis());
        String dummySourceFilePath = dummySourceFile.toUri().toString();

        if (dummySourceFilePath.startsWith("file:")) {
            if (!dummySourceFilePath.startsWith("file://")) {
                dummySourceFilePath = "file://" + dummySourceFilePath.substring("file:".length());
            }
        } else {
            dummySourceFilePath = "file://" + dummySourceFilePath;
        }

        log.debug("Dummy source file: " + dummySourceFilePath);

        URI cleanURI = new URI(dummySourceFilePath);

        File dummyFile = new File(cleanURI);

        File sourcesGenerationFolder = dummyFile.getParentFile();

        File projectRoot = sourcesGenerationFolder.getParentFile();

        File androidManifestFile = new File (projectRoot, "AndroidManifest.xml");
        for (int i = 0; i < MAX_PARENTS_FROM_SOURCE_FOLDER; i++) {
            if (androidManifestFile.exists()) {
                break;
            } else {
                if (projectRoot.getParentFile() != null) {
                    projectRoot = projectRoot.getParentFile();
                    androidManifestFile = new File(projectRoot, "AndroidManifest.xml");
                } else {
                    break;
                }
            }
        }

        if (!androidManifestFile.exists()) {
            throw new IllegalStateException("Could not find the AndroidManifest.xml file, going up from path [" + sourcesGenerationFolder.getAbsolutePath() + "] found using dummy file [" + dummySourceFilePath + "] (max atempts: " + MAX_PARENTS_FROM_SOURCE_FOLDER + ")");
        } else {
            log.debug("AndroidManifest.xml file found: " + androidManifestFile.toString());
        }

        return androidManifestFile;
    }

}
