#include <math.h>
#include <stdlib.h>
#include "SceneGraphRoot.h"
#include "MainLoop.h"
#include "property_chain.h"
#include "types.h"
#include "MyFunc.h"
#include "matrix_calc.h"
#include "SgChange.h"

#define FALSE 0
#define TRUE !FALSE
static const int PROPERTY_LENGTH = 50;
static double chain_width = 10;

typedef struct {
    float xyz[3];
    float angle[3];
    float stack_xyz[3];
    float next_xyz[3];
    float v_xyz[3];
    float next_v_xyz[3];
    int property_index;
    int parent_index;
    int have_parent;
    int can_move;
    memaddr parent;
    memaddr children;
    memaddr node;
    int sgid;
} *PropertyPtr, Property;


Property *property, *update_property;

// prototype
static void collision(SceneGraphPtr node, void *sgroot_, int screen_w, int screen_h, SceneGraphPtr tree);
static void move(SceneGraphPtr node, void *sgroot_, int screen_w, int screen_h);
static void createSceneGraphFromProperty(SchedTask *s, void *sgroot, void *arg1);
static void set_property(Property *p, SceneGraphPtr sg, int index);
static void apply_property(SceneGraphPtr sg, Property *p);
static void regist_task(SceneGraphRoot *sgroot);
static void set_relation(SceneGraphPtr parent, SceneGraphPtr child);

static void
init_chain(Property *p) {
    for (int i = 0; i < 3; i++) {
	p->xyz[i] = 0;
	p->angle[i] = 0;
	p->stack_xyz[i] = 0;
	p->next_xyz[i] = 0;
	p->v_xyz[i] = 0;
	p->next_v_xyz[i] = 0;
    }

    p->property_index = 0;
    p->parent_index = 0;
    p->have_parent = 0;
    p->can_move = TRUE;
    p->parent = NULL;
    p->children = NULL;
    p->node = NULL;
}

static void
move(SceneGraphPtr node, void *sgroot_, int screen_w, int screen_h)
{
    SceneGraphRoot *sgroot = (SceneGraphRoot *)sgroot_;
    HTaskPtr property_task = sgroot->move_exec_task;

    property_task->add_inData(property, sizeof(Property)*PROPERTY_LENGTH);
    property_task->add_outData(update_property, sizeof(Property)*PROPERTY_LENGTH);
    property_task->set_cpu(SPE_ANY);
    property_task->set_post(createSceneGraphFromProperty, (void *)sgroot, 0);
    property_task->spawn();
}

static void
collision(SceneGraphPtr node, void *sgroot_, int screen_w, int screen_h,
	  SceneGraphPtr tree)
{
}

static void
createSceneGraphFromProperty(SchedTask *s, void *sgroot_, void *arg1)
{
    SceneGraphRoot *sgroot = (SceneGraphRoot *)sgroot_;
    SceneGraphPtr camera = sgroot->camera;
    SceneGraphPtr p_node;
    SceneGraphPtr root;
    
    // ここが allExecute の tree をたどって clone して行くところに相当する
    Property *p;
    
    for (int i = 0; i < PROPERTY_LENGTH; i++) {
	p = &update_property[i];
	SceneGraphPtr node = sgroot->createSceneGraph(p->sgid);
	apply_property(node, p);    
    }
	
    for (int j = 0; j < PROPERTY_LENGTH; j++) {
	p = &update_property[j];
	p_node = (SceneGraphPtr)p->node;
	//float scale[] = {1,1,1};
	if (p->have_parent) {
	    SceneGraphPtr parent = (SceneGraphPtr)update_property[p->parent_index].node;
	    parent->addChild(p_node);
	    get_matrix(p_node->matrix, p_node->angle, p_node->xyz, parent->matrix);
	    get_matrix(p_node->real_matrix, p_node->angle, p_node->xyz, parent->real_matrix);
	} else {
	  get_matrix(p_node->matrix, p_node->angle, p_node->xyz, camera->matrix);
	  get_matrix(p_node->real_matrix, p_node->angle, p_node->xyz, camera->real_matrix);
	}
    }

    root = (SceneGraphPtr)update_property[0].node;
    root->set_move_collision(move, collision);
    sgroot->setSceneData(root);

    Property *tmp = property;
    property = update_property;
    update_property = tmp;

    sgroot->move_finish();
}

static void
apply_property(SceneGraphPtr node, Property *p)
{
    for (int i = 0; i < 3; i++) {
	node->xyz[i] = p->xyz[i];
	node->angle[i] = p->angle[i];
	node->stack_xyz[i] = p->stack_xyz[i];
    }
    p->node = (memaddr)node;
    node->property = (memaddr)p;
}

/*
  ここで必要な値をプロパティに格納
 */
static void
set_property(Property *p, SceneGraphPtr node, int index)
{
    for (int i = 0; i < 3; i++) {
	p->xyz[i] = node->xyz[i];
	p->angle[i] = node->angle[i];
	p->stack_xyz[i] = node->stack_xyz[i];
    }
    p->parent = (memaddr)node->parent;
    p->children = (memaddr)node->children;
    p->property_index = index;    
    p->sgid = node->sgid;
    p->node = (memaddr)node;
    node->property = (memaddr)p;
}

static void
regist_task(SceneGraphRoot *sgroot)
{
    TaskManager *manager = sgroot->tmanager;
    HTaskPtr task = manager->create_task(ChainTask);
    // sgroot->setExecTask(task); とやるべき？
    sgroot->move_exec_task = task;
}

/*
  Property に親子関係を書き込む
  これも API に入れちゃっていいかな
 */
static void
set_relation(SceneGraphPtr parent, SceneGraphPtr child)
{
    Property *p = (Property *)parent->property;
    Property *c = (Property *)child->property;
    p->children = (memaddr)child;
    c->parent = (memaddr)parent;
    c->parent_index = p->property_index;
    c->have_parent = 1;
}

MainLoopPtr 
property_chain::init(Viewer *viewer, int screen_w, int screen_h)
{
    // SgChange を使うための2行
    SgChange *sgroot = new SgChange(viewer);
    sgroot->run_init();
    // 上で書いた regist_task() を登録
    // sgroot->appTaskRegist(regist_task); がいいかな
    sgroot->sgroot_A->appTaskRegist(regist_task);

    property        = (Property *)sgroot->manager->allocate(sizeof(Property)*PROPERTY_LENGTH);
    update_property = (Property *)sgroot->manager->allocate(sizeof(Property)*PROPERTY_LENGTH);

    // property の初期化 application ごとに固有
    for(int i = 0; i < PROPERTY_LENGTH; i++) {
	init_chain(&property[i]);
    }

    SceneGraphPtr root_chain, chain;
    sgroot->createFromXMLfile("xml_file/chain.xml");
    root_chain = sgroot->createSceneGraph("CHAIN");
    //root_chain->xyz[0] = screen_w / 2;
    //root_chain->xyz[0] = screen_w / 4;
    root_chain->xyz[0] = 300.0f;
    root_chain->xyz[1] = 0.0f;
    root_chain->set_move_collision(move, collision);
    set_property(&property[0], root_chain, 0);
    
    for (int i = 1; i < PROPERTY_LENGTH; i++) {
	chain = sgroot->createSceneGraph("CHAIN");
	chain->xyz[0] = 0;
	chain->xyz[1] = chain_width * i;
	chain->angle[1] = -90 * (i % 2);
	root_chain->addChild(chain);
	set_property(&property[i], chain, i);
	set_relation(root_chain, chain);
    }

    // root の set_property は SceneGraph を作成してから実行
    // addChild したら set_relation しないとだめ、あんまりにもあんまり

    /*
      apply_property は post_func の createSceneGraphFromProperty 中で使う
      apply_property は sgroot に持たせてもいいと思います○
    */
    
    sgroot->setSceneData(root_chain);

    return sgroot;
}

extern Application *
application() {
    return new property_chain();
}

const char *usr_help_str = "Usage: ./test_nogl [OPTION]\n";

extern int init(TaskManager *manager, int argc, char *argv[]);
extern void task_initialize();
static void TMend(TaskManager *manager);

int
TMmain(TaskManager *manager, int argc, char *argv[])
{
    task_initialize();
    manager->set_TMend(TMend);
    return init(manager, argc, argv);

}

void
TMend(TaskManager *manager)
{
    printf("test_nogl end\n");
}

/* end */
