// Copyright 2020 Google Inc. All rights reserved.
//
// 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 java

// This file contains the module implementations for runtime_resource_overlay and
// override_runtime_resource_overlay.

import "android/soong/android"

func init() {
	RegisterRuntimeResourceOverlayBuildComponents(android.InitRegistrationContext)
}

func RegisterRuntimeResourceOverlayBuildComponents(ctx android.RegistrationContext) {
	ctx.RegisterModuleType("runtime_resource_overlay", RuntimeResourceOverlayFactory)
	ctx.RegisterModuleType("override_runtime_resource_overlay", OverrideRuntimeResourceOverlayModuleFactory)
}

type RuntimeResourceOverlay struct {
	android.ModuleBase
	android.DefaultableModuleBase
	android.OverridableModuleBase
	aapt

	properties            RuntimeResourceOverlayProperties
	overridableProperties OverridableRuntimeResourceOverlayProperties

	certificate Certificate

	outputFile android.Path
	installDir android.InstallPath
}

type RuntimeResourceOverlayProperties struct {
	// the name of a certificate in the default certificate directory or an android_app_certificate
	// module name in the form ":module".
	Certificate *string

	// Name of the signing certificate lineage file.
	Lineage *string

	// For overriding the --rotation-min-sdk-version property of apksig
	RotationMinSdkVersion *string

	// optional theme name. If specified, the overlay package will be applied
	// only when the ro.boot.vendor.overlay.theme system property is set to the same value.
	Theme *string

	// If not blank, set to the version of the sdk to compile against. This
	// can be either an API version (e.g. "29" for API level 29 AKA Android 10)
	// or special subsets of the current platform, for example "none", "current",
	// "core", "system", "test". See build/soong/java/sdk.go for the full and
	// up-to-date list of possible values.
	// Defaults to compiling against the current platform.
	Sdk_version *string

	// if not blank, set the minimum version of the sdk that the compiled artifacts will run against.
	// Defaults to sdk_version if not set.
	Min_sdk_version *string

	// list of android_library modules whose resources are extracted and linked against statically
	Static_libs []string

	// list of android_app modules whose resources are extracted and linked against
	Resource_libs []string

	// Names of modules to be overridden. Listed modules can only be other overlays
	// (in Make or Soong).
	// This does not completely prevent installation of the overridden overlays, but if both
	// overlays would be installed by default (in PRODUCT_PACKAGES) the other overlay will be removed
	// from PRODUCT_PACKAGES.
	Overrides []string
}

// RuntimeResourceOverlayModule interface is used by the apex package to gather information from
// a RuntimeResourceOverlay module.
type RuntimeResourceOverlayModule interface {
	android.Module
	OutputFile() android.Path
	Certificate() Certificate
	Theme() string
}

// RRO's partition logic is different from the partition logic of other modules defined in soong/android/paths.go
// The default partition for RRO is "/product" and not "/system"
func rroPartition(ctx android.ModuleContext) string {
	var partition string
	if ctx.DeviceSpecific() {
		partition = ctx.DeviceConfig().OdmPath()
	} else if ctx.SocSpecific() {
		partition = ctx.DeviceConfig().VendorPath()
	} else if ctx.SystemExtSpecific() {
		partition = ctx.DeviceConfig().SystemExtPath()
	} else {
		partition = ctx.DeviceConfig().ProductPath()
	}
	return partition
}

func (r *RuntimeResourceOverlay) DepsMutator(ctx android.BottomUpMutatorContext) {
	sdkDep := decodeSdkDep(ctx, android.SdkContext(r))
	if sdkDep.hasFrameworkLibs() {
		r.aapt.deps(ctx, sdkDep)
	}

	cert := android.SrcIsModule(String(r.properties.Certificate))
	if cert != "" {
		ctx.AddDependency(ctx.Module(), certificateTag, cert)
	}

	ctx.AddVariationDependencies(nil, staticLibTag, r.properties.Static_libs...)
	ctx.AddVariationDependencies(nil, libTag, r.properties.Resource_libs...)
}

func (r *RuntimeResourceOverlay) GenerateAndroidBuildActions(ctx android.ModuleContext) {
	// Compile and link resources
	r.aapt.hasNoCode = true
	// Do not remove resources without default values nor dedupe resource configurations with the same value
	aaptLinkFlags := []string{"--no-resource-deduping", "--no-resource-removal"}
	// Allow the override of "package name" and "overlay target package name"
	manifestPackageName, overridden := ctx.DeviceConfig().OverrideManifestPackageNameFor(ctx.ModuleName())
	if overridden || r.overridableProperties.Package_name != nil {
		// The product override variable has a priority over the package_name property.
		if !overridden {
			manifestPackageName = *r.overridableProperties.Package_name
		}
		aaptLinkFlags = append(aaptLinkFlags, generateAaptRenamePackageFlags(manifestPackageName, false)...)
	}
	if r.overridableProperties.Target_package_name != nil {
		aaptLinkFlags = append(aaptLinkFlags,
			"--rename-overlay-target-package "+*r.overridableProperties.Target_package_name)
	}
	r.aapt.buildActions(ctx, r, nil, nil, aaptLinkFlags...)

	// Sign the built package
	_, certificates := collectAppDeps(ctx, r, false, false)
	certificates = processMainCert(r.ModuleBase, String(r.properties.Certificate), certificates, ctx)
	signed := android.PathForModuleOut(ctx, "signed", r.Name()+".apk")
	var lineageFile android.Path
	if lineage := String(r.properties.Lineage); lineage != "" {
		lineageFile = android.PathForModuleSrc(ctx, lineage)
	}

	rotationMinSdkVersion := String(r.properties.RotationMinSdkVersion)

	SignAppPackage(ctx, signed, r.aapt.exportPackage, certificates, nil, lineageFile, rotationMinSdkVersion)
	r.certificate = certificates[0]

	r.outputFile = signed
	partition := rroPartition(ctx)
	r.installDir = android.PathForModuleInPartitionInstall(ctx, partition, "overlay", String(r.properties.Theme))
	ctx.InstallFile(r.installDir, r.outputFile.Base(), r.outputFile)
}

func (r *RuntimeResourceOverlay) SdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
	return android.SdkSpecFrom(ctx, String(r.properties.Sdk_version))
}

func (r *RuntimeResourceOverlay) SystemModules() string {
	return ""
}

func (r *RuntimeResourceOverlay) MinSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
	if r.properties.Min_sdk_version != nil {
		return android.SdkSpecFrom(ctx, *r.properties.Min_sdk_version)
	}
	return r.SdkVersion(ctx)
}

func (r *RuntimeResourceOverlay) TargetSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
	return r.SdkVersion(ctx)
}

func (r *RuntimeResourceOverlay) Certificate() Certificate {
	return r.certificate
}

func (r *RuntimeResourceOverlay) OutputFile() android.Path {
	return r.outputFile
}

func (r *RuntimeResourceOverlay) Theme() string {
	return String(r.properties.Theme)
}

// runtime_resource_overlay generates a resource-only apk file that can overlay application and
// system resources at run time.
func RuntimeResourceOverlayFactory() android.Module {
	module := &RuntimeResourceOverlay{}
	module.AddProperties(
		&module.properties,
		&module.aaptProperties,
		&module.overridableProperties)

	android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
	android.InitDefaultableModule(module)
	android.InitOverridableModule(module, &module.properties.Overrides)
	return module
}

// runtime_resource_overlay properties that can be overridden by override_runtime_resource_overlay
type OverridableRuntimeResourceOverlayProperties struct {
	// the package name of this app. The package name in the manifest file is used if one was not given.
	Package_name *string

	// the target package name of this overlay app. The target package name in the manifest file is used if one was not given.
	Target_package_name *string
}

type OverrideRuntimeResourceOverlay struct {
	android.ModuleBase
	android.OverrideModuleBase
}

func (i *OverrideRuntimeResourceOverlay) GenerateAndroidBuildActions(_ android.ModuleContext) {
	// All the overrides happen in the base module.
	// TODO(jungjw): Check the base module type.
}

// override_runtime_resource_overlay is used to create a module based on another
// runtime_resource_overlay module by overriding some of its properties.
func OverrideRuntimeResourceOverlayModuleFactory() android.Module {
	m := &OverrideRuntimeResourceOverlay{}
	m.AddProperties(&OverridableRuntimeResourceOverlayProperties{})

	android.InitAndroidMultiTargetsArchModule(m, android.DeviceSupported, android.MultilibCommon)
	android.InitOverrideModule(m)
	return m
}