/*
 * LILACA (LILACA is Langton's Ant Cellular Automaton)
 * by Adam "akaz" Kazimierczak
 *
 * version: 0.9 beta
 * date: January 2010
 *
 * visit: http://akaz.com.pl/
 * or
 * mail me by: adam.kazimierczak@gmail.com
 *
 * License: GNU GPLv3
 */

/*
 *   This file is part of LILACA.
 *
 *   LILACA is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   LILACA is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with LILACA.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "langton.h"
#include <allegro.h>

using namespace Langton;
using namespace std;

//-------------------

void Field::reset() {
    for(unsigned int i=1; i<=800; i++) {
        for(unsigned int z=1; z<=800; z++) {
            this->set_fill(false, i, z);
        }
    }
    this->filled_cells=0;
}

void Field::draw_dots(BITMAP *buffer) {
    for(unsigned int i=1; i<800; i++) {
        for(unsigned int z=1; z<800; z++) {
            if(this->cell[i][z]==1)
                _putpixel32(buffer,i,z,makecol(0,0,0));
            else
                _putpixel32(buffer,i,z,makecol(255,255,255));
        }
    }
}

void Field::create_random_dots() {
    for(unsigned int i=390; i<=410; i++) {
        for(unsigned int z=390; z<=410; z++) {
            if((rand()%20)==1)
                this->set_fill(true,i,z);
        }
    }
}

void Field::update(BITMAP *buffer, unsigned int &pos_x, unsigned int &pos_y) {
    if(this->cell[pos_x][pos_y]) {
        this->set_fill(false,pos_x,pos_y);
    }
    else {
        this->set_fill(true,pos_x,pos_y);
    }
    this->draw_dots(buffer);
}

void Field::set_fill(bool fill, unsigned &pos_x, unsigned &pos_y) {
    if(fill==true) {
        this->cell[pos_x][pos_y]=1;
        this->filled_cells++;
    }
    else {
        this->cell[pos_x][pos_y]=0;
        if(this->filled_cells!=0)
            this->filled_cells--;
    }
}

//------------------------

void Ant::set_position(unsigned int x, unsigned int y) {
    this->pos_x=x;
    this->pos_y=y;
}

void Ant::draw(BITMAP *buffer) {
    _putpixel32(buffer,pos_x,pos_y,makecol(255,0,0));
}

void Ant::set_direction(char dir) {
    this->direction=dir;
}

void Ant::move() {
    switch(this->direction) {
        case 'N':
            this->set_position(this->pos_x,this->pos_y-1);
            break;
        case 'E':
            this->set_position(this->pos_x+1,this->pos_y);
            break;
        case 'S':
            this->set_position(this->pos_x,this->pos_y+1);
            break;
        case 'W':
            this->set_position(this->pos_x-1,this->pos_y);
            break;
    }
}

void Ant::turn_left() {
    switch(this->direction) {
        case 'N':
            this->set_direction('W');
            break;
        case 'E':
            this->set_direction('N');
            break;
        case 'S':
            this->set_direction('E');
            break;
        case 'W':
            this->set_direction('S');
            break;
    }
}

void Ant::turn_right() {
    switch(this->direction) {
        case 'N':
            this->set_direction('E');
            break;
        case 'E':
            this->set_direction('S');
            break;
        case 'S':
            this->set_direction('W');
            break;
        case 'W':
            this->set_direction('N');
            break;
    }
}

void Ant::check_field(bool cell[800][800]) {
    if(cell[this->pos_x][this->pos_y]) {
        this->turn_right();
    }
    else {
        this->turn_left();
    }
}

void Ant::update(BITMAP* buffer, bool cell[800][800]) {
    this->check_field(cell); // Check cell state and turn ant
    this->move(); // Move ant position
    
    this->draw(buffer);
}

//---------------------------

void Info::draw_info(BITMAP* buffer, Field &field, Ant &ant) {
    textprintf_ex(buffer, font, 10, screen->h-20, makecol(255,0,0),-1, "To leave press [ESC]");

    textprintf_ex(buffer, font, 10, 10, makecol(0,0,255),-1, "Langton's Ant cellular automaton");

    textprintf_ex(buffer, font, 10, 30, makecol(0,0,0),-1, "Adam \"akaz\" Kazimierczak");
    textprintf_ex(buffer, font, 10, 40, makecol(0,0,0),-1, "http://www.akaz.com.pl/");
    textprintf_ex(buffer, font, 10, 50, makecol(0,0,0),-1, "version: %s", VERSION);

    //--------
    
    textprintf_ex(buffer, font, 10, 70, makecol(255,0,0),-1, "Current step: %i", this->step);
    textprintf_ex(buffer, font, 10, 80, makecol(255,0,0),-1, "Filled cells: %i", field.filled_cells);

    //--------

    textprintf_ex(buffer, font, 10, 100, makecol(255,0,255),-1, "Ant 1:");
    textprintf_ex(buffer, font, 10, 110, makecol(255,0,255),-1, "Position x = %i",ant.pos_x);
    textprintf_ex(buffer, font, 10, 120, makecol(255,0,255),-1, "Position y = %i",ant.pos_y);
    textprintf_ex(buffer, font, 10, 130, makecol(255,0,255),-1, "Direction  = %c",ant.direction);
}

void Info::update(BITMAP* buffer, Field &field, Ant &ant) {
    this->step++;
    this->draw_info(buffer, field, ant);
}

//---------------------------

void System::init() {
    allegro_init();
    install_keyboard();

    set_color_depth(32); // If you want to change color depth, change also functions _putpixel32()
    set_gfx_mode(GFX_AUTODETECT_WINDOWED,800,800,0,0);
    set_window_title("Langton's Ant cellular automaton (LILACA) by akaz");

    this->buffer = create_bitmap(800,800);

    this->field.reset(); // Make whole matrix unfilled (0)
    //this->field.create_random_dots(); // For testing purposes

    this->ant.set_position(400,400); // Center of view
    this->ant.set_direction('W'); // Becase first field is white, so turn left
}

void System::deinit() {
    destroy_bitmap(this->buffer);
    allegro_exit();
}

void System::start() {
    this->init();

    while(!key[KEY_ESC]) { // Main loop
        this->field.update(this->buffer, this->ant.pos_x, this->ant.pos_y);
        this->ant.update(this->buffer, this->field.cell);
        this->info.update(this->buffer, this->field, this->ant);

        blit(this->buffer, screen, 0, 0, 0, 0, 800, 800); // Update screen
    }

    this->deinit();
}
