#include <math.h>
#include <iostream>
#include <fstream>
#include <string>

using namespace std;

#include "SDL/SDL.h"

#include "../lib/gfxMR.hpp"
#include "../lib/sdlMR.hpp"

string get_key(string table,int n)
{
    size_t begin = table.find('{');
        size_t start = table.find('{',begin)+1;
        size_t end = table.find(':');
        size_t len = table.find('}');
        for (n;n>0;n--)
        {
            start = table.find(';',end)+1;
            end = table.find(':',start);
            if (end >= len)
                return "end";
        }
        return table.substr(start,end-start);
}

string get_value(string table,int n)
{
    size_t begin = table.find('{');
        size_t start = table.find(':',begin)+1;
        size_t end = table.find(';');
        size_t len = table.find('}');
        for (n;n>0;n--)
        {
            start = table.find(':',end)+1;
            end = table.find(';',start);
            if (start >= len)
                return "end";
        }
        return table.substr(start,end-start);
}

int get_count(string table)
{
    size_t count = 0;
    for (int i = 0; i<table.length(); i++)
    {
        if (table[i] == ';')
            count++;
    }
    return count;
}

string Lsys(string source, size_t start)
{
    size_t tmp = source.find('}')+1;
    if (start < tmp)
        start = tmp;
    int level = 0; //stack level

    size_t trav_start = start,
            trav_end = start;
    int nbRule = get_count(source);
    while(start < source.length() && level >=0)
    {
        if (source[start] == '[')
        {
            level++;
        }
        else if (source[start] == ']')
        {
            level--;
        }
        if(level<0)
        {
            trav_end = start;
            start = source.length();
        }
        else
        {
            for (int i=0;i<nbRule;i++)
            {
                string key = get_key(source,i);
                int len = key.length();
                if (source.compare(start, len, key) == 0)
                {
                    string value = get_value(source,i);
                    source.replace(start, len, value);
                    i = nbRule;
                    start += value.length()-1;
                }
            }
            start ++;
        }
    }
    return source;
}

class Turtle
{
    public:
        Turtle * _back;
        int x,
            y;
        Uint8 color;
        float radius,
              radian,
              rotate,
              increase;
        Turtle()
        {
            _back = 0;
            x = 0;
            y = 0;
            color = 0;
            radius = 0;
            radian = 0;
            rotate = 0;
            increase = 0;
        }
        Turtle(Turtle * back)
        {
            _back = back;
            x = back->x;
            y = back->y;
            color = back->color;
            radius = back->radius;
            radian = back->radian;
            rotate = back->rotate;
            increase = back->increase;
        }
        Turtle * back()
        {
            return _back;
        }
        void operator () ()
        {
            x = x + radius * sin(radian);
            y = y + radius * cos(radian);
        }
};

/*
synthax

a for a straight forward line
- for turning left
+ for turning right
[ push
] pop

*/

int main(int argc, char * argv[])
{
    //chessy loading file
    if (argc != 2)
    {
        cout << "invalid argument\n";
        return 0;
    }
    ifstream test_file(argv[1]);
    string line,buff;
    do
    {
        getline(test_file,buff);
        line.append(buff);
    }while(test_file);
    cout << line << endl;

    SDL_Surface *screen;
    SDL_Event event;

    int keypress = 0;
    int mx = 0,
        my = 0,
        mi = 0;

    SDL_Init(SDL_INIT_EVERYTHING);
    screen = SDL_SetVideoMode(512, 512, 8, SDL_SWSURFACE);

    sdlPutPixel plot(screen);
    sdlGetPixel get(screen);
    ploter * pp = &plot;
    bresenhamCircle cercle(plot);
    bresenhamLine ligne(plot);
    floodFill paint(plot,get);

    while(!keypress)
    {
        SDL_Flip(screen);
        SDL_FillRect(screen,0,0);

        while(SDL_PollEvent(&event))
        {
            switch (event.type)
            {
                case SDL_QUIT:
                    keypress = 1;
                    break;
                case SDL_KEYDOWN:
                    keypress = 1;
                    break;
                case SDL_MOUSEMOTION:
                    mx = event.motion.x;
                    my = event.motion.y;
                    break;
                case SDL_MOUSEBUTTONDOWN:
                    if (mi)
                    {
                        line = Lsys(line,mi);
                        //cout << line << endl;
                    }
                    break;
            }
        }

        cercle(mx,my,3,5);

        //now use this in an l-tree object.
        Turtle * T = new Turtle(),
                    * TT = 0;
        T->x = 255;
        T->y = 511;
        T->color = 255;
        T->radius = 10;
        T->radian = 3.1415;
        T->rotate = 0.49;
        T->increase = 5;
        Uint8 c = 0;

        mi = 0;

        int level = 0;
        for (int i=0; i<line.length();i++)
        {
            switch(line[i])
            {
                case '{':
                    do{
                        i++;
                    }while(line[i]!='}');
                    break;
                case 'a':
                    TT = new Turtle(T);
                    (*T)();
                        if (mi == 0 &&
                            mx+5 > T->x &&
                            mx-5 < T->x &&
                            my+5 > T->y &&
                            my-5 < T->y)
                        {
                            c = 48;
                            mi = i;
                        }
                        else
                            c = T->color;
                    ligne(TT->x, TT->y, T->x, T->y, c);
                    delete TT;
                    break;
                case '+':
                    T->radian += T->rotate;
                    break;
                case '-':
                    T->radian -= T->rotate;
                    break;
                case '[':
                    T = new Turtle(T);
                    level++;
                    break;
                case ']':
                    TT = T->back();
                    delete T;
                    T = TT;
                    level--;
                    break;
                case 'o':
                    T->radius /= 2;
                    (*T)();
                    cercle(T->x, T->y, T->color, T->radius);
                    paint(T->x, T->y, T->color & 85,0);
                    T->radius *= 2;
                    (*T)();
                    break;
                case '/':
                    T->rotate -= 0.33;
                    break;
                case '\\':
                    T->rotate += 0.33;
                    break;
                case '|':
                    T->radius += T->increase;
                    break;
                case '_':
                    T->radius -= T->increase;
                    break;
            }
        }
        delete T;
    }

    SDL_Quit();

    test_file.close();
    return 0;
}
