# 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.

from waflib import Task, TaskGen

from resources.types.resource_ball import ResourceBall
from resources.types.resource_definition import ResourceDefinition


enum_header = (
"""#pragma once

//
// AUTOGENERATED BY BUILD
// DO NOT MODIFY - CHANGES WILL BE OVERWRITTEN
//

typedef enum {
  INVALID_RESOURCE = 0,
  RESOURCE_ID_INVALID = 0,
  DEFAULT_MENU_ICON = 0,
""")


define_header = (
"""#pragma once

//
// AUTOGENERATED BY BUILD
// DO NOT MODIFY - CHANGES WILL BE OVERWRITTEN
//

#define  DEFAULT_MENU_ICON  0

"""
)


extern_header = (
"""#pragma once
#include <stdint.h>

//
// AUTOGENERATED BY BUILD
// DO NOT MODIFY - CHANGES WILL BE OVERWRITTEN
//

""")


definitions_file = (
"""#include <stdint.h>

//
// AUTOGENERATED BY BUILD
// DO NOT MODIFY - CHANGES WILL BE OVERWRITTEN
//

""")


@Task.update_outputs
class generate_resource_id_header(Task.Task):
    def run(self):
        use_extern = getattr(self, 'use_extern', False)
        use_define = getattr(self, 'use_define', False)
        published_media = getattr(self, 'published_media', [])

        if use_extern:
            RESOURCE_ID_DECLARATION = "extern uint32_t RESOURCE_ID_{};"
            PUBLISHED_ID_DECLARATION = "extern uint32_t PUBLISHED_ID_{};"
        elif use_define:
            RESOURCE_ID_DECLARATION = "#define  RESOURCE_ID_{}  {}"
            PUBLISHED_ID_DECLARATION = "#define  PUBLISHED_ID_{}  {}"
        else:
            RESOURCE_ID_DECLARATION = "  RESOURCE_ID_{} = {},"
            INVALID_RESOURCE_ID_DECLARATION = "  RESOURCE_ID_{} = INVALID_RESOURCE,"

            if published_media:
                self.generator.bld.fatal("publishedMedia is only supported for resource headers "
                                         "using externs and defines. Check your "
                                         "generate_resource_id_header arguments.")

        resource_ball = ResourceBall.load(self.inputs[0].abspath())
        declarations_dict = {d.name: d for d in resource_ball.get_all_declarations()}

        self.outputs[0].parent.mkdir()
        with open(self.outputs[0].abspath(), 'w') as output_file:
            if use_extern:
                output_file.write(extern_header)
            elif use_define:
                output_file.write(define_header)
            else:
                output_file.write(enum_header)

            # Write out all the fonts and their aliases
            for i, declaration in enumerate(resource_ball.get_all_declarations(), start=1):
                output_file.write(RESOURCE_ID_DECLARATION.format(declaration.name, i) + "\n")
                if isinstance(declaration, ResourceDefinition):
                    for alias in declaration.aliases:
                        output_file.write(RESOURCE_ID_DECLARATION.format(alias, i) + " // alias\n")
            for item in published_media:
                output_file.write(
                        PUBLISHED_ID_DECLARATION.format(item['name'], item['id'] or 0) + "\n")

            # Handle defining extended font ids for extended fonts that don't actually exist.
            # Every font should have a matching ID defined, but if the resource itself doesn't
            # exist we generate a fake ID for them.
            if not use_extern and not use_define:
                def write_invalid_id_if_needed(name):
                    extended_name = name + '_EXTENDED'

                    if extended_name not in declarations_dict:
                        output_file.write(INVALID_RESOURCE_ID_DECLARATION.format(extended_name) +
                                          "\n")

                for o in resource_ball.resource_objects:
                    if o.definition.type == 'font':
                        write_invalid_id_if_needed(o.definition.name)

                        for alias in o.definition.aliases:
                            write_invalid_id_if_needed(alias)
                output_file.write('} ResourceId;')


@Task.update_outputs
class generate_resource_id_definitions(Task.Task):
    def run(self):
        RESOURCE_ID_DEFINITION = "uint32_t RESOURCE_ID_{} = {};"
        PUBLISHED_ID_DEFINITION = "uint32_t PUBLISHED_ID_{} = {};"

        resource_ball = ResourceBall.load(self.inputs[0].abspath())
        published_media = getattr(self, 'published_media', [])

        self.outputs[0].parent.mkdir()
        with open(self.outputs[0].abspath(), 'w') as output_file:
            output_file.write(definitions_file)

            for i, declaration in enumerate(resource_ball.get_all_declarations(), start=1):
                output_file.write(RESOURCE_ID_DEFINITION.format(declaration.name, i) + "\n")
                if isinstance(declaration, ResourceDefinition):
                    for alias in declaration.aliases:
                        output_file.write(RESOURCE_ID_DEFINITION.format(alias, i) + " // alias\n")

            for item in published_media:
                output_file.write(PUBLISHED_ID_DEFINITION.format(item['name'], item['id']))


@TaskGen.feature('generate_resource_id_header')
@TaskGen.before_method('process_source', 'process_rule')
def process_generate_resource_id_header(self):
    task = self.create_task('generate_resource_id_header',
                            self.resource_ball,
                            self.resource_id_header_target)
    task.use_extern = getattr(self, 'use_extern', False)
    task.use_define = getattr(self, 'use_define', False)
    task.published_media = getattr(self, 'published_media', [])


@TaskGen.feature('generate_resource_id_definitions')
@TaskGen.before_method('process_source', 'process_rule')
def generate_resource_id_definitions(self):
    task = self.create_task('generate_resource_id_definitions',
                            self.resource_ball,
                            self.resource_id_definitions_target)
    task.published_media = getattr(self, 'published_media', [])
