

#include "parser.h"
#include "parser_v2.h"
#include "internal_gl.h"
#include <string>
#include "util.h"
#include <vector>

typedef enum{
    DYNAMIC=0, STATIC
}Alloc_t;

struct Animator{
    SimParser *Parser;
    int Start;
    int Ending;
    int Current;
    int ActiveIndex;
    bool Paused;
    Alloc_t AllocationType;
    std::string BasePath;
    std::string Extension;
    FrameInfo *LastInfo;
    std::vector<FrameInfo *> Frames;
    std::string DetachedFile;
    bool IsDetached;
    std::string v2format;
    Parser_v2 *parser2;
};

static void Animator_load_to_memory(Animator *animator){
    std::cout << "Loading files to memory" << std::endl;
    for(int i = animator->Start; i <= animator->Ending; i += 1){
        std::string Target = animator->BasePath +
            MAKE_STR(i, int) + animator->Extension;
        animator->LastInfo = animator->Parser->readDetachedFile(Target.c_str());
        animator->Frames.push_back(animator->LastInfo);
        std::cout << "Loaded " << Target << std::endl;
    }
    animator->LastInfo = animator->Frames.front();
}

static void Animator_clear_memory(Animator *animator){
    for(int i = 0; i < (int)(animator->Frames.size()); i += 1){
        FrameInfo *Frame = animator->Frames.at(i);
        Frame->clear();
        delete Frame;
    }
    animator->Frames.clear();
}

Animator *Animator_new(const char *BasePath, const char *Extension,
                       int Origin, int Ending, Alloc_t Type,
                       const char *format)
{
    Animator *animator = new Animator;
    animator->Start = Origin;
    animator->Ending = Ending;
    animator->BasePath = std::string(BasePath);
    animator->Extension = std::string(Extension);
    animator->Current = animator->Start;
    animator->ActiveIndex = 0;
    animator->Paused = false;
    animator->LastInfo = 0;
    animator->v2format = format;
    animator->AllocationType = Type;
    animator->Parser = new SimParser(format);
    animator->parser2 = 0;
    if(animator->AllocationType == STATIC){
        Animator_load_to_memory(animator);
    }
    return animator;
}

Animator * Animator_new_with_v2(const char *BasePath, const char *Extension,
                                int Origin, int Ending, const char *format)
{
    Animator *animator = new Animator;
    animator->Start = Origin;
    animator->Ending = Ending;
    animator->BasePath = std::string(BasePath);
    animator->Extension = std::string(Extension);
    animator->Current = animator->Start;
    animator->ActiveIndex = 0;
    animator->Paused = false;
    animator->LastInfo = new FrameInfo;
    animator->v2format = format;
    animator->parser2 = Parser_v2_new(format);
    Parser_v2_load_files(animator->parser2, BasePath, Extension, Origin, Ending);
    animator->LastInfo->f_VAO = animator->parser2->gldata.vao;
    animator->LastInfo->c_VAO = animator->parser2->gldata.cvao;
    animator->LastInfo->cell_count = animator->parser2->gldata.dPtr->cells;
    animator->LastInfo->part_count = Parser_v2_get_current_part_count(animator->parser2);
    return animator;
}

Animator * Animator_new_with_v2(const char *BasePath, const char *Extension,
                                int Origin, int Ending, const char *format,
                                Parser_Opts opts)
{
    
    Animator *animator = new Animator;
    animator->Start = Origin;
    animator->Ending = Ending;
    animator->BasePath = std::string(BasePath);
    animator->Extension = std::string(Extension);
    animator->Current = animator->Start;
    animator->ActiveIndex = 0;
    animator->Paused = false;
    animator->LastInfo = new FrameInfo;
    animator->v2format = format;
    animator->parser2 = Parser_v2_new_with_opts(format, opts);
    Parser_v2_load_files(animator->parser2, BasePath, Extension, Origin, Ending);
    animator->LastInfo->f_VAO = animator->parser2->gldata.vao;
    animator->LastInfo->c_VAO = animator->parser2->gldata.cvao;
    animator->LastInfo->cell_count = animator->parser2->gldata.dPtr->cells;
    animator->LastInfo->part_count = Parser_v2_get_current_part_count(animator->parser2);
    return animator;
}

Animator * Animator_new_with_v2(const char *BasePath, const char *Extension,
                                int Origin, int Ending, const char *format,
                                Parser_Opts opts, const char *ExtraBase,
                                const char *ExtraFormat)
{
    Animator *animator = new Animator;
    animator->Start = Origin;
    animator->Ending = Ending;
    animator->BasePath = std::string(BasePath);
    animator->Extension = std::string(Extension);
    animator->Current = animator->Start;
    animator->ActiveIndex = 0;
    animator->Paused = false;
    animator->LastInfo = new FrameInfo;
    animator->v2format = format;
    animator->parser2 = Parser_v2_new_with_opts(format, opts);
    Parser_v2_add_extra_files(animator->parser2, ExtraBase, Extension, ExtraFormat);
    Parser_v2_load_files(animator->parser2, BasePath, Extension, Origin, Ending);
    animator->LastInfo->f_VAO = animator->parser2->gldata.vao;
    animator->LastInfo->c_VAO = animator->parser2->gldata.cvao;
    animator->LastInfo->cell_count = animator->parser2->gldata.dPtr->cells;
    animator->LastInfo->part_count = Parser_v2_get_current_part_count(animator->parser2);
    return animator;
}

Animator *Animator_new(const char *BasePath, const char *Extension,
                       int Origin, int Ending, Alloc_t Type)
{
    Animator *animator = new Animator;
    animator->Start = Origin;
    animator->Ending = Ending;
    animator->BasePath = std::string(BasePath);
    animator->Extension = std::string(Extension);
    animator->Current = animator->Start;
    animator->ActiveIndex = 0;
    animator->Paused = false;
    animator->LastInfo = 0;
    animator->AllocationType = Type;
    animator->IsDetached = false;
    animator->Parser = new SimParser("pb");
    if(animator->AllocationType == STATIC){
        Animator_load_to_memory(animator);
    }
    return animator;
}

static void set_vao_binding_points(FrameInfo *info, std::string format){
    std::string p("pP");
    std::string n("nN");
    std::string b("bB");
    info->has_0 = 0;
    info->has_1 = 0;
    info->has_2 = 0;
    
    if(format.find_first_of(p) != std::string::npos)
        info->has_0 = 1;
    
    if(format.find_first_of(b) != std::string::npos)
        info->has_1 = 1;
    
    if(format.find_first_of(n) != std::string::npos)
        info->has_2 = 1;
}

int Animator_is_last_frame_v2(Animator *animator){
    return (!(animator->ActiveIndex+1 < animator->parser2->gldata.count));
}

void Animator_reset(Animator *animator){
    animator->ActiveIndex = 0;
    Parser_v2_next(animator->parser2, animator->ActiveIndex);
    animator->LastInfo->f_VAO = animator->parser2->gldata.vao;
    animator->LastInfo->part_count = Parser_v2_get_current_part_count(animator->parser2);
    animator->LastInfo->cell_count = animator->parser2->gldata.dPtr->cells;
    animator->LastInfo->c_VAO = animator->parser2->gldata.cvao;
    set_vao_binding_points(animator->LastInfo, animator->v2format);
}

FrameInfo * Animator_advance_with_v2(Animator * animator){
    if(animator->Paused) return animator->LastInfo;
    animator->ActiveIndex = animator->parser2->gldata.active + 1;
    if(!(animator->ActiveIndex < animator->parser2->gldata.count)){
        //return animator->LastInfo;
        animator->ActiveIndex = 0;
    }
    
    Parser_v2_next(animator->parser2, animator->ActiveIndex);
    animator->LastInfo->f_VAO = animator->parser2->gldata.vao;
    animator->LastInfo->part_count = Parser_v2_get_current_part_count(animator->parser2);
    animator->LastInfo->cell_count = animator->parser2->gldata.dPtr->cells;
    animator->LastInfo->c_VAO = animator->parser2->gldata.cvao;
    set_vao_binding_points(animator->LastInfo, animator->v2format);
    return animator->LastInfo;
}

FrameInfo * Animator_advance(Animator *animator){
    if(animator->Paused) return animator->LastInfo;
    
    animator->Current += 1;
    animator->ActiveIndex += 1;
    if(animator->Current > animator->Ending){
        animator->Current = animator->Start;
        animator->ActiveIndex = 0;
    }
    if(animator->AllocationType == STATIC){
        animator->LastInfo = animator->Frames.at(animator->ActiveIndex);
    }else{
        std::string Target = animator->BasePath +
            MAKE_STR(animator->ActiveIndex, int) + animator->Extension;
        
        if(animator->LastInfo){
            animator->LastInfo->clear();
        }
        
        animator->LastInfo = animator->Parser->readDetachedFile(Target.c_str());
    }
    animator->LastInfo->c_VAO = 0;
    return animator->LastInfo;
}

FrameInfo * Animator_to_specific(Animator *animator, const char *path){
    animator->LastInfo = animator->Parser->readDetachedFile(path);
    animator->DetachedFile = std::string(path);
    animator->IsDetached = true;
    animator->LastInfo->c_VAO = 0;
    return animator->LastInfo;
}

FrameInfo * Animator_reframe(Animator *animator){
    if(animator->IsDetached){
        animator->LastInfo = animator->Parser->readDetachedFile(animator->DetachedFile.c_str());
        animator->LastInfo->c_VAO = 0;
        return animator->LastInfo;
    }
    
    if(animator->AllocationType == STATIC){
        Animator_clear_memory(animator);
        Animator_load_to_memory(animator);
        animator->LastInfo = animator->Frames.at(animator->ActiveIndex);
        animator->LastInfo->c_VAO = 0;
    }else{
        if(animator->LastInfo){
            animator->LastInfo->clear();
        }
        
        std::string Target = animator->BasePath +
            MAKE_STR(animator->Current, int) + animator->Extension;
        
        animator->LastInfo = animator->Parser->readDetachedFile(Target.c_str());
        animator->LastInfo->c_VAO = 0;
    }
    
    return animator->LastInfo;
}

FrameInfo * Animator_previous(Animator *animator){
    if(animator->parser2){
        animator->ActiveIndex = animator->parser2->gldata.active - 1;
        if(animator->ActiveIndex < 0){
            animator->ActiveIndex = 0;
        }
        
        Parser_v2_next(animator->parser2, animator->ActiveIndex);
        animator->LastInfo->f_VAO = animator->parser2->gldata.vao;
        animator->LastInfo->part_count = Parser_v2_get_current_part_count(animator->parser2);
        animator->LastInfo->cell_count = animator->parser2->gldata.dPtr->cells;
        animator->LastInfo->c_VAO = animator->parser2->gldata.cvao;
        set_vao_binding_points(animator->LastInfo, animator->v2format);
        
    }else{
        animator->Current -= 1;
        animator->ActiveIndex -= 1;
        if(animator->Current < animator->Start){
            animator->Current = animator->Start;
            animator->ActiveIndex = 0;
        }
        
        if(animator->AllocationType == STATIC){
            animator->LastInfo = animator->Frames.at(animator->ActiveIndex);
        }else{
            std::string Target = animator->BasePath +
                MAKE_STR(animator->Current, int) + animator->Extension;
            
            if(animator->LastInfo){
                animator->LastInfo->clear();
            }
            
            animator->LastInfo = animator->Parser->readDetachedFile(Target.c_str());
        }
        
        animator->LastInfo->c_VAO = 0;
    }
    
    return animator->LastInfo;
}

void Animator_swap_boundary_state(Animator *animator){
    if(animator && animator->parser2){
        Parser_v2_set_buffers_for(animator->parser2,
                                  1 - animator->parser2->gldata.boundaryOnly);
    }
}

Parser_v2 * Animator_get_parser_v2(Animator *animator){
    return animator->parser2;
}

int Animator_get_current_frame_index(Animator *animator){
    return animator->ActiveIndex;
}

void Animator_pause(Animator *animator){
    animator->Paused = true;
}

void Animator_continue(Animator *animator){
    animator->Paused = false;
}

bool Animator_is_paused(Animator *animator){
    return animator->Paused;
}
