#include "toolkit.h"

#define TITLE "Spring Toys 1"

// How big a tick difference is considered 'time warp', i.e. skip the time
// (to avoid physics blowing up)
#define TICK_TIMEWARP 1000
// Physics ticks per second
#define PHYSICS_FPS 100
// For windowed mode
#define DESIRED_WINDOW_WIDTH 800
// For windowed mode
#define DESIRED_WINDOW_HEIGHT 600
// Desired aspect ratio. 640x480 = 4/3. Comment out to use whatever the desktop is set.
#define DESIRED_ASPECT ((float)(DESIRED_WINDOW_WIDTH)/(float)(DESIRED_WINDOW_HEIGHT))

//#define FULLSCREEN_BY_DEFAULT

//#define RESIZABLE_WINDOW

int gKeyState = 0;
int gLastTick = 0;
int gScreenWidth = 0;
int gScreenHeight = 0;

ACFont fn, fn14;
void initvideo(int argc);

struct masspoint
{
    float x, y;
    float xi, yi;
};

struct spring
{
    masspoint *p1, *p2;
    float desired_d;
    float fx, fy;
    float k;
};

masspoint mp[1000];
int max_mp = 0;

spring sp[1000];
int max_spring = 0;

float mp_distance(masspoint &mp1, masspoint &mp2)
{
    float xd = mp1.x-mp2.x;
    float yd = mp1.y-mp2.y;
    return sqrt((xd*xd)+(yd*yd));
}

void define_spring(masspoint &mp1, masspoint &mp2, float k)
{
    sp[max_spring].p1 = &mp1;
    sp[max_spring].p2 = &mp2;
    sp[max_spring].desired_d = mp_distance(mp1, mp2);
    sp[max_spring].fx = sp[max_spring].fy = 0;
    sp[max_spring].k = k;
    max_spring++;
}

masspoint & define_masspoint(float x, float y)
{
    mp[max_mp].x = x;
    mp[max_mp].y = y;
    mp[max_mp].xi = mp[max_mp].yi = 0;
    max_mp++;
    return mp[max_mp-1];
}

void handle_key(int keysym, int down)
{
    switch(keysym)
    {
    case SDLK_ESCAPE:
        if (!down)
        {
            exit(0);
        }
        break;
    }
}

void process_events()
{
    SDL_Event event;

    while (SDL_PollEvent(&event)) 
    {
        switch (event.type) 
        {
        case SDL_KEYDOWN:
            handle_key(event.key.keysym.sym, 1);
			// If a key is pressed, report it to the widgets
			gUIState.keyentered = event.key.keysym.sym;
			gUIState.keymod = event.key.keysym.mod;
			// if key is ASCII, accept it as character input
			if ((event.key.keysym.unicode & 0xFF80) == 0)
				gUIState.keychar = event.key.keysym.unicode & 0x7f;				
            break;
        case SDL_KEYUP:
            handle_key(event.key.keysym.sym, 0);
            break;
        case SDL_MOUSEMOTION:
			// update mouse position
			gUIState.mousex = (event.motion.x * (float)DESIRED_WINDOW_WIDTH) / (float)gScreenWidth;
			gUIState.mousey = (event.motion.y * (float)DESIRED_WINDOW_HEIGHT) / (float)gScreenHeight;
            break;
		case SDL_MOUSEBUTTONDOWN:
			// update button down state if left-clicking
			if (event.button.button == 1)
            {
				gUIState.mousedown = 1;
			    gUIState.mousedownx = (event.motion.x * (float)DESIRED_WINDOW_WIDTH) / (float)gScreenWidth;
			    gUIState.mousedowny = (event.motion.y * (float)DESIRED_WINDOW_HEIGHT) / (float)gScreenHeight;
            }
            if (event.button.button == 4)
            {
                gUIState.scroll = +1;
            }
            if (event.button.button == 5)
            {
                gUIState.scroll = -1;
            }
			break;
		case SDL_MOUSEBUTTONUP:
			// update button down state if left-clicking
			if (event.button.button == 1)
				gUIState.mousedown = 0;
			break;
        case SDL_QUIT:
            SDL_Quit();
            exit(0);
            break;
        case SDL_VIDEORESIZE:
            gScreenWidth = event.resize.w;
            gScreenHeight = event.resize.h;
            initvideo(0);
            break;

        }
    }
}



void physics_tick(int tick)
{
    int i;

    for (i = 0; i < max_spring; i++)
    {        
        float dx = sp[i].p1->x - sp[i].p2->x;
        float dy = sp[i].p1->y - sp[i].p2->y;
        float d = sqrt(dx*dx+dy*dy);
        if (d > 0.000001f)
        {
            // F = -ky

            // how much of the force goes to horizontal and vertical vectors..
            float fx = dx / d;
            float fy = dy / d;

            // helper variable, basically this is the force
            float intensity = (d - sp[i].desired_d) * sp[i].k;
            
            // apply
            sp[i].fx = fx * intensity;
            sp[i].fy = fy * intensity;
            sp[i].p1->xi -= sp[i].fx;
            sp[i].p1->yi -= sp[i].fy;
            sp[i].p2->xi += sp[i].fx;
            sp[i].p2->yi += sp[i].fy;
            
        }
    }

    for (i = 0; i < max_mp; i++)
    {
        mp[i].yi += 0.2;
        mp[i].x += mp[i].xi;
        mp[i].y += mp[i].yi;
        if (mp[i].y > 600) mp[i].y = 600;
        if (mp[i].x < 0) mp[i].x = 0;
        if (mp[i].x > 800) mp[i].x = 800;
        mp[i].xi *= 0.95;
        mp[i].yi *= 0.95;
    }


}



void drawline(float x0, float y0, float x1, float y1, int color)
{
    glColor4f(((color >> 16) & 0xff) / 256.0f,
              ((color >> 8) & 0xff) / 256.0f,
              ((color >> 0) & 0xff) / 256.0f,
              ((color >> 24) & 0xff) / 256.0f);
    glBegin(GL_LINES);
      glVertex2f(x0,y0);
      glVertex2f(x1,y1);
    glEnd();
}


static void draw_screen_ingame()
{
    int tick = SDL_GetTicks();

    int i;    

    if (tick - gLastTick > TICK_TIMEWARP) 
        gLastTick = tick;

    if (gLastTick >= tick)
    {
        SDL_Delay(1);
        return;
    }

    while (gLastTick < tick)
    {

        if (gUIState.mousedown)
        {
            mp[0].x = gUIState.mousex;
            mp[0].y = gUIState.mousey;
        }

        physics_tick(gLastTick);

        gLastTick += 1000 / PHYSICS_FPS;
    }

    ////////////////////////////////////
    // Rendering
    ////////////////////////////////////

    glClearColor(0.1,0.1,0.2,1.0);
    glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

    fn14.drawstring("Hello world",10,10);

    for (i = 0; i < max_mp; i++)
        drawrect(mp[i].x-2, mp[i].y-2, 5, 5, 0xffffffff);

    for (i = 0; i < max_spring; i++)
        drawline(sp[i].p1->x,sp[i].p1->y,sp[i].p2->x,sp[i].p2->y,0xff00ff00);

    

    SDL_Delay(10);
	SDL_GL_SwapBuffers();
}



void draw_screen()
{
    draw_screen_ingame();
}


void init()
{
    // corners
    masspoint &p1 = define_masspoint(100, 100); // top left
    masspoint &p2 = define_masspoint(100, 200); // top right
    masspoint &p3 = define_masspoint(200, 100); // bottom left
    masspoint &p4 = define_masspoint(200, 200); // bottom right

    // midpoints
    masspoint &p5 = define_masspoint(100, 150); // middle top
    masspoint &p6 = define_masspoint(150, 100); // middle left
    masspoint &p7 = define_masspoint(200, 150); // middle bottom
    masspoint &p8 = define_masspoint(150, 200); // middle right


    // around    
    define_spring(p1,p2,0.05);
    define_spring(p2,p4,0.05);
    define_spring(p3,p4,0.05);
    define_spring(p3,p1,0.05);

    // cross
    define_spring(p1,p4,0.05);
    define_spring(p2,p3,0.05);

    // midpoints to corners
    define_spring(p1,p5,0.05);
    define_spring(p1,p6,0.05);
    define_spring(p2,p5,0.05);
    define_spring(p2,p8,0.05);
    define_spring(p3,p7,0.05);
    define_spring(p3,p6,0.05);
    define_spring(p4,p7,0.05);
    define_spring(p4,p8,0.05);

    // midpoints across
    define_spring(p5,p7,0.05);
    define_spring(p6,p8,0.05);

    // midpoints diamond
    define_spring(p5,p6,0.05);
    define_spring(p6,p7,0.05);
    define_spring(p7,p8,0.05);
    define_spring(p8,p5,0.05);

    // corners to midpoints
    define_spring(p1,p7,0.05);
    define_spring(p1,p8,0.05);
    define_spring(p2,p6,0.05);
    define_spring(p2,p7,0.05);
    define_spring(p3,p5,0.05);
    define_spring(p3,p8,0.05);
    define_spring(p4,p5,0.05);
    define_spring(p4,p6,0.05);

}

void initvideo(int argc)
{
    const SDL_VideoInfo *info = NULL;
    int bpp = 0;
    int flags = 0;

    info = SDL_GetVideoInfo();

    if (!info) 
    {
        fprintf(stderr, "Video query failed: %s\n", SDL_GetError());
        SDL_Quit();
        exit(0);
    }

#ifdef _DEBUG
    int fsflag = 0;
#else
#ifdef FULLSCREEN_BY_DEFAULT
    int fsflag = 1;
#else
    int fsflag = 0;
#endif
#endif

    if (argc > 1) fsflag = !fsflag;

    if (fsflag) 
    {
        gScreenWidth = info->current_w;
        gScreenHeight = info->current_h;
        bpp = info->vfmt->BitsPerPixel;
        flags = SDL_OPENGL | SDL_FULLSCREEN;
    }
    else
    {
        if (argc == 0)
        {
            // window was resized
        }
        else
        {
            gScreenWidth = DESIRED_WINDOW_WIDTH;
            gScreenHeight = DESIRED_WINDOW_HEIGHT;
        }
        bpp = info->vfmt->BitsPerPixel;
        flags = SDL_OPENGL;
#ifdef RESIZABLE_WINDOW
        flags |= SDL_RESIZABLE;
#endif
    }

    SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5);
    SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 5);
    SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5);
    SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16);
    SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);

    if (SDL_SetVideoMode(gScreenWidth, gScreenHeight, bpp, flags) == 0) 
    {
        fprintf( stderr, "Video mode set failed: %s\n", SDL_GetError());
        SDL_Quit();
        exit(0);
    }
   
#ifdef DESIRED_ASPECT
    float aspect = DESIRED_ASPECT;
    if (((float)gScreenWidth / gScreenHeight) > aspect)
    {
        float realx = gScreenHeight * aspect;
        float extrax = gScreenWidth - realx;

        glViewport( extrax / 2, 0, realx, gScreenHeight );
    }
    else
    {
        float realy = gScreenWidth / aspect;
        float extray = gScreenHeight - realy;

        glViewport( 0, extray / 2, gScreenWidth, realy );
    }
#else
    glViewport( 0, 0, gScreenWidth, gScreenHeight );
#endif

    glMatrixMode( GL_PROJECTION );
    glLoadIdentity( );
#ifdef DESIRED_ASPECT
	gluOrtho2D(0,DESIRED_WINDOW_WIDTH,DESIRED_WINDOW_HEIGHT,0);
#else
    gluOrtho2D(0,gScreenWidth,gScreenHeight,0);
#endif

    reload_textures();    

    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA,GL_ONE);
}


int main (int argc, char** args)
{
    gVisualRand.init_genrand(0xc0cac01a);

    if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE) < 0) 
    {
        fprintf(stderr, "Video initialization failed: %s\n", SDL_GetError());
        SDL_Quit();
        exit(0);
    }

    initvideo(argc);

    init();

	// For imgui - Enable keyboard repeat to make sliders more tolerable
	SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
	// For imgui - Enable keyboard UNICODE processing for the text field.
	SDL_EnableUNICODE(1);

    fn14.load("vera14.fnt");
    
    // set window title
    SDL_WM_SetCaption(TITLE " - http://iki.fi/sol/", NULL);

    // hide cursor
    //SDL_ShowCursor(0);
    
    while (1) 
    {
        process_events();
        draw_screen();
    }

    return 0;
}

