#!/usr/bin/env python3
# Copyright 2022 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.
"""Generates freertos_tsktcb.h from FreeRTOS source.

Extracts the tskTCB struct from FreeRTOS sources and writes it as a header to
the specified output path.
"""
import argparse
import re
import sys
from typing import Optional, TextIO
from pathlib import Path

_GENERATED_HEADER = """\
// This header is generated by generate_freertos_tsktcb.py, DO NOT EDIT!
#pragma once

#include "FreeRTOS.h"
#include "task.h"

"""


def _parse_args() -> argparse.Namespace:
    """Parses arguments for this script, splitting out the command to run."""

    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument(
        '--freertos-src-dir',
        type=Path,
        help=(
            'Path to the FreeRTOS source directory. Required unless'
            ' --freertos-tasks-c is provided.'
        ),
    )
    parser.add_argument(
        '--freertos-tasks-c',
        type=Path,
        help=(
            'Path to the tasks.c file in the FreeRTOS source directory. '
            'Required unless --freertos-src-dir is provided.'
        ),
    )
    parser.add_argument(
        '--output',
        '-o',
        type=argparse.FileType('w'),
        help=('Path to write generated tskTCB.h file to'),
    )
    return parser.parse_args()


def _extract_struct(tasks_src: str):
    tsk_tcb_struct = re.search(
        r'(typedef struct tskTaskControlBlock.*tskTCB;\n)',
        tasks_src,
        flags=re.DOTALL,
    )
    if tsk_tcb_struct:
        return tsk_tcb_struct.group(1)
    raise ValueError('Could not find tskTCB struct in tasks.c')


def _main(
    freertos_src_dir: Optional[Path],
    freertos_tasks_c: Optional[Path],
    output: TextIO,
):
    if freertos_tasks_c is None or not freertos_tasks_c.is_file():
        assert freertos_src_dir is not None
        freertos_tasks_c = freertos_src_dir / 'tasks.c'
    with open(freertos_tasks_c, 'r') as tasks_c:
        output.write(_GENERATED_HEADER)
        output.write(_extract_struct(tasks_c.read()))


if __name__ == '__main__':
    sys.exit(_main(**vars(_parse_args())))
