#!/usr/bin/env python

import sys
import os
import yaml

if len(sys.argv) < 2:
    print("no argument given")
    exit(0)

if not os.path.isfile(sys.argv[1]):
    print("argument is not a correct file path")
    exit(0)

cfg = yaml.load(open(sys.argv[1]))

if not os.path.exists("src/auto"):
    os.mkdir("src/auto")

if not os.path.exists("python"):
    os.mkdir("python")


s = '''/**
 * \\file %s.h
 * 
 * \\brief
 *   This file is autogenerated! Do not touch it, it will be overwritten
 *   while reinstalling this library!!!
 *
 **/

#include <c_bagel/bagel.h>
#include <c_bagel/bg_impl.h>
#include <c_bagel/bg_node.h>
#include <assert.h>
#include <stdlib.h>


/* CAN WE GET NAMESPACE PROBLEMS HERE? */


#ifdef __cplusplus
extern "C" {
#endif

/**** private structs ****/

''' % cfg["name"]

s += "typedef struct "+cfg["name"]+"_input_data {\n"
for i in cfg["inputs"]:
    s += "  bg_real " + i["name"].replace("/", "_") + ";\n"
s += "}"+cfg["name"]+"_input_data;\n"
s += "\n"
s += "typedef struct "+cfg["name"]+"_output_data {\n"
for i in cfg["outputs"]:
    s += "  bg_real " + i["name"].replace("/", "_") + ";\n"
s += "}"+cfg["name"]+"_output_data;\n"

s += '''
typedef struct %s_private_data {
  %s_input_data inputs;
  %s_output_data outputs;
  void *intern_data;
}%s_private_data;


/**** private functions ****/

void install_%s(void);
bg_error init_%s(bg_node_t *node);
bg_error deinit_%s(bg_node_t *node);
bg_error evaluate_%s(bg_node_t *node, %s_private_data* p_data);

#ifdef __cplusplus
}
#endif

''' % (cfg["name"],cfg["name"],cfg["name"],cfg["name"],
       cfg["name"],cfg["name"],cfg["name"],cfg["name"],cfg["name"])

with open("src/auto/%s.h" % cfg["name"], "w") as f:
    f.write(s)


s = '''/**
 * \\file %s.c
 * 
 * \\brief
 *   This file is autogenerated! Do not touch it, it will be overwritten
 *   while reinstalling this library!!!
 *
 **/

#include <auto/%s.h>
#include <string.h>

/**** initialization of an instance of the node ****/

static bg_error init_%s_(bg_node_t *node) {
  %s_private_data *p_data = (%s_private_data*)malloc(sizeof(%s_private_data));
  bg_error err;
  bg_node_create_input_ports(node, node->type->input_port_cnt);
  bg_node_create_output_ports(node, node->type->output_port_cnt);
  err = bg_error_get();
  if(err != bg_SUCCESS) {
    printf("error in node initialization: %s\\n");
  }
  node->_priv_data = (void*)p_data;
  return init_%s(node);
}


/**** destructor of a node ****/

static bg_error deinit_%s_(bg_node_t *node) {
  bg_error err;
  bg_node_remove_input_ports(node);
  bg_node_remove_output_ports(node);
  err = bg_error_get();
  if(err != bg_SUCCESS) {
    printf("error in node deinitialization: %s\\n");
  }
  err = deinit_%s(node);
  free(node->_priv_data);
  return err;
}


/**** do the calculation ****/

static bg_error eval_%s_(bg_node_t *node) {
  %s_private_data *p_data = (%s_private_data*)node->_priv_data;
  bg_error err;
''' % (cfg["name"], cfg["name"], cfg["name"], cfg["name"],
       cfg["name"], cfg["name"], cfg["name"], cfg["name"],
       cfg["name"], cfg["name"], cfg["name"], cfg["name"],
       cfg["name"], cfg["name"])

index = 0
for i in cfg["inputs"]:
    s += "  p_data->inputs.%s = node->input_ports[%d]->value;\n" %(i["name"].replace("/", "_"), index)
    index += 1

s += "\n  err = evaluate_%s(node, (%s_private_data*)node->_priv_data);\n\n" % (cfg["name"], cfg["name"])
index = 0
for i in cfg["outputs"]:
    s += "  node->output_ports[%d]->value = p_data->outputs.%s;\n" %(index, i["name"].replace("/", "_"))
    index += 1

s += "\n  return err;\n}\n"


s += '''
/**** interval support is not planed yet ****/

static bg_error eval_%s_interval_(bg_node_t *node) {
  return bg_ERR_NOT_IMPLEMENTED;
  (void)node;
}



/**** registration part ****/

static node_type_t %s_types[] = {
/*{type_id, name, input_cnt, output_cnt, init, deinit, eval}*/
  {bg_NODE_TYPE_EXTERN, "%s", %d, %d, init_%s_, deinit_%s_, eval_%s_, eval_%s_interval_},
  /* sentinel */
  {0, NULL, 0, 0, NULL, NULL, NULL, NULL}
};


/**** initialization when the library is loaded ****/

void init_nodes(void) {
  /* this function could be used to initialize static stuff */
  bg_extern_node_type_register(%s_types);
  install_%s();
}

/**** get info of node ****/

void node_info(char **name, size_t *num_inputs, size_t *num_outputs) {
  *name = malloc(sizeof("%s")+1);
  strcpy(*name, "%s");
  *num_inputs = %d;
  *num_outputs = %d;
}

''' % (cfg["name"], cfg["name"], cfg["name"], len(cfg["inputs"]), len(cfg["outputs"]), cfg["name"], cfg["name"], cfg["name"], cfg["name"], cfg["name"], cfg["name"], cfg["name"], cfg["name"], len(cfg["inputs"]), len(cfg["outputs"]))

with open("src/auto/%s.c" % cfg["name"], "w") as f:
    f.write(s)


s = "def %s(" % cfg["name"]
first = True
for i in cfg["inputs"]:
    if not first:
        s+= ", "
    s += i["name"].replace("/", "_")
    first = False
s += "):\n"

for i in cfg["outputs"]:
    s += "    "+i["name"].replace("/", "_")+" = 0\n"

s += "    return ("
first = True
for i in cfg["outputs"]:
    if not first:
        s+= ", "
    s += i["name"].replace("/", "_")
    first = False
s += ")\n"

with open("python/%s.py" % cfg["name"], "w") as f:
    f.write(s)
