# Copyright 2024 Google LLC
#
# 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.

import os
from waflib import Logs, Task
from waflib.TaskGen import after_method, feature

from binutils import size
from memory_reports import (app_memory_report, app_resource_memory_error,
                            app_appstore_resource_memory_error,
                            bytecode_memory_report, simple_memory_report)
from sdk_helpers import is_sdk_2x


class memory_usage_report(Task.Task):
    """
    Task class to print a memory usage report for the specified binary and resources, if any
    """
    def run(self):
        """
        This method executes when the memory usage report task runs
        :return: None
        """
        bin_type = self.bin_type
        platform = self.generator.env.PLATFORM_NAME

        if bin_type == 'rocky':
            env = self.generator.bld.all_envs[self.env.PLATFORM_NAME]
            Logs.pprint(*bytecode_memory_report(platform, env.SNAPSHOT_SIZE, env.SNAPSHOT_MAX))
            return

        bin_path = self.inputs[0].abspath()
        resources_path = self.inputs[1].abspath() if len(self.inputs) > 1 else None
        max_ram, max_resources, max_appstore_resources = self.max_sizes

        # Handle zero-size binaries (more common with packages)
        ram_size = sum(size(bin_path)) if size(bin_path) != 0 else 0

        resource_size = os.stat(resources_path).st_size if resources_path else None
        if resource_size and max_resources and max_appstore_resources:
            if resource_size > max_resources:
                Logs.pprint(*app_appstore_resource_memory_error(platform, resource_size,
                                                                max_resources))
                return -1
            elif resource_size > max_appstore_resources:
                Logs.pprint(*app_appstore_resource_memory_error(platform, resource_size,
                                                                max_appstore_resources))

        if max_ram:
            # resource_size and max_appstore_resources are optional
            free_ram = max_ram - ram_size
            Logs.pprint(*app_memory_report(platform, bin_type, ram_size, max_ram,
                                           free_ram, resource_size, max_appstore_resources))
        else:
            # resource_size is optional
            Logs.pprint(*simple_memory_report(platform, ram_size, resource_size))


@feature('memory_usage')
@after_method('cprogram', 'cstlib', 'process_rocky_js')
def generate_memory_usage_report(task_gen):
    """
    Generates and prints a report of the project's memory usage (binary + resources, if applicable).

    Keyword arguments:
    app -- The path to the app elf file, if this is an app being evaluated
    worker - The path to the worker elf file, if this is a worker being evaluated
    lib - The path to the library archive file, if this is a library being evaluated
    resources - The path to the resource pack or resource ball, if resources exist for this bin_type

    :param task_gen: the task generator instance
    :return: None
    """
    app, worker, lib, resources = (getattr(task_gen, attr, None)
                                   for attr in ('app', 'worker', 'lib', 'resources'))

    max_resources = task_gen.env.PLATFORM["MAX_RESOURCES_SIZE"]
    max_resources_appstore = task_gen.env.PLATFORM["MAX_RESOURCES_SIZE_APPSTORE"]
    app_max_ram = task_gen.env.PLATFORM["MAX_APP_MEMORY_SIZE"] if app else None
    worker_max_ram = task_gen.env.PLATFORM["MAX_WORKER_MEMORY_SIZE"] if worker else None

    if app:
        app_task = task_gen.create_task('memory_usage_report',
                                        [task_gen.to_nodes(app)[0],
                                         task_gen.to_nodes(resources)[0]])
        app_task.max_sizes = (app_max_ram, max_resources, max_resources_appstore)
        app_task.bin_type = 'app'
    if worker:
        worker_task = task_gen.create_task('memory_usage_report',
                                           task_gen.to_nodes(worker)[0])
        worker_task.max_sizes = (worker_max_ram, None, None)
        worker_task.bin_type = 'worker'
    if lib:
        lib_task = task_gen.create_task('memory_usage_report',
                                        [task_gen.to_nodes(lib)[0],
                                         task_gen.to_nodes(resources)[0]])
        lib_task.max_sizes = (None, None, None)
        lib_task.bin_type = 'lib'
    if getattr(task_gen, 'bin_type', None) == 'rocky':
        rocky_task = task_gen.create_task('memory_usage_report', task_gen.env.JS_RESO)
        rocky_task.bin_type = 'rocky'
        rocky_task.vars = ['PLATFORM_NAME']
