/*
* This file is part of the stmbl project.
*
* Copyright (C) 2013-2017 Rene Hopf <renehopf@mac.com>
* Copyright (C) 2013-2017 Nico Stute <crinq@crinq.de>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "hal.h"
#include <string.h>
#include <stdio.h>
#include <math.h>
#include "commands.h"
#include "defines.h"
#include "version.h"

hal_t hal;

hal_comp_t *comp_by_name(NAME name) {
  for(int i = 0; i < comp_count; i++) {
    if(!strncmp(name, comps[i]->name, sizeof(NAME))) {
      return (comps[i]);
    }
  }
  return (0);
}

volatile hal_comp_inst_t *comp_inst_by_name(NAME name, uint32_t instance) {
  hal_comp_t *comp = comp_by_name(name);
  if(comp) {
    for(int i = 0; i < hal.comp_inst_count; i++) {
      if(hal.comp_insts[i].comp == comp && instance == hal.comp_insts[i].instance) {
        return (&hal.comp_insts[i]);
      }
    }
  }
  return (0);
}

pin_t *pin_by_name(NAME comp_name, NAME pin_name) {
  uint32_t offset = 0;
  for(int i = 0; i < comp_count; i++) {
    if(!strncmp(comp_name, comps[i]->name, sizeof(NAME))) {
      for(int j = 0; j < comps[i]->pin_count; j++) {
        if(!strncmp(pin_name, pins[j + offset], sizeof(NAME))) {
          return (&pins[j + offset]);
        }
      }
    }
  }
  return (0);
}

volatile hal_pin_inst_t *pin_inst_by_name(NAME comp_name, uint32_t instance, NAME pin_name) {
  volatile hal_comp_inst_t *comp = comp_inst_by_name(comp_name, instance);
  if(comp) {
    for(int i = 0; i < comp->comp->pin_count; i++) {
      if(!strncmp(pin_name, comp->pins[i], sizeof(NAME))) {
        return (&comp->pin_insts[i]);
      }
    }
  }
  return (0);
}

pin_t *pin_by_pin_inst(volatile hal_pin_inst_t *p) {
  for(int i = 0; i < hal.comp_inst_count; i++) {
    for(int j = 0; j < hal.comp_insts[i].comp->pin_count; j++) {
      if(&(hal.comp_insts[i].pin_insts[j]) == p) {
        return (&hal.comp_insts[i].pins[j]);
      }
    }
  }
  return (0);
}

volatile hal_comp_inst_t *comp_inst_by_pin_inst(volatile hal_pin_inst_t *p) {
  for(int i = 0; i < hal.comp_inst_count; i++) {
    for(int j = 0; j < hal.comp_insts[i].comp->pin_count; j++) {
      if(&(hal.comp_insts[i].pin_insts[j]) == p) {
        return (&hal.comp_insts[i]);
      }
    }
  }
  return (0);
}


void hal_term_print_state() {
  switch(hal.hal_state) {
    break;
    case HAL_OK2:
      printf("HAL state:  HAL_OK2\n");
      break;
    case RT_TOO_LONG:
      printf("HAL state:  RT_TOO_LONG\n");
      break;
    case FRT_TOO_LONG:
      printf("HAL state:  FRT_TOO_LONG\n");
      break;
    case MISC_ERROR:
      printf("HAL state:  MISC_ERROR\n");
      switch(hal.error_info.error_handler) {
        case HardFault:
          printf("error handler: HardFault\n");
          break;

        case NMI:
          printf("error handler: NMI\n");
          break;

        case MemManage:
          printf("error handler: MemManage\n");
          break;

        case BusFault:
          printf("error handler: BusFault\n");
          break;

        case UsageFault:
          printf("error handler: UsageFault\n");
          break;
      }
      printf("active rt func: %lu\n", hal.error_info.active_rt_func);
      printf("active frt func: %lu\n", hal.error_info.active_frt_func);
      printf("active nrt func: %lu\n", hal.error_info.active_nrt_func);

      break;
    case MEM_ERROR:
      printf("HAL state:  MEM_ERROR\n");
      break;
    case CONFIG_LOAD_ERROR:
      printf("HAL state:  CONFIG_LOAD_ERROR\n");
      break;
    case CONFIG_ERROR:
      printf("HAL state:  CONFIG_ERROR\n");
      break;
    case NAN_ERROR:
      printf("HAL state:  NAN_ERROR\n");
      break;
    default:
      printf("HAL state:  unkonwn error\n");
  }
}

void hal_term_print_info(char *ptr) {
  printf("######## hal info ########\n");
  printf("HAL_MAX_COMPS %lu/%i\n", hal.comp_inst_count, HAL_MAX_COMPS);
  printf("HAL_MAX_PINS %lu/%i\n", hal.pin_inst_count, HAL_MAX_PINS);
  printf("HAL_MAX_CTX %lu/%i\n", hal.ctx_count, HAL_MAX_CTX);
  hal_term_print_state();

  // printf("link errors %lu\n", hal.link_errors);
  // printf("pin errors %lu\n", hal.pin_errors);
  // printf("comp errors %lu\n", hal.comp_errors);
  // printf("set errors %lu\n", hal.set_errors);
  // printf("get errors %lu\n", hal.get_errors);
  // printf("foo0.bar:  %f\n", hal_get_pin("foo0.bar"));
  // printf("error_name: %s\n",hal.error_name);
  float pe               = hal.rt_period;
  float ct               = (float)hal.rt_ticks / hal_get_systick_freq();
  float mct              = (float)hal.rt_max_ticks / hal_get_systick_freq();
  float cct              = 0;
  float mcct             = 0;
  struct pin_ctx_t *pins = 0;
  printf("\n### rt info ###\n");
  switch(hal.rt_state) {
    case RT_STOP:
      printf("rt state:  STOP\n");
      break;
    case RT_SLEEP:
      printf("rt state:  SLEEP\n");
      break;
    case RT_CALC:
      printf("rt state:  CALC\n");
      break;
  }
  if(mct > 0.0) {
    printf("rt_ticks: %lu(max %lu)\n", hal.rt_ticks, hal.rt_max_ticks);
    if(pe > 0.0) {
      printf("rt time: %f(max %f)us/%fus = %f(max %f)%% @ %fkHz\n", ct * 1000000.0, mct * 1000000.0, pe * 1000000.0, (ct / pe) * 100.0, (mct / pe) * 100.0, 1.0 / pe / 1000.0);
    } else {
      printf("rt time: %f(max %f)us\n", ct * 1000000.0, mct * 1000000.0);
    }
  }
  printf("active rt funcs(%lu):\n", hal.rt_comp_count);
  for(int j = 0; j < hal.rt_comp_count; j++) {
    pins = (struct pin_ctx_t *)(hal.rt_comps[j]->pin_insts);
    cct  = (float)hal.rt_comps[j]->rt_ticks / hal_get_systick_freq();
    mcct = (float)hal.rt_comps[j]->rt_max_ticks / hal_get_systick_freq();
    printf("%s(%f) %f(max %f)us", hal.rt_comps[j]->comp->name, PIN(rt_prio), cct * 1000000.0, mcct * 1000000.0);
    if(pe > 0.0) {
      printf(" = %f(max %f)%%\n", cct / pe * 100.0, mcct / pe * 100.0);
    } else {
      printf("\n");
    }
  }


  pe  = hal.frt_period;
  ct  = (float)hal.frt_ticks / hal_get_systick_freq();
  mct = (float)hal.frt_max_ticks / hal_get_systick_freq();
  printf("\n### frt info ###\n");
  switch(hal.frt_state) {
    case RT_STOP:
      printf("frt state:  STOP\n");
      break;
    case RT_SLEEP:
      printf("frt state:  SLEEP\n");
      break;
    case RT_CALC:
      printf("frt state:  CALC\n");
      break;
  }
  if(mct > 0.0) {
    printf("frt_ticks: %lu(max %lu)\n", hal.frt_ticks, hal.frt_max_ticks);
    if(pe > 0.0) {
      printf("frt time: %f(max %f)us/%fus = %f(max %f)%% @ %fkHz\n", ct * 1000000.0, mct * 1000000.0, pe * 1000000.0, (ct / pe) * 100.0, (mct / pe) * 100.0, 1.0 / pe / 1000.0);
    } else {
      printf("frt time: %f(max %f)us\n", ct * 1000000.0, mct * 1000000.0);
    }
  }
  printf("active frt funcs(%lu):\n", hal.frt_comp_count);
  for(int j = 0; j < hal.frt_comp_count; j++) {
    pins = (struct pin_ctx_t *)(hal.frt_comps[j]->pin_insts);
    cct  = (float)hal.frt_comps[j]->frt_ticks / hal_get_systick_freq();
    mcct = (float)hal.frt_comps[j]->frt_max_ticks / hal_get_systick_freq();
    printf("%s(%f) %f(max %f)us", hal.frt_comps[j]->comp->name, PIN(frt_prio), cct * 1000000.0, mcct * 1000000.0);
    if(pe > 0.0) {
      printf(" = %f(max %f)%%\n", cct / pe * 100.0, mcct / pe * 100.0);
    } else {
      printf("\n");
    }
  }

  ct  = (float)hal.nrt_ticks / hal_get_systick_freq();
  mct = (float)hal.nrt_max_ticks / hal_get_systick_freq();
  printf("\n### nrt info ###\n");
  if(mct > 0.0) {
    printf("nrt time: %f(max %f)us\n", ct * 1000000.0, mct * 1000000.0);
  }
  printf("active nrt funcs(%lu):\n", hal.comp_inst_count);
  for(int j = 0; j < hal.comp_inst_count; j++) {
    if(hal.comp_insts[j].comp->nrt) {
      pins = (struct pin_ctx_t *)(hal.comp_insts[j].pin_insts);

      cct  = (float)hal.comp_insts[j].nrt_ticks / hal_get_systick_freq();
      mcct = (float)hal.comp_insts[j].nrt_max_ticks / hal_get_systick_freq();
      printf("%s %f(max %f)us\n", hal.comp_insts[j].comp->name, cct * 1000000.0, mcct * 1000000.0);
    }
  }
}

COMMAND("hal", hal_term_print_info, "print HAL stats");


uint32_t load_comp(hal_comp_t *comp) {
  if(!comp) {
    printf("<font color=\"FireBrick\">load_comp: not found</font>\n");
    return (0);
  }
  if(hal.comp_inst_count >= HAL_MAX_COMPS - 1) {
    printf("<font color=\"FireBrick\">load_comp: not enough space to load comp: %s</font>\n", comp->name);
    return (0);
  }
  if(hal.pin_inst_count + comp->pin_count >= HAL_MAX_PINS - 1) {
    printf("<font color=\"FireBrick\">load_comp: not enough space to load comp pins: %s</font>\n", comp->name);
    return (0);
  }
  uint32_t ctx_size = ((uint32_t)ceil((comp->ctx_size / 4.0))) * 4;
  if(hal.ctx_count + ctx_size >= HAL_MAX_CTX - 1) {
    printf("<font color=\"FireBrick\">load_comp: not enough space to load comp ctx: %s</font>\n", comp->name);
    return (0);
  }

  // load comp
  hal.comp_insts[hal.comp_inst_count].comp      = comp;
  hal.comp_insts[hal.comp_inst_count].ctx       = &hal.ctxs[hal.ctx_count];
  hal.comp_insts[hal.comp_inst_count].pin_insts = &hal.pin_insts[hal.pin_inst_count];
  hal.comp_insts[hal.comp_inst_count].ctx_size  = ctx_size;
  hal.comp_insts[hal.comp_inst_count].state     = PRE_INIT;

  uint32_t offset = 0;
  for(int i = 0; i < comp_count; i++) {
    if(comps[i] == comp) {
      hal.comp_insts[hal.comp_inst_count].pins = &pins[offset];
    }
    offset += comps[i]->pin_count;
  }
  hal.comp_insts[hal.comp_inst_count].instance = 0;
  for(int i = 0; i < hal.comp_inst_count; i++) {
    if(hal.comp_insts[i].comp == comp) {
      hal.comp_insts[hal.comp_inst_count].instance++;
    }
  }


  // load pins
  for(int i = hal.pin_inst_count; i < hal.pin_inst_count + comp->pin_count; i++) {
    hal.pin_insts[i].value  = 0.0;
    hal.pin_insts[i].source = &hal.pin_insts[i];
  }
  hal.pin_inst_count += comp->pin_count;

  // load ctx
  for(int i = hal.ctx_count; i < hal.ctx_count + ctx_size; i++) {
    hal.ctxs[i] = 0;
  }

  hal.ctx_count += ctx_size;

  if(hal.comp_insts[hal.comp_inst_count].comp->nrt_init != 0) {
    hal.comp_insts[hal.comp_inst_count].comp->nrt_init(hal.comp_insts[hal.comp_inst_count].ctx, hal.comp_insts[hal.comp_inst_count].pin_insts);
  }
  hal.comp_insts[hal.comp_inst_count].nrt_ticks     = 0;
  hal.comp_insts[hal.comp_inst_count].nrt_max_ticks = 0;
  hal.comp_insts[hal.comp_inst_count].state         = PRE_HW_INIT;

  hal.comp_inst_count++;

  return (1);
}

void hal_run_rt() {
#ifdef HAL_CALC_TIME
  uint32_t hal_start = hal_get_systick_value();
#endif

  switch(hal.rt_state) {
    case RT_STOP:
      return;
    case RT_CALC:  // call stop
      hal.rt_state  = RT_STOP;
      hal.hal_state = RT_TOO_LONG;
      hal.frt_state = RT_STOP;
      return;
    case RT_SLEEP:
      if(hal.active_rt_func > -1) {  // call stop
        hal.rt_state  = RT_STOP;
        hal.hal_state = MISC_ERROR;
        hal.frt_state = RT_STOP;
        return;
      }
      hal.rt_state = RT_CALC;
  }

#ifdef HAL_COMP_CALC_TIME
  uint32_t start = hal_get_systick_value();
#endif

  for(hal.active_rt_func = 0; hal.active_rt_func < hal.rt_comp_count; hal.active_rt_func++) {
    hal.rt_comps[hal.active_rt_func]->comp->rt(hal.rt_period, hal.rt_comps[hal.active_rt_func]->ctx, hal.rt_comps[hal.active_rt_func]->pin_insts);
#ifdef HAL_COMP_CALC_TIME
    uint32_t end_ticks = hal_get_systick_value();
    if(start < end_ticks) {
      start += hal_get_systick_reload();
    }
    hal.rt_comps[hal.active_rt_func]->rt_ticks     = start - end_ticks;
    hal.rt_comps[hal.active_rt_func]->rt_max_ticks = MAX(hal.rt_comps[hal.active_rt_func]->rt_max_ticks, hal.rt_comps[hal.active_rt_func]->rt_ticks);
    start                                          = end_ticks;
#endif
  }
  hal.active_rt_func = -1;

  if(hal.rt_state == RT_CALC) {
    hal.rt_state = RT_SLEEP;
  }

#ifdef HAL_CALC_TIME
  uint32_t hal_end = hal_get_systick_value();
  if(hal_start < hal_end) {
    hal_start += hal_get_systick_reload();
  }
  hal.rt_ticks     = hal_start - hal_end;
  hal.rt_max_ticks = MAX(hal.rt_max_ticks, hal.rt_ticks);
#endif

#ifdef HAL_WATCHDOG
  hal_reset_watchdog();
#endif
}

void hal_run_frt() {
#ifdef HAL_CALC_TIME
  uint32_t hal_start = hal_get_systick_value();
#endif

  switch(hal.frt_state) {
    case RT_STOP:
      return;
    case RT_CALC:
      hal.rt_state  = RT_STOP;
      hal.hal_state = FRT_TOO_LONG;
      hal.frt_state = RT_STOP;
      return;
    case RT_SLEEP:
      if(hal.active_frt_func > -1) {
        hal.rt_state  = RT_STOP;
        hal.hal_state = MISC_ERROR;
        hal.frt_state = RT_STOP;
        return;
      }
      hal.frt_state = RT_CALC;
  }

#ifdef HAL_COMP_CALC_TIME
  uint32_t start = hal_get_systick_value();
#endif

  for(hal.active_frt_func = 0; hal.active_frt_func < hal.frt_comp_count; hal.active_frt_func++) {
    hal.frt_comps[hal.active_frt_func]->comp->frt(hal.frt_period, hal.frt_comps[hal.active_frt_func]->ctx, hal.frt_comps[hal.active_frt_func]->pin_insts);
#ifdef HAL_COMP_CALC_TIME
    uint32_t end_ticks = hal_get_systick_value();
    if(start < end_ticks) {
      start += hal_get_systick_reload();
    }
    hal.frt_comps[hal.active_frt_func]->frt_ticks     = start - end_ticks;
    hal.frt_comps[hal.active_frt_func]->frt_max_ticks = MAX(hal.frt_comps[hal.active_frt_func]->frt_max_ticks, hal.frt_comps[hal.active_frt_func]->frt_ticks);
    start                                             = end_ticks;
#endif
  }
  hal.active_frt_func = -1;

  if(hal.frt_state == RT_CALC) {
    hal.frt_state = RT_SLEEP;
  }

#ifdef HAL_CALC_TIME
  uint32_t hal_end = hal_get_systick_value();
  if(hal_start < hal_end) {
    hal_start += hal_get_systick_reload();
  }
  hal.frt_ticks     = hal_start - hal_end;
  hal.frt_max_ticks = MAX(hal.frt_max_ticks, hal.frt_ticks);
#endif

#ifdef HAL_WATCHDOG
  hal_reset_watchdog();
#endif
}

void hal_run_nrt() {
#ifdef HAL_CALC_TIME
  uint32_t hal_start = hal_get_systick_value();
#endif

#ifdef HAL_COMP_CALC_TIME
  uint32_t start = hal_get_systick_value();
#endif

  for(hal.active_nrt_func = 0; hal.active_nrt_func < hal.comp_inst_count; hal.active_nrt_func++) {
    if(hal.comp_insts[hal.active_nrt_func].comp->nrt != 0) {
      hal.comp_insts[hal.active_nrt_func].comp->nrt(hal.comp_insts[hal.active_nrt_func].ctx, hal.comp_insts[hal.active_nrt_func].pin_insts);
#ifdef HAL_COMP_CALC_TIME
      uint32_t end_ticks = hal_get_systick_value();
      if(start < end_ticks) {
        start += hal_get_systick_reload();
      }
      hal.comp_insts[hal.active_nrt_func].nrt_ticks     = start - end_ticks;
      hal.comp_insts[hal.active_nrt_func].nrt_max_ticks = MAX(hal.comp_insts[hal.active_nrt_func].nrt_max_ticks, hal.comp_insts[hal.active_nrt_func].nrt_ticks);
      start                                             = end_ticks;
#endif
    }
  }
  hal.active_nrt_func = -1;

#ifdef HAL_CALC_TIME
  uint32_t hal_end = hal_get_systick_value();
  if(hal_start < hal_end) {
    hal_start += hal_get_systick_reload();
  }
  hal.nrt_ticks     = hal_start - hal_end;
  hal.nrt_max_ticks = MAX(hal.nrt_max_ticks, hal.nrt_ticks);
#endif

#ifdef HAL_WATCHDOG
  hal_reset_watchdog();
#endif
}

void hal_init_hw() {
  for(int i = 0; i < hal.comp_inst_count; i++) {
    if(hal.comp_insts[i].state == PRE_HW_INIT) {
      if(hal.comp_insts[i].comp->hw_init != 0) {
        hal.comp_insts[i].comp->hw_init(hal.comp_insts[i].ctx, hal.comp_insts[i].pin_insts);
      }
      hal.comp_insts[i].state = STARTED;
    }
  }
}


void load(char *ptr) {
  printf("load :%s:\n", ptr);
  load_comp(comp_by_name(ptr));
}

COMMAND("load", load, "load comp from flash");

void show(char *ptr) {
  int j          = 0;
  int pin_offset = 0;
  for(int i = 0; i < comp_count; i++) {
    printf("%s\n", comps[i]->name);
    printf("#pins: %lu\n", comps[i]->pin_count);
    printf("#ctx: %lu byte\n", comps[i]->ctx_size);
    for(; j < pin_offset + comps[i]->pin_count; j++) {
      printf("- %s\n", pins[j]);
    }
    pin_offset += comps[i]->pin_count;
  }
}
COMMAND("show", show, "show comps in flash");

void list(char *ptr) {
  for(int i = 0; i < hal.comp_inst_count; i++) {
    printf("%s%lu:\n", hal.comp_insts[i].comp->name, hal.comp_insts[i].instance);
    printf("#pins: %lu\n", hal.comp_insts[i].comp->pin_count);
    printf("#ctx: %lu byte\n", hal.comp_insts[i].comp->ctx_size);
    printf("real #ctx: %lu byte\n", hal.comp_insts[i].ctx_size);
    printf("*pins: 0x%x\n", (unsigned int)hal.comp_insts[i].pin_insts);
    printf("*ctx: 0x%x\n", (unsigned int)hal.comp_insts[i].ctx);
    printf("state: ");
    switch(hal.comp_insts[i].state) {
      case PRE_INIT:
        printf("PRE_INIT\n");
        break;
      case PRE_HW_INIT:
        printf("PRE_HW_INIT\n");
        break;
      case STARTED:
        printf("STARTED\n");
        break;
      default:
        printf("unknown\n");
    }
    for(int j = 0; j < hal.comp_insts[i].comp->pin_count; j++) {
      volatile hal_comp_inst_t *comp = comp_inst_by_pin_inst(hal.comp_insts[i].pin_insts[j].source->source);
      printf("-  %s <= %s%lu.%s = %f\n", hal.comp_insts[i].pins[j], comp->comp->name, comp->instance, (char *)pin_by_pin_inst(hal.comp_insts[i].pin_insts[j].source->source), hal.comp_insts[i].pin_insts[j].source->source->value);
    }
  }
}
COMMAND("list", list, "show comp instances");

void sort_rt() {
  float min     = INFINITY;
  int min_index = -1;
  float rt_prio = 0.0;
  char added[HAL_MAX_COMPS];
  struct pin_ctx_t *pins;

  for(int i = 0; i < hal.comp_inst_count; i++) {
    added[i] = 0;
  }

  hal.rt_comp_count = 0;
  for(int i = 0; i < hal.comp_inst_count; i++) {
    min       = INFINITY;
    min_index = -1;
    for(int j = hal.comp_inst_count - 1; j >= 0; j--) {
      pins    = (struct pin_ctx_t *)(hal.comp_insts[j].pin_insts);
      rt_prio = PIN(rt_prio);
      if(rt_prio <= min && added[j] == 0 && rt_prio > 0.0 && hal.comp_insts[j].comp->rt != 0) {
        min       = rt_prio;
        min_index = j;
      }
    }
    if(min_index >= 0) {
      added[min_index]                  = 1;
      hal.rt_comps[hal.rt_comp_count++] = &hal.comp_insts[min_index];
    }
  }
}

void sort_frt() {
  float min      = INFINITY;
  int min_index  = -1;
  float frt_prio = 0.0;
  char added[HAL_MAX_COMPS];
  struct pin_ctx_t *pins;

  for(int i = 0; i < hal.comp_inst_count; i++) {
    added[i] = 0;
  }

  hal.frt_comp_count = 0;
  for(int i = 0; i < hal.comp_inst_count; i++) {
    min       = INFINITY;
    min_index = -1;
    for(int j = hal.comp_inst_count - 1; j >= 0; j--) {
      pins     = (struct pin_ctx_t *)(hal.comp_insts[j].pin_insts);
      frt_prio = PIN(frt_prio);
      if(frt_prio <= min && added[j] == 0 && frt_prio > 0.0 && hal.comp_insts[j].comp->frt != 0) {
        min       = frt_prio;
        min_index = j;
      }
    }
    if(min_index >= 0) {
      added[min_index]                    = 1;
      hal.frt_comps[hal.frt_comp_count++] = &hal.comp_insts[min_index];
    }
  }
}

void start_rt() {
  for(int i = 0; i < hal.rt_comp_count; i++) {
    if(hal.rt_comps[i]->comp->rt_start != 0) {
      hal.rt_comps[i]->comp->rt_start(hal.rt_comps[i]->ctx, hal.rt_comps[i]->pin_insts);
    }
    hal.rt_comps[i]->rt_ticks     = 0;
    hal.rt_comps[i]->rt_max_ticks = 0;
  }

  hal.rt_ticks     = 0.0;
  hal.rt_max_ticks = 0.0;

  hal.rt_state = RT_SLEEP;
}

void start_frt() {
  for(int i = 0; i < hal.frt_comp_count; i++) {
    if(hal.frt_comps[i]->comp->frt_start != 0) {
      hal.frt_comps[i]->comp->frt_start(hal.frt_comps[i]->ctx, hal.frt_comps[i]->pin_insts);
    }
    hal.frt_comps[i]->frt_ticks     = 0;
    hal.frt_comps[i]->frt_max_ticks = 0;
  }

  hal.frt_ticks     = 0.0;
  hal.frt_max_ticks = 0.0;

  hal.frt_state = RT_SLEEP;
}

void hal_start() {
  hal.hal_state = HAL_OK2;

  sort_rt();
  sort_frt();
  hal_init_hw();
  start_rt();
  start_frt();
}

COMMAND("start", hal_start, "start rt system");

void stop_rt() {
  hal.rt_state = RT_STOP;

  for(int i = 0; i < hal.rt_comp_count; i++) {
    if(hal.rt_comps[i]->comp->rt_stop != 0) {
      hal.rt_comps[i]->comp->rt_stop(hal.rt_comps[i]->ctx, hal.rt_comps[i]->pin_insts);
    }
  }
}

void stop_frt() {
  hal.frt_state = RT_STOP;

  for(int i = 0; i < hal.frt_comp_count; i++) {
    if(hal.frt_comps[i]->comp->frt_stop != 0) {
      hal.frt_comps[i]->comp->frt_stop(hal.frt_comps[i]->ctx, hal.frt_comps[i]->pin_insts);
    }
  }
}

void hal_stop() {
  stop_rt();
  stop_frt();
}

COMMAND("stop", hal_stop, "stop rt system");

void hal_init(float rt_period, float frt_period) {
  hal.rt_state  = RT_STOP;
  hal.frt_state = RT_STOP;

  for(int i = 0; i < HAL_MAX_COMPS; i++) {
    hal.rt_comps[i]  = 0;
    hal.frt_comps[i] = 0;
  }

  hal.comp_inst_count = 0;
  hal.rt_comp_count   = 0;
  hal.frt_comp_count  = 0;
  hal.pin_inst_count  = 0;

  for(int i = 0; i < HAL_MAX_CTX; i++) {
    hal.ctxs[i] = 0;
  }
  hal.ctx_count = 0;

  hal.active_rt_func  = -1;
  hal.active_frt_func = -1;
  hal.active_nrt_func = -1;

  hal.rt_ticks      = 0.0;
  hal.rt_max_ticks  = 0.0;
  hal.frt_ticks     = 0.0;
  hal.frt_max_ticks = 0.0;
  hal.nrt_ticks     = 0.0;
  hal.nrt_max_ticks = 0.0;
  hal.rt_period     = rt_period;
  hal.frt_period    = frt_period;
  hal.nrt_ticks     = 0.0;
  hal.nrt_max_ticks = 0.0;
}

void hal_print_pin(volatile hal_pin_inst_t *p) {
  pin_t *pin                     = pin_by_pin_inst(p);
  volatile hal_comp_inst_t *comp = comp_inst_by_pin_inst(p);

  pin_t *pin2;
  volatile hal_comp_inst_t *comp2;
  pin_t *pin3;
  volatile hal_comp_inst_t *comp3;

  if(p && pin && comp) {
    if(p == p->source) {  //if pin is not linked
      printf("%s%lu.%s = %f\n", (char *)comp->comp->name, comp->instance, (char *)pin, p->value);
    } else if(p->source == p->source->source) {  //pin is single linked
      pin2  = pin_by_pin_inst(p->source);
      comp2 = comp_inst_by_pin_inst(p->source);
      printf("%s%lu.%s <= %s%lu.%s = %f\n", (char *)comp->comp->name, comp->instance, (char *)pin, (char *)comp2->comp->name, comp2->instance, (char *)pin2, p->source->value);
    } else {  //pin is double linked
      pin2  = pin_by_pin_inst(p->source);
      comp2 = comp_inst_by_pin_inst(p->source);
      pin3  = pin_by_pin_inst(p->source->source);
      comp3 = comp_inst_by_pin_inst(p->source->source);
      printf("%s%lu.%s <= %s%lu.%s <= %s%lu.%s = %f\n", (char *)comp->comp->name, comp->instance, (char *)pin, (char *)comp2->comp->name, comp2->instance, (char *)pin2, (char *)comp3->comp->name, comp3->instance, (char *)pin3, p->source->source->value);
    }
  }
}

char *findline(char *ptr) {
  for(int i = 0; i < 64; i++) {
    if(ptr[i] == 0) {
      return (0);
    }
    if(ptr[i] == '\n') {
      return (ptr + i + 1);
    }
  }
  return (0);
}

uint32_t hal_parse_(char *cmd);

uint32_t hal_parse(char *cmd) {
  do {
    hal_parse_(cmd);
    cmd = findline(cmd);
  } while(cmd);
  return (0);
}

uint32_t hal_parse_(char *cmd) {
  if(call_cmd(cmd)) {
    return (1);
  }

  int32_t foo = 0;

  char sinkc[64];
  uint32_t sinki = 0;
  char sinkp[64];

  float value = 0.0;

  char sourcec[64];
  uint32_t sourcei = 0;
  char sourcep[64];

  volatile hal_pin_inst_t *sink;
  volatile hal_pin_inst_t *source;

  uint32_t found = 0;

  foo = sscanf(cmd, " %[a-zA-Z_]%lu.%[a-zA-Z0-9_] = %f", sinkc, &sinki, sinkp, &value);
  switch(foo) {
    case 0:
      break;
    case 1:  // search comps
      for(int i = 0; i < hal.comp_inst_count; i++) {
        if(!strncmp(hal.comp_insts[i].comp->name, sinkc, strlen(sinkc))) {
          printf("%s%lu\n", hal.comp_insts[i].comp->name, hal.comp_insts[i].instance);
          found = 1;
        }
      }
      if(!found) {
        printf("<font color=\"FireBrick\">not found: %s</font>\n", cmd);
      }
      break;
    case 2:  // search comps + instance
      for(int i = 0; i < hal.comp_inst_count; i++) {
        if(hal.comp_insts[i].instance == sinki && !strcmp(hal.comp_insts[i].comp->name, sinkc)) {
          for(int j = 0; j < hal.comp_insts[i].comp->pin_count; j++) {
            //volatile hal_comp_inst_t * comp = comp_inst_by_pin_inst(hal.comp_insts[i].pin_insts[j].source->source);
            //printf("%s%lu.%s <= %s%lu.%s = %f\n", hal.comp_insts[i].comp->name, hal.comp_insts[i].instance, hal.comp_insts[i].pins[j], comp->comp->name, comp->instance, (char *)pin_by_pin_inst(hal.comp_insts[i].pin_insts[j].source->source), hal.comp_insts[i].pin_insts[j].source->source->value);
            hal_print_pin(&(hal.comp_insts[i].pin_insts[j]));
            found = 1;
          }
        }
      }
      if(!found) {
        printf("<font color=\"FireBrick\">not found: %s</font>\n", cmd);
      }
      break;
    case 3:
      foo = sscanf(cmd, " %[a-zA-Z_]%lu.%[a-zA-Z0-9_] = %[a-zA-Z_]%lu.%[a-zA-Z0-9_]", sinkc, &sinki, sinkp, sourcec, &sourcei, sourcep);
      if(foo == 6) {  // link pins
        sink   = pin_inst_by_name(sinkc, sinki, sinkp);
        source = pin_inst_by_name(sourcec, sourcei, sourcep);
        if(sink && source) {
          sink->source = source;
          printf("OK %s%lu.%s <= %s%lu.%s = %f\n", sinkc, sinki, sinkp, sourcec, sourcei, sourcep, source->source->value);

        } else if(sink) {
          printf("<font color=\"FireBrick\">not found: %s%lu.%s</font>\n", sourcec, sourcei, sourcep);
        } else {
          printf("<font color=\"FireBrick\">not found: %s%lu.%s</font>\n", sinkc, sinki, sinkp);
        }
      } else {  // search comps + instance + pin
        for(int i = 0; i < hal.comp_inst_count; i++) {
          if(hal.comp_insts[i].instance == sinki && !strcmp(hal.comp_insts[i].comp->name, sinkc)) {
            for(int j = 0; j < hal.comp_insts[i].comp->pin_count; j++) {
              //volatile hal_comp_inst_t * comp = comp_inst_by_pin_inst(hal.comp_insts[i].pin_insts[j].source->source);
              if(!strncmp(hal.comp_insts[i].pins[j], sinkp, strlen(sinkp))) {
                hal_print_pin(&(hal.comp_insts[i].pin_insts[j]));
                //printf("%s%lu.%s <= %s%lu.%s = %f\n", hal.comp_insts[i].comp->name, hal.comp_insts[i].instance, hal.comp_insts[i].pins[j], comp->comp->name, comp->instance, (char *)pin_by_pin_inst(hal.comp_insts[i].pin_insts[j].source->source), hal.comp_insts[i].pin_insts[j].source->source->value);
                found = 1;
              }
            }
          }
        }
        if(!found) {
          printf("<font color=\"FireBrick\">not found: %s</font>\n", cmd);
        }
      }
      break;
    case 4:  // set pin
      sink = pin_inst_by_name(sinkc, sinki, sinkp);
      if(sink) {
        sink->value  = value;
        sink->source = sink;
        printf("OK %s%lu.%s = %f\n", sinkc, sinki, sinkp, value);
      } else {
        printf("<font color=\"FireBrick\">not found: %s%lu.%s</font>\n", sinkc, sinki, sinkp);
      }
      break;
    default:
      printf("<font color=\"FireBrick\">not found: %s</font>\n", cmd);
  }
  return (0);
}

void hal_error(uint32_t error_handler) {
  hal.error_info.active_rt_func  = hal.active_rt_func;
  hal.error_info.active_frt_func = hal.active_frt_func;
  hal.error_info.active_nrt_func = hal.active_nrt_func;
  hal.error_info.error_handler   = error_handler;
  hal_stop();
  hal.hal_state = MISC_ERROR;
}

void fault() {
  printf("trigger fault handler\n");
  volatile uint32_t *ptr = (uint32_t *)0x08010000;
  ptr[0]                 = 1;
}

COMMAND("fault", fault, "trigger fault");

void about() {
  printf("######## software info ########\n");
  printf(
      "%s v%i.%i.%i %s\n",
      version_info.product_name,
      version_info.major,
      version_info.minor,
      version_info.patch,
      version_info.git_version);
  printf("Branch %s\n", version_info.git_branch);
  printf("Compiled %s %s ", version_info.build_date, version_info.build_time);
  printf("by %s on %s\n", version_info.build_user, version_info.build_host);
  printf("GCC        %s\n", __VERSION__);
  printf("newlib     %s\n", _NEWLIB_VERSION);
  // printf("CMSIS      %i.%i\n",__CM4_CMSIS_VERSION_MAIN,__CM4_CMSIS_VERSION_SUB);
  // printf("StdPeriph  %i.%i.%i\n",__STM32F4XX_STDPERIPH_VERSION_MAIN,__STM32F4XX_STDPERIPH_VERSION_SUB1,__STM32F4XX_STDPERIPH_VERSION_SUB2);
  // printf("CPU ID     %lx %lx %lx\n",U_ID[0], U_ID[1], U_ID[2]);
  printf("size: %lu crc:%lx\n", version_info.image_size, version_info.image_crc);

  // printf("######## Bootloader info ########\n");
  // printf(
  //    "%s v%i.%i.%i %s\n",
  //    bt_version_info->product_name,
  //    bt_version_info->major,
  //    bt_version_info->minor,
  //    bt_version_info->patch,
  //    bt_version_info->git_version
  // );
  // printf("Branch %s\n",bt_version_info->git_branch);
  // printf("Compiled %s %s ",bt_version_info->build_date, bt_version_info->build_time);
  // printf("by %s on %s\n",bt_version_info->build_user, bt_version_info->build_host);
  //
  // printf("start:%p ,size:%p ,end%p \n",&_binary_obj_hv_hv_bin_start,&_binary_obj_hv_hv_bin_size,&_binary_obj_hv_hv_bin_end);
}

COMMAND("about", about, "show system infos");
