#include "blocks/skunk.h"
Block* player_allocate(void) {
    Skunk * self;
    land_alloc(self);
    return & self->super;
}
void player_init(Block * super) {
    Skunk * self = (void *) super;
    self->step = 0;
    self->direction = 0;
    self->want_direction = 0;
    self->rotstep = 0;
    self->pull = 0;
    self->reverse = 0;
    self->stack = land_array_new();
    self->pushing = 0;
    self->dead = 0;
    game->skunk = & self->super;
}
void player_destroy(Block * super) {
    game->skunk = NULL;
    Skunk * self = (void *) super;
    land_array_destroy(self->stack);
    block_destroy(super);
}
void player_try_pull(Skunk * self, float px, float pz) {
    float ox = self->super.x;
    float oz = self->super.z;
    self->super.x -= px * 3 * sqrt(2);
    self->super.z -= pz * 3 * sqrt(2);
    LandArray * pulls = block_colliders((void *) self);
    self->super.x = ox;
    self->super.z = oz;
    int tag = block_recursion_tag();
    {
        LandArrayIterator __iter0__ = LandArrayIterator_first(pulls);
        for (Block * p = LandArrayIterator_item(pulls, &__iter0__); LandArrayIterator_next(pulls, &__iter0__); p = LandArrayIterator_item(pulls, &__iter0__)) {
            if (! p->block_type->dynamic) {
                continue;
            }
            if (p->block_type->fixed) {
                continue;
            }
            p->recursion_prevention = tag;
            if (block_pull(p, px, 0, pz)) {
                self->super.dx = 0;
                self->super.dz = 0;
                self->pushing = 1;
            }
        }
    }
    land_array_destroy(pulls);
}
void player_grab(Skunk * self) {
    float ox = self->super.x;
    float oz = self->super.z;
    float px = sin((self->want_direction + 1) * pi / 4);
    float pz = cos((self->want_direction + 1) * pi / 4);
    self->super.x += px * 3 * sqrt(2);
    self->super.z += pz * 3 * sqrt(2);
    LandArray * grabs = block_colliders(& self->super);
    self->super.x = ox;
    self->super.z = oz;
    land_array_clear(self->stack);
    float max_y = - 9000;
    {
        LandArrayIterator __iter0__ = LandArrayIterator_first(grabs);
        for (Block * b = LandArrayIterator_item(grabs, &__iter0__); LandArrayIterator_next(grabs, &__iter0__); b = LandArrayIterator_item(grabs, &__iter0__)) {
            if (! b->block_type->lift) {
                continue;
            }
            if (b->y >= max_y) {
                max_y = b->y;
            }
        }
    }
    {
        LandArrayIterator __iter0__ = LandArrayIterator_first(grabs);
        for (Block * b = LandArrayIterator_item(grabs, &__iter0__); LandArrayIterator_next(grabs, &__iter0__); b = LandArrayIterator_item(grabs, &__iter0__)) {
            if (! b->block_type->lift) {
                continue;
            }
            if (b->y == max_y) {
                land_array_add(self->stack, b);
            }
        }
    }
    land_array_destroy(grabs);
}
void player_lift(Skunk * self) {
    block_recursion_tag();
    {
        LandArrayIterator __iter0__ = LandArrayIterator_first(self->stack);
        for (Block * p = LandArrayIterator_item(self->stack, &__iter0__); LandArrayIterator_next(self->stack, &__iter0__); p = LandArrayIterator_item(self->stack, &__iter0__)) {
            // don't want to be able to walk under it
            if (p->y + 1 < self->super.y + self->super.ys) {
                if (block_pull(p, 0, 1, 0)) {
                    p->no_fall = 1;
                    p->dy = 0;
                }
            }
        }
    }
}
void player_tick(Block * super) {
    Skunk * self = (void *) super;
    dialog_skunk(super);
    Application * a = app();
    float x = 0;
    float z = 0;
    float sx = 0;
    float sz = 0;
    int ns = 0;
    if (a->up) {
        x -= 1;
        z -= 1;
        ns++;
    }
    if (a->down) {
        x += 1;
        z += 1;
        ns++;
    }
    if (a->left) {
        x -= 1;
        z += 1;
        ns++;
    }
    if (a->right) {
        x += 1;
        z -= 1;
        ns++;
    }
    if (ns > 0) {
        sx -= a->up_s;
        sz -= a->up_s;
        sx += a->down_s;
        sz += a->down_s;
        sx -= a->left_s;
        sz += a->left_s;
        sx += a->right_s;
        sz -= a->right_s;
        self->accel++;
        if (self->accel > 120) {
            self->accel = 120;
        }
    }
    else {
        self->accel = 0;
    }
    if (sx > 1) {
        sx = 1;
    }
    if (sz > 1) {
        sz = 1;
    }
    if (sx < - 1) {
        sx = - 1;
    }
    if (sz < - 1) {
        sz = - 1;
    }
    x = sx;
    z = sz;
    //bool push_animation = False
    // slow down jump stronger if only tapping
    if (! a->jump && self->super.dy > 0) {
        self->super.dy *= 0.5;
    }
    float s = pow(sx * sx + sz * sz, 0.5);
    float d = pow(x * x + z * z, 0.5);
    if (d > 0 && ! land_array_count(self->stack)) {
        x /= d;
        z /= d;
        if (s > 0 && s < 1) {
            x *= s;
            z *= s;
        }
        double angle = atan2(x, z) - pi / 4;
        angle = angle / (2 * pi);
        angle = (int)((angle - floor(angle)) * 8 + 0.5) & 7;
        self->want_direction = angle;
        if ((super->ground || (a->godmode && self->super.dy < 5)) && a->jump) {
            if (self->pull) {
                if (! self->reverse) {
                    player_grab(self);
                    if (land_array_count(self->stack)) {
                        x = z = 0;
                        self->super.dx = 0;
                        self->super.dz = 0;
                        self->pull = 0;
                    }
                    else {
                        self->reverse = 1;
                    }
                }
                if (self->reverse) {
                    self->want_direction += 4;
                    self->want_direction &= 7;
                }
            }
            else if (a->jump) {
                self->super.dy += 30;
                sound(Data_uhg, 1);
            }
        }
        if (self->accel > 15) {
            float f = (self->accel - 15.0) / (120 - 15);
            x *= 1 + f / 2;
            z *= 1 + f / 2;
        }
        x *= 2.8;
        z *= 2.8;
        super->dx += x;
        super->dz += z;
        self->step += 1;
        self->step &= 31;
        if (self->step == 9 || self->step == 25) {
            if (self->super.ground) {
                if (self->metal) {
                    sound(Data_metal, 0.3);
                }
                else {
                    sound(Data_step, 0.3);
                }
            }
        }
    }
    else {
        if (self->step != 8 && self->step != 24) {
            self->step += 1;
            self->step &= 31;
        }
        else {
            self->lever = 0;
        }
        if (super->ground && a->jump) {
            self->pull = 1;
        }
    }
    if (self->pushing) {
        if (game->ticks % 8 == 1) {
            sound(Data_push, 0.3);
            self->pushing = 0;
        }
    }
    if (self->want_direction != self->direction) {
        self->rotstep += 1;
        if (self->rotstep >= 4) {
            self->rotstep = 0;
            d = self->want_direction - self->direction;
            if (d < - 4) {
                d += 8;
            }
            else if (d > 4) {
                d -= 8;
            }
            if (abs((int) d) == 4) {
                self->direction += (land_rand(0, 1)) * 2 - 1;
            }
            else if (d < 0) {
                self->direction -= 1;
            }
            else if (d > 0) {
                self->direction += 1;
            }
            self->direction &= 7;
        }
    }
    int f = (self->step / 4) % 8;
    if (f >= 5) {
        f = 8 - f;
    }
    int dir = (self->direction + 7) % 8;
    if (dir >= 3) {
        super->flipped = 0;
        dir -= 3;
    }
    else {
        super->flipped = 1;
        dir = 3 - dir;
    }
    super->frame = dir * 5 + f;
    float px = super->dx;
    float pz = super->dz;
    if (super->pushed_something) {
        super->pushed_something = 0;
        super->dx *= 0.7;
        super->dz *= 0.7;
    }
    //push_animation = True
    block_tick(super);
    if (self->pull) {
        if (! a->jump) {
            self->pull = 0;
            self->reverse = 0;
        }
        if (px || pz) {
            float px2 = px;
            float pz2 = pz;
            float fx = fabs(px2);
            float fz = fabs(pz2);
            // for gamepad, strongly prefer pulling in the cardinal directions
            if (fx < 0.5 * fz) {
                px2 = 0;
            }
            else if (fz < 0.5 * fx) {
                pz2 = 0;
            }
            player_try_pull(self, px2, pz2);
        }
    }
    //push_animation = True
    if (land_array_count(self->stack)) {
        if (! a->jump) {
            while (land_array_count(self->stack)) {
                Block * b = land_array_pop(self->stack);
                b->no_fall = 0;
            }
        }
        else {
            player_lift(self);
        }
    }
}
//push_animation = True
//if not super.ground and a.jump:
//    push_animation = True
//if push_animation:
//    super.frame += 64
bool player_touch(Block * super, Block * c, float dx, float dy, float dz) {
    if (c->block_type == Data_Herbalist) {
        c->talk = 1;
    }
    if (c->block_type == Data_Elephant) {
        if (game->dialog.elephant == 0) {
            c->talk = 1;
        }
        else if (! c->talk) {
            if (game->level == 25) {
                c->interject = "look at\nthose iridescent bubbles!";
            }
            else {
                str words [] = {"hey", "ouch", "back off", "what are you doing?", "i am the elephant"};
                c->interject = words [land_rand(0, 4)];
            }
            c->talk = 1;
        }
    }
    if (c->block_type == Data_Pyramidtree) {
        game->gnome = c;
        game->dialog.gnome = 0;
        c->talk = 1;
    }
    if (c->block_type == Data_PyramidtreeCandy) {
        game->gnome = c;
        game->dialog.gnome = 10;
        c->talk = 1;
    }
    if (c->block_type == Data_Chameleon) {
        c->talk = 1;
    }
    if (dy < 0) {
        int lx = 0, lz = 0;
        if (c->block_type == Data_BlockExit || c->block_type == Data_BlockExitR) {
            if (block_center_overlaps(super, c)) {
                lx = c->x > 0 ? 1 : - 1;
            }
        }
        else if (c->block_type == Data_BlockExitU || c->block_type == Data_BlockExitD) {
            if (block_center_overlaps(super, c)) {
                lz = c->z > 0 ? 1 : - 1;
            }
        }
        if (lx || lz) {
            bool yes = 1;
            if (game->elephant && game->dialog.elephant >= 2) {
                if (block_distance(super, game->elephant) > 150) {
                    yes = 0;
                    if (! game->elephant->interject) {
                        game->target_x = c->x;
                        game->target_z = c->z;
                        game->elephant->talk = 1;
                        game->elephant->interject = "Coming";
                        game->elephant->run = 1;
                    }
                }
            }
            if (yes) {
                game_level_done(game, lx, lz);
            }
        }
    }
    return c->block_type != Data_IsoBoulder;
}
bool have_friend(void) {
    return game->dialog.elephant >= 2;
}
void avoid_collision(Block * b) {
    LandArray * a = block_colliders(b);
    print("collides with: %d", land_array_count(a));
    float maxy = 0;
    {
        LandArrayIterator __iter0__ = LandArrayIterator_first(a);
        for (Block * c = LandArrayIterator_item(a, &__iter0__); LandArrayIterator_next(a, &__iter0__); c = LandArrayIterator_item(a, &__iter0__)) {
            if (c->y + c->ys > maxy) {
                maxy = c->y + c->ys;
            }
        }
    }
    if (b->y < maxy) {
        b->y = maxy;
        print("changed to %.1f", b->y);
    }
    land_array_destroy(a);
}
void player_find_entrance(void) {
    print("find entrance");
    float x = 0;
    float y = 0;
    float z = 0;
    if (game->gox) {
        x = 24 + game->gox * (- 18 * 24);
        z = - 24;
    }
    if (game->goz) {
        z = 24 + game->goz * (- 18 * 24);
        x = - 24;
    }
    if (have_friend() && ! game->elephant) {
        game->elephant = block_new(game->blocks, x, y, z, Data_Elephant);
        print("create elephant %.1f", y);
        block_add(game->elephant);
    }
    if (have_friend() && game->elephant) {
        print("moving elephant to %.1f", y);
        game->elephant->x = x;
        game->elephant->y = y;
        game->elephant->z = z;
        avoid_collision(game->elephant);
    }
    if (! game->skunk) {
        game->skunk = block_new(game->blocks, x, y, z, Data_Skunk);
        block_add(game->skunk);
    }
    game->skunk->x = x;
    game->skunk->y = y;
    game->skunk->z = z;
    avoid_collision(game->skunk);
}
