# Copyright (c) Facebook, Inc. and its affiliates.
#
# 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.

from __future__ import print_function

import json
import logging
import os
import os.path
import sys

from programs.buck_tool import BuckTool, platform_path
from programs.subprocutils import which
from programs.tracing import Tracing


# If you're looking for JAVA_CLASSPATHS, they're now defined in the programs/classpaths file.

RESOURCES = {
    "android_agent_path": "assets/android/agent.apk",
    "legacy_fix_script": "programs/fixes/source_only_abi/autofix_source_only_abi_warnings.py",
    "buck_server": "bin/buck",
    "buck_build_type_info": "config/build_type/LOCAL_ANT/type.txt",
    "dx": "third-party/java/dx/etc/dx",
    "jacoco_agent_jar": "third-party/java/jacoco/jacocoagent.jar",
    "libjcocoa.dylib": "third-party/java/ObjCBridge/libjcocoa.dylib",
    "logging_config_file": "config/logging.properties.st",
    "native_exopackage_fake_path": "assets/android/native-exopackage-fakes.apk",
    "path_to_sh_binary_template": "src/com/facebook/buck/shell/sh_binary_template",
    "path_to_isolated_trampoline": "src/com/facebook/buck/rules/modern/builders/trampoline.sh",
    "report_generator_jar": "ant-out/report-generator.jar",
    "testrunner_classes": "ant-out/testrunner/classes",
    # python resources used by buck file parser.
    "path_to_pathlib_py": "third-party/py/pathlib/pathlib.py",
    "path_to_pywatchman": "third-party/py/pywatchman",
    "path_to_typing": "third-party/py/typing/python2",
    "path_to_six_py": "third-party/py/six/six.py",
    "path_to_python_dsl": "python-dsl",
}

BUCK_BINARY_HASH_LOCATION = os.path.join(
    "ant-out", "classes", "META-INF", "buck-binary-hash.txt"
)
BUCK_INFO_LOCATION = os.path.join("ant-out", "buck-info.json")


class BuckRepo(BuckTool):
    def __init__(self, buck_bin_dir, buck_project, buck_reporter):
        self.buck_dir = platform_path(os.path.dirname(buck_bin_dir))

        super(BuckRepo, self).__init__(buck_project, buck_reporter)

        dot_git = os.path.join(self.buck_dir, ".git")
        self.is_git = (
            os.path.exists(dot_git)
            and os.path.isdir(dot_git)
            and which("git")
            and sys.platform != "cygwin"
        )
        self._is_buck_repo_dirty_override = os.environ.get("BUCK_REPOSITORY_DIRTY")
        if not self._fake_buck_version:
            # self._fake_buck_version has been set previously through BuckTool when the environment
            # variable BUCK_FAKE_VERSION is set.
            # If the environement variable is not set, we'll use the content of .fakebuckversion
            # at the root of the repository if it exists.
            fake_buck_version_file_path = os.path.join(
                self.buck_dir, ".fakebuckversion"
            )
            if os.path.exists(fake_buck_version_file_path):
                with open(fake_buck_version_file_path) as fake_buck_version_file:
                    self._fake_buck_version = fake_buck_version_file.read().strip()
                    logging.info(
                        "Using fake buck version (via .fakebuckversion): {}".format(
                            self._fake_buck_version
                        )
                    )

    def _get_package_info(self):
        return json.loads(self.__read_file(BUCK_INFO_LOCATION))

    def __read_file(self, filename):
        with open(os.path.join(self.buck_dir, filename)) as file:
            return file.read().strip()

    def _join_buck_dir(self, relative_path):
        return os.path.join(self.buck_dir, *(relative_path.split("/")))

    def _get_resource_lock_path(self):
        return None

    def _has_resource(self, resource):
        return True

    def _get_resource(self, resource, exe=False):
        return self._join_buck_dir(RESOURCES[resource.name])

    def _get_buck_git_commit(self):
        return self._get_buck_version_uid()

    def _get_buck_repo_dirty(self):
        return (
            self._is_buck_repo_dirty_override == "1" or self._package_info["is_dirty"]
        )

    def _get_extra_java_args(self):
        with Tracing("BuckRepo._get_extra_java_args"):
            modules_dir = os.path.join(self.buck_dir, "ant-out", "buck-modules")
            module_resources_dir = os.path.join(
                self.buck_dir, "ant-out", "buck-modules-resources"
            )
            return [
                "-Dbuck.git_dirty={0}".format(int(self._get_buck_repo_dirty())),
                "-Dpf4j.pluginsDir={0}".format(modules_dir),
                "-Dbuck.mode=repository",
                "-Dbuck.module.resources={0}".format(module_resources_dir),
            ]

    def _get_bootstrap_classpath(self):
        return self._join_buck_dir("ant-out/bootstrapper/bootstrapper.jar")

    def _get_buckfilesystem_classpath(self):
        return self._join_buck_dir("ant-out/buckfilesystem/buckfilesystem.jar")

    def _unpack_modules(self):
        pass

    def _get_java_classpath(self):
        classpath_file_path = os.path.join(
            self.buck_dir, "ant-out", "classpath", "classpaths"
        )
        classpath_entries = []
        with open(classpath_file_path, "r") as classpath_file:
            for line in classpath_file.readlines():
                line = line.strip()
                if line.startswith("#"):
                    continue
                classpath_entries.append(line)
        return self._pathsep.join([self._join_buck_dir(p) for p in classpath_entries])

    def _get_buck_binary_hash(self):
        return self.__read_file(BUCK_BINARY_HASH_LOCATION)

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        pass
