#include <iostream>
#include <SDL.h>
#include <SDL_opengl.h>
#include <SDL_image.h>
#include <libxml/parser.h>
#include <string.h>
#include "SceneGraph.h"
#include "xml.h"
#include "matrix_calc.h"
#include "TextureHash.h"
#include "texture.h"
#include "TaskManager.h"
#include "polygon_pack.h"

using namespace std;

SceneGraphPtr scene_graph = NULL;
SceneGraphPtr scene_graph_viewer = NULL;

static TextureHash texture_hash;
texture_list list[TABLE_SIZE];

extern int decode(char *cont, FILE *outfile);

static void
no_move(SceneGraphPtr self, void *sgroot_, int screen_w, int screen_h) {}

static void
no_collision(SceneGraphPtr self, void *sgroot_, int screen_w, int screen_h,
             SceneGraphPtr tree) {}

/**
 * 事前に計算したテクスチャの最大縮小率 scale まで、
 * テクスチャを 1/2 縮小していく。
 * このとき、テクスチャは TEXTURE_SPLIT_PIXELx2 のブロック (Tile) で分割し、
 * これらを連続したメモリ領域に格納していく。
 * 以下の (1), (2), (3) を Tapestry と呼ぶ
 *
 * 例 scale = 4 の場合
 *
 *   Tapestry(1) 1/1
 * +---+---+---+---+
 * | 0 | 1 | 2 | 3 |
 * +---+---+---+---+
 * | 4 | 5 | 6 | 7 |   (2) 1/2
 * +---+---+---+---+  +---+---+
 * | 8 | 9 | 10| 11|  | 16| 17|   (3) 1/4
 * +---+---+---+---+  +---+---+  +---+
 * | 12| 13| 14| 15|  | 18| 19|  | 20|
 * +---+---+---+---+  +---+---+  +---|
 *
 * (1)                                                 (2)             (3)
 *  +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
 *  | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * | * | * | 14| 15| 16| 17| 18| 19| 20|
 *  +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
 *
 * @param[in] tex_w         Width of orignal texture
 * @param[in] tex_h         Height of orignal texture
 * @param[in] tex_src       Original texture
 * @param[in] all_pixel_num Tapestry の合計 pixel 数
 * @param[in] scale         テクスチャの最大縮小率 (= 2^n)
 * @return (1) のアドレス
 */


uint32 white[256] __attribute__((aligned(16)));

static uint32*
makeTapestry(TaskManager *manager, uint32 tex_w, uint32 tex_h, uint32 *tex_src,
             int all_pixel_num, int scale_cnt)
{
    if (tex_w==0 && tex_h==0) {
	// non texture case
	uint32 pattern = SDL_BYTEORDER == SDL_LIL_ENDIAN? 0x00ffffff : 0xffffff00; /* OpenGL RGBA masks */
	if (white[0]!=pattern)  { // dumb!
#if 1
            for(int i=0;i<256;i++)
               white[i] = pattern;
#else
	    memset_pattern4(white,&pattern,256);
#endif
        }
	return white;
    }

    uint32 t = 0;
    uint32 diff = TEXTURE_SPLIT_PIXEL;
    uint32 p_diff = 1;

    uint32 *tex_dest = (uint32*)manager->allocate(sizeof(int)*all_pixel_num);
    // uint32 *tex_src_max = (uint32*)( tex_src + tex_h*tex_w);

    // uint32 alpha =  SDL_BYTEORDER == SDL_LIL_ENDIAN? 0xff000000 : 0xff; /* OpenGL RGBA masks */
    uint32 alpha =  tex_src[0];

    while (scale_cnt) {
	// we should use average, except clear one
        for (uint32 y = 0; y < align(tex_h,diff); y += diff) {
            for (uint32 x = 0; x < align(tex_w,diff); x += diff) {
                for (uint32 j = 0; j < diff; j += p_diff) {
                    for (uint32 i = 0; i < diff; i += p_diff) {
                        tex_dest[t++] = 
			    (x+i<tex_w && y+j<tex_h) ? tex_src[(x+i) + tex_w*(y+j)]: alpha;
                    }
                }
            }
        }

        diff <<= 1; p_diff <<= 1;
        scale_cnt >>= 1;
    }

    return tex_dest;
}


/**
 * 何の情報も持ってない SceneGraph の生成
 * 今のところ、とりあえず木構造の繋がりに使うぐらい
 */
SceneGraph::SceneGraph(TaskManager *manager)
{

    init();

    this->matrix = (float*)manager->allocate(sizeof(float)*16);
    this->real_matrix = (float*)manager->allocate(sizeof(float)*16);
    this->texture_info = (texture_list*)manager->allocate(sizeof(texture_list));

    texture_info->texture_id = -1;

    for (int i = 0; i < 16; i++) {
      matrix[i]      = 0;
      real_matrix[i] = 0;
    }


    finalize = &SceneGraph::finalize_copy;

    this->name = "NULLPO";
 
}

/**
 * orig のコピーとして SceneGraph を生成する
 */
SceneGraph::SceneGraph(SceneGraphPtr orig)
{

    init();

    memcpy(this, orig, sizeof(SceneGraph));

    // コピーしない
    //flag_remove = 0;
    //flag_drawable = 1;
    next = NULL;
    prev = NULL;
    last = NULL;

    parent = NULL;
    brother = NULL;
    children = NULL;
    lastChild = NULL;

    finalize = &SceneGraph::finalize_copy;


    frame = 0;
}


/* construct polygon from xmlNode.  */
SceneGraph::SceneGraph(TaskManager *manager, xmlNodePtr surface)
{

    init();

    this->matrix = (float*)manager->allocate(sizeof(float)*16);
    this->real_matrix = (float*)manager->allocate(sizeof(float)*16);
    this->texture_info = (texture_list*)manager->allocate(sizeof(texture_list));
    texture_info->texture_id = -1;
    
    //size : 頂点の数かな
    size = atoi((char *)xmlGetProp(surface,(xmlChar *)"size"));
    name = (char *)xmlGetProp(surface,(xmlChar *)"name");
    parent_name = (char *)xmlGetProp(surface,(xmlChar *)"parent");

    for (int i = 0; i < 16; i++) {
      matrix[i]      = 0;
      real_matrix[i] = 0;
    }

    if (size % 3 != 0) {
      printf("vertex size is error. size %% 3 = %lld\n", size % 3);
    }

    if (size > 0) {
      pp_num = (size/3 + MAX_SIZE_TRIANGLE - 1) / MAX_SIZE_TRIANGLE;
      pp = (PolygonPack*)manager->allocate(sizeof(PolygonPack)*pp_num);
    } else {
      pp_num = 0;
      pp = NULL;
    }

    get_data(manager, surface->children);

    finalize = &SceneGraph::finalize_original;

}

void
SceneGraph::init()
{
    next = NULL;
    prev = NULL;
    last = NULL;

    parent = NULL;
    brother = NULL;
    children = NULL;
    lastChild = NULL;

    stack_xyz[0] = 0.0f;
    stack_xyz[2] = 0.0f;
    stack_xyz[1] = 0.0f;
    stack_angle[0] = 0.0f;
    stack_angle[1] = 0.0f;
    stack_angle[2] = 0.0f;

    size = 0;
    pp_num = 0;

    //data = NULL;
   
    move = no_move;
    collision = no_collision;

    flag_remove = 0;
    flag_drawable = 1;
    sgid = -1;
    gid = -1;

    frame = 0;
}

SceneGraph::~SceneGraph()
{
    (this->*finalize)();
}

/**
 * xml ファイルから生成されたオリジナル SceneGraph なので
 * polygon data を削除
 */
void
SceneGraph::finalize_original()
{
    //delete [] data;

    free(pp);
    free(matrix);
    free(real_matrix);
    free(texture_info);

}

/**
 * SceneGraph ID から生成された、コピー SceneGraph なので
 * polygon data は削除しない。オリジナルの方で削除する。
 */
void
SceneGraph::finalize_copy()
{

    free(matrix);
    free(real_matrix);

}


/**
 * add Children
 * 親の登録と、brother のリストへ加える
 *
 * @param child new child
 */
SceneGraphPtr
SceneGraph::addChild(SceneGraphPtr child)
{
    /* childrenのリストの最後に加える (brother として)*/
    if (this->lastChild != NULL) {
        SceneGraphPtr last = this->lastChild;
        last->brother = child;
	//child->parent = this;
	//return child;
    }
    
    this->lastChild = child;
    
    if (this->children == NULL) {
        this->children = child;
    }
    
    child->parent = this;

    return child;
}


/**
 * add Brother
 * addChild() でも brother の操作をしないといけないので、そっちに回す
 *
 * @param bro new Brother
 */
SceneGraphPtr
SceneGraph::addBrother(SceneGraphPtr bro)
{
    if (this->parent) {
        parent->addChild(bro);
    } else {
        fprintf(stderr, "error : SceneGraph::%s : %s doesn't have parent\n",
                __FUNCTION__, this->name);
    }

    return bro;
}

/* thisの子や子孫にnameのものが存在すればそいつを返す なければNULL.  */
SceneGraphPtr
SceneGraph::searchSceneGraph(const char *name)
{
    SceneGraphPtr tmp;
    SceneGraphPtr result;

    /* 本人か  */
    if( 0==strcmp(this->name, name) ) return this;

    /* 子供から再帰的に探す  */
    for(tmp = this->children; tmp; tmp = tmp->next) {
        if ((result=tmp->searchSceneGraph(name)) != NULL)
            return result;
    }

    /* 無かったら NULL.  */
    return NULL;
}

void
SceneGraph::tree_check()
{
    SceneGraphPtr t = this;

    while(t)
    {
        cout << "my_name : " << t->name << endl;
        if(t->children != NULL)
        {
            cout << "--move children : " << t->children->name << endl;
            t = t->children;
        }
        else if(t->brother != NULL)
        {
            cout << "--move brother : " << t->brother->name << endl;
            t = t->brother;
        }
        else
        {
            while(t)
            {
                if(t->brother != NULL)
                {
                    cout << "--move brother : " << t->brother->name << endl;
                    t = t->brother;
                    break;
                }
                else
                {
                    if(t->parent)
                    {
                        cout << "--move parent : " << t->parent->name << endl;
                    }
                    t = t->parent;
                }
            }
        }
    }
}


void
SceneGraph::print_member()
{
    cout << "size = " << size << endl;
    cout << "name = " << name << endl;
    cout << "parent_name = " << parent_name << endl;

    if (parent != NULL) {
        cout << "parent->name = " << parent->name << endl;
    }

    if (children != NULL) {
        cout << "children->name = " << children->name << endl;
    }
}


/*
 * surface nodeからポリゴンの情報を読み出す 再帰しない
 */
void
SceneGraph::get_data(TaskManager *manager, xmlNodePtr cur)
{
    //char *image_name;

    for(;cur;cur=cur->next)
    {
        if(!xmlStrcmp(cur->name,(xmlChar*)"coordinate"))
        {
            char *cont = (char *)xmlNodeGetContent(cur);
            pickup_coordinate(cont);
        }
        else if(!xmlStrcmp(cur->name,(xmlChar*)"normal"))
        {
            char *cont = (char *)xmlNodeGetContent(cur);
            pickup_normal(cont);
        }
        else if(!xmlStrcmp(cur->name,(xmlChar*)"model"))
        {
            char *cont = (char *)xmlNodeGetContent(cur);
            pickup_model(cont);
        }
        else if(!xmlStrcmp(cur->name,(xmlChar*)"texture"))
        {
            char *cont = (char *)xmlNodeGetContent(cur);
            pickup_texture(cont);
        }
        else if(!xmlStrcmp(cur->name,(xmlChar*)"imageflag"))
        {
            int id;
            char *filename = (char *)xmlGetProp(cur, (xmlChar *)"name");
            texture_hash.hash_regist(filename, id);
        }
        else if(!xmlStrcmp(cur->name,(xmlChar*)"image"))
        {
            get_image(manager, cur);
        }
    }
}

SDL_Surface*
SceneGraph::load_decode_image(char *image_name, xmlNodePtr cur)
{
    int fd = mkstemp(image_name);
    FILE *outfile = fdopen(fd, "wb");

    if (NULL == outfile) {
            cout << "error open file\n";
            return 0;
    }

    char *cont = (char *)xmlNodeGetContent(cur);
    //decode(cont, image_name);
    decode(cont, outfile);
    fclose(outfile);


/**
 * image を 32bit(RGBA) に変換する
 */
    SDL_Surface *texture_image = IMG_Load(image_name);
    if (!texture_image) return 0;
    SDL_Surface *tmpImage
        = SDL_CreateRGBSurface(SDL_HWSURFACE, texture_image->w,
                             texture_image->h, 32, redMask,
                            greenMask, blueMask, alphaMask);

      //= SDL_CreateRGBSurface(SDL_HWSURFACE, 0,
      //                       0, 32, redMask,
      //                       greenMask, blueMask, alphaMask);

    SDL_Surface *converted;
    converted = SDL_ConvertSurface(texture_image, tmpImage->format,
                                   SDL_HWSURFACE);

    //SDL_SetAlpha(converted, 0, 0);

    if (converted != NULL) {
        SDL_FreeSurface(texture_image);
        texture_image = converted;
    }

    // this->gl_tex = SDL_GL_LoadTexture(texture_image);
    return texture_image;
}

int
SceneGraph::makeTapestries(TaskManager *manager, SDL_Surface *texture_image, int id) {
                uint32 *tapestry;
                int scale = 1;
                int tex_w = texture_image->w;
                int tex_h = texture_image->h;
                int all_pixel_num = 0;
		int nw = tex_w;
		int nh = tex_h;

                /**
                 * テクスチャの w or h が 8 pixel で分割できる間、
                 * 1/2 の縮小画像を作る。
                 * ここでは、最大の scale (1/scale) を見つける
                 *
                 * (ex)
                 *  (128,128) => 64,64 : 32,32: 16,16 : 8,8
                 *     scale = 16
                 *  (128, 64) => 64,32 : 32,16: 16,8
                 *     scale = 8
                 *  8 pixcel align してない場合は、透明に 8 pixcel に拡張する
                 *  (200, 57) => 200,64 : 100,32 : 56,16: 32,8  (16,1 : 8,1 まで落すべき? 32byte)
                 *     scale = 32
                 */

		do {
		    tex_w = align(tex_w,8);
		    tex_h = align(tex_h,8);
                    all_pixel_num += tex_w * tex_h;
                    tex_w >>= 1; /* tex_w /= 2 */
                    tex_h >>= 1;
                    scale <<= 1; /* scale *= 2 */
                } while( tex_w >8 || tex_h > 8 );

                scale >>= 1;     // 必ず 1 以上になる

                tapestry = makeTapestry(manager, texture_image->w, texture_image->h,
                                        (uint32*)texture_image->pixels,
                                        all_pixel_num, scale);

                list[id].t_w = nw;
                list[id].t_h = nh;
                list[id].pixels_orig = (Uint32*)texture_image->pixels;
                list[id].pixels = tapestry;
                list[id].scale_max = scale;
                list[id].texture_image = texture_image;

                return id;
    }

void
SceneGraph::get_image(TaskManager *manager, xmlNodePtr cur)
{
    char image_name[20] = "/tmp/image_XXXXXX";
    char *filename = (char *)xmlGetProp(cur, (xmlChar *)"name");

    if (filename == NULL || filename[0] == 0) {
        return;
    }

    /**
     * image_name を既に Load していれば何もしない
     */
    int tex_id;
    if (!texture_hash.hash_regist(filename, tex_id)) {

        SDL_Surface *texture_image = load_decode_image(image_name, cur);
	if (texture_image==0) {
	    printf("Can't load image %s\n",filename);
	    exit(0);
	}
        texture_info->texture_id = makeTapestries(manager, texture_image, tex_id);
	tex_id = texture_info->texture_id;

        if (unlink(image_name)) {
            cout << "unlink error\n";
        }
    } else {
        /**
         * 以前に Load されている Texture を共用
         */
        texture_info->texture_id = tex_id;
    }

      // こんなことすると list[] のいみあるのかなーと
      // 微妙に思う、自分で書き換えた感想 by gongo
      texture_info->t_w = list[tex_id].t_w;
      texture_info->t_h = list[tex_id].t_h;;
      texture_info->pixels_orig = list[tex_id].pixels_orig;
      texture_info->pixels = list[tex_id].pixels;
      texture_info->scale_max = list[tex_id].scale_max;
      texture_info->texture_image = list[tex_id].texture_image;

}


void
SceneGraph::delete_data()
{
    SceneGraphPtr n = this->next, m;

    //n = this;
    //delete [] n->data;

    if (next) {
        while (n) {
            m = n->next;
            delete n;
            n = m;
        }
    }
}

/* move_func 実行 sgroot 渡す */
void
SceneGraph::move_execute(int w, int h)
{
    (*move)(this, this->sgroot, w, h);
}

void
SceneGraph::collision_check(int w, int h, SceneGraphPtr tree)
{
    (*collision)(this, this->sgroot, w, h, tree);
}

void
SceneGraph::create_sg_execute()
{
    (*create_sg)(this->sgroot, property, update_property);
}

void
SceneGraph::set_move_collision(move_func new_move)
{
    this->move = new_move;
}

void
SceneGraph::set_move_collision(collision_func new_collision)
{
    this->collision = new_collision;
}

void
SceneGraph::set_move_collision(move_func new_move,
                               collision_func new_collision)
{
    this->move = new_move;
    this->collision = new_collision;
}


void
SceneGraph::set_move_collision(move_func new_move,
                               collision_func new_collision, void *sgroot_)
{
    this->move = new_move;
    this->collision = new_collision;
    // add
    this->sgroot = sgroot_;
}

void
SceneGraph::set_move_collision(move_func new_move,
                               collision_func new_collision, create_sg_func new_create_sg)
{
    this->move = new_move;
    this->collision = new_collision;
    this->create_sg = new_create_sg;
}

void
SceneGraph::add_next(SceneGraphPtr next)
{
    /* next のリストの最後に加える */
    if (this->next != NULL) {
        SceneGraphPtr tmp = this->last;
        tmp->next = next;
    } else {
        this->next = next;
    }

    this->last = next;
}

/**
 * SceneGraph の clone
 * @return clone SceneGraph
 */
SceneGraphPtr
SceneGraph::clone(TaskManager *manager) {
    SceneGraphPtr p = new SceneGraph(this);
    
    p->matrix = (float*)manager->allocate(sizeof(float)*16);
    p->real_matrix = (float*)manager->allocate(sizeof(float)*16);
    //これはいらない？
    p->texture_info = (texture_list*)manager->allocate(sizeof(texture_list));

    for (int i = 0; i < 16; i++) {
      p->matrix[i] = this->matrix[i];
      p->real_matrix[i] = this->real_matrix[i];
    }

    memcpy(p->texture_info, this->texture_info, sizeof(texture_list));

    return p;
}

/**
 * SceneGraph の clone
 * 予め allocate されてる領域への placement new を行う
 *
 * @param buf clone 領域
 * @return clone SceneGraph
 */
SceneGraphPtr
SceneGraph::clone(void *buf) {
    SceneGraphPtr p = new(buf) SceneGraph(this);
    return p;
}

void
SceneGraph::remove()
{
    this->flag_remove = 1;
}

/**
 * tree から node を削除する
 *
 * @param tree SceneGraphTree
 * @return node削除後の SceneGraphTree
 */
SceneGraphPtr
SceneGraph::realRemoveFromTree(SceneGraphPtr tree)
{
    SceneGraphPtr node = this;
    SceneGraphPtr parent = node->parent;
    SceneGraphPtr ret = tree;

    if (parent) {
        SceneGraphPtr brother = parent->children;
        SceneGraphPtr p, p1 = NULL;

        p = brother;
        if (p) {
            if (p == node) {
                parent->children = NULL;
                parent->lastChild = NULL;
            } else {
                p1 = p->brother;

                while (p1 && p1 != node) {
                    p1 = p1->brother;
                    p = p->brother;
                }

                if (p1) {
                    p->brother = p1->brother;

                    // node が最後尾なら、lastChild を変更
                    if (parent->lastChild == p1) {
                        parent->lastChild = p;
                    }
                } else {
                    // Can't find remove node
                }
            }
        }
    } else {
        // 親が居ない = tree root なので
        // NULL を返す
        ret = NULL;
    }

    return ret;
}

/**
 * list から node を削除する
 *
 * @param list SceneGraphList
 * @return node削除後の SceneGraphList
 */
SceneGraphPtr
SceneGraph::realRemoveFromList(SceneGraphPtr list)
{
    SceneGraphPtr node = this;
    SceneGraphPtr prev = node->prev;
    SceneGraphPtr next = node->next;
    SceneGraphPtr ret = list;

    if (prev) {
        prev->next = next;
    } else {
        ret = next;
    }

    if (next) {
        next->prev = prev;
    }

    return ret;
}

int
SceneGraph::isRemoved()
{
    return flag_remove;
}

/**
 * 平行移動
 *
 * @param x Ttranslate in the x direction
 * @param y Ttranslate in the y direction
 * @param z Ttranslate in the z direction
 */
void
SceneGraph::translate(float x, float y, float z)
{
    this->xyz[0] = x;
    this->xyz[1] = y;
    this->xyz[2] = z;
}

/**
 * x 軸方向への平行移動
 *
 * @param x Ttranslate in the x direction
 */
void
SceneGraph::translateX(float x)
{
    this->xyz[0] = x;
}

/**
 * y 軸方向への平行移動
 *
 * @param y Ttranslate in the y direction
 */
void
SceneGraph::translateY(float y)
{
    this->xyz[1] = y;
}

/**
 * z 軸方向への平行移動
 *
 * @param z Ttranslate in the z direction
 */
void
SceneGraph::translateZ(float z)
{
    this->xyz[2] = z;
}

/* end */
