#include "LatticeWorld.h"
#include <stdlib.h>
#include <time.h>
#include <string.h>

uint8_t RuleTable[16];

void UpdateWorld(LatticeWorld *world) {
    uint8_t *site = world->sites[world->currentSite];
    uint8_t *newSite = world->sites[(world->currentSite + 1) % 2];
    unsigned int i, x, y;
    // first apply all the collisions
    for(i = 0; i < world->width * world->height; ++i) {
        site[i] = RuleTable[site[i]];
    }
    // now transport all the particles
    for(x = 1; x < world->width - 1; ++x) {
        for(y = 1; y < world->height - 1; ++y) {
            i = x + y * world->width;
            newSite[i] = (site[i+1] & 0x01) |
                (site[i + world->width] & 0x02) |
                (site[i - 1] & 0x04) |
                (site[i - world->width] & 0x08);
        }
    }
    // handle transport for top and bottom edges
    for(x = 1; x < world->width - 1; ++x) {
        //top
        i = x;
        newSite[i] = (site[i + 1] & 0x01) |
            (site[i +  world->width] & 0x02) |
            (site[i - 1] & 0x04) |
            (site[i + (world->height - 1) * world->width] & 0x08);

        //bottom
        i = x + (world->height - 1) * world->width;
        newSite[i] =
            (site[i + 1] & 0x01) |
            (site[x] & 0x02) |
            (site[i - 1] & 0x04) |
            (site[i - world->width] & 0x08);
    }
    // handle transport for side edges
    for(y = 1; y < world->height - 1; ++y) {
        //left
        i = y * world->width;
        newSite[i] = (site[i+1] & 0x01) |
            (site[i + world->width] & 0x02) |
            (site[i + world->width - 1] & 0x04) |
            (site[i - world->width] & 0x08);

        //right
        i = world->width - 1 + y * world->width;
        newSite[i] = (site[i - (world->width - 1)] & 0x01) |
            (site[i + world->width] & 0x02) |
            (site[i - 1] & 0x04) |
            (site[i - world->width] & 0x08);
    }
    // handle transport for corners
    // top-left
    i = 0;
    newSite[i] = (site[i + 1] & 0x01) |
        (site[i + world->width] & 0x02) |
        (site[world->width - 1] & 0x04) |
        (site[(world->height - 1) * world->width] & 0x08);
    // top-right
    i = world->width - 1;
    newSite[i] = (site[0] & 0x01) |
        (site[i + world->width] & 0x02) |
        (site[i - 1] & 0x04) |
        (site[world->width * world->height - 1] & 0x08);
    // bottom-left
    i = world->width * (world->height - 1);
    newSite[i] = (site[i+1] & 0x01) |
        (site[0] & 0x02) |
        (site[world->width * world->height] & 0x04) |
        (site[i - world->width] & 0x08);
    // bottom-right
    i = world->width * world->height - 1;
    newSite[i] = (site[(world->height - 1) * world->width] & 0x01) |
        (site[world->width - 1] & 0x02) |
        (site[i - 1] & 0x04) |
        (site[i - world->width] & 0x08);

    world->currentSite = (world->currentSite + 1) % 2;
}

/* The rightmost 4 bits are the incoming particles for the site, starting with
 * the right and going clockwise. Note that after the collision step the
 * particles are actually reversed, so that in the transport step they can just
 * be copied.
 */
void InitRules(void){
    unsigned int i;
    for(i = 0; i < 16; ++i) {
        RuleTable[i] = i;
    }
    // 2 particles colliding will scatter
    RuleTable[0x05] = 0x0a;
    RuleTable[0x0a] = 0x05;
}
