// Copyright 2025 The Pigweed Authors
//
// 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
//
//     https://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.

{% if apps | length > 0 %}
pub fn start() {
    use core::ptr::NonNull;

    use {{ arch_crate_name }} as arch;
    use kernel::__private::foreign_box::{ForeignRc, static_foreign_box};
    use kernel::macro_exports::foreign_box::ForeignBox;
    use kernel::object::{KernelObject, ObjectTable};
    use kernel::scheduler::timer::TimerCallback;

    type MemoryConfig = arch::MemoryConfig;
    type K = arch::Arch;
    let kernel = K {};

// Channel handlers are declared first so that initiators can reference them.
// Unlike other object templates, handlers return a tuple of two ForeignRcs.
// The first is a `dyn KernelObject<K>` which is suitable for adding to a
// processes object table.  The second is a `ChannelHandlerObject<K>` which
// is suitable for creating a `ChannelInitiator` object
{%- for app_name in apps %}
    {%- set app = apps[app_name] %}
    {%- for object_name in app.process.ordered_object_names %}
    {%- set object = app.process.objects[object_name] %}
        {% if object.type == "channel_handler" -%}
            let (
                object_{{app_name}}_{{object_name | lower}},
                object_{{app_name}}_{{object_name | lower}}_handler
            ) = {% include "object_channel_handler" %};
        {%- endif -%}
        // Other objects declared later.
    {%- endfor -%}
{%- endfor -%}

{%- for app_name in apps %}
    {%- set app = apps[app_name] %}
    let start_fn_{{app_name}} = {{app.start_fn_address | hex}}_usize;

    const MEMORY_CONFIG_{{app_name | upper }}: MemoryConfig = MemoryConfig::const_new(&[
        kernel::MemoryRegion::new(
            kernel::MemoryRegionType::ReadOnlyExecutable,
            {{app.flash_start_address | hex}},
            {{(app.flash_start_address + app.flash_size_bytes) | hex}},
        ),
        kernel::MemoryRegion::new(
            kernel::MemoryRegionType::ReadWriteData,
            {{app.ram_start_address | hex}},
            {{(app.ram_start_address + app.ram_size_bytes) | hex}},
        ),
    ]);

    {%- for object_name in app.process.ordered_object_names %}
    {%- set object = app.process.objects[object_name] %}

        let object_{{app_name}}_{{object_name | lower}} =
        {% if object.type == "channel_handler" -%}
                // Reference to handler object already declared above.
                object_{{app_name}}_{{object_name | lower}}
        {% elif object.type == "channel_initiator" -%}
            {% include "object_channel_initiator" %}
        {% elif object.type == "ticker" -%}
            {% include "object_ticker" %}
        {%- endif -%}
        ;
    {%- endfor %}

    let object_table_{{app_name}}: ForeignBox<dyn ObjectTable<K>> = unsafe {
        static_foreign_box!(
            [ForeignRc<<K as kernel::Arch>::AtomicUsize, dyn KernelObject<K>>; {{app.process.objects | length}}],
            [
            {%- for object_name in app.process.ordered_object_names %}
                object_{{app_name}}_{{object_name | lower}},
            {%- endfor -%}
            ]
        )
    };

    let process_{{app_name}} = unsafe { kernel::init_non_priv_process!(
        "{{app.process.name}}",
        MEMORY_CONFIG_{{app_name | upper}},
        object_table_{{app_name}}
    )};

{%- for thread in app.process.threads -%}
{% set thread_index = loop.index0 %}
    let thread_{{app_name}}_{{thread_index}} = unsafe {
        kernel::init_non_priv_thread!(
            "{{thread.name}}",
            process_{{app_name}},
            start_fn_{{app_name}},
            {{app.initial_sp | hex}},
            {{thread.stack_size_bytes}}
        )
    };
    kernel::start_thread(arch::Arch, thread_{{app_name}}_{{thread_index}});
{%- endfor -%}
{%- endfor %}
}
{% endif %}
