/*
 * Copyright (C) 2023 Daniele Lacamera <root@danielinux.net>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "task.h"
#include <stdint.h>
#include <stdlib.h>
#include "system.h"

static struct screen *focus = NULL;
static int refresh = 0, redraw = 0;
static struct task *tasklist;
static struct screen *screenlist;
static uint32_t trigger = 0U;
static volatile uint32_t pending_events = 0;
static uint8_t current_screen = 0;


void screen_set_focus(struct screen *s)
{
    if (s) {
        focus = s;
        if (s->draw)
            s->draw();
    }

}

struct screen *screen_get_focus(void)
{
    return focus;
}

void screen_refresh(void)
{
    refresh = 1;
}

void screen_draw(void)
{
    redraw = 1;
}

struct screen *screens(void)
{
    return screenlist;
}

int screen_n_screens(void)
{
    int n = 0;
    struct screen *s = screenlist;
    while (s) {
        n++;
        s = s->next;
    }
    return n;
}

void register_task(struct task *t)
{
    t->screen = NULL;
    t->next = tasklist;
    tasklist = t;
    if (t->init)
        t->init();
}

static int screen_id = 0;

void register_screen(struct task *t, struct screen *s)
{
    t->screen = s;
    s->task = t;
    s->next = screenlist;
    s->id = screen_id++;
    screenlist = s;
    if (!focus) {
        focus = screenlist;
        focus->draw();
    }
}

void task_add_events(struct task *t, uint32_t ev)
{
    t->events |= ev;
}

void task_del_events(struct task *t, uint32_t ev)
{
    t->events &= ~ev;
}

void trigger_event(uint32_t ev)
{
    pending_events |= ev;
}

void clear_event(uint32_t ev)
{
    pending_events &= ~ev;
}

void main_loop(void) {
    struct task *t = tasklist;
    struct screen *s;
    int prio;
    uint32_t processing_events;
    while(1) {
        WFI(); 
        if (!pending_events)
            continue;

        for (prio = 0; prio < 32; prio++) {
            processing_events = pending_events&(1 << prio);
            if (processing_events == 0)
                continue;
            t = tasklist;
            while(t) {
                if (processing_events & t->events)
                    t->run(processing_events & t->events, NULL);
                t = t->next;
            }
        }
        clear_event(EV_SYSTICK);
    }
}


