#include "render.h"
static Render r;
static void draw_move_controls(void);
static void draw_jump_controls(void);
static void draw_pause_controls(void);
Render* render_global(void) {
    return & r;
}
void render_setup(void) {
    if (r.was_setup) {
        return ;
    }
    r.was_setup = 1;
    land_render_state(LAND_ALPHA_TEST, 1);
    land_render_state(LAND_ALPHA_FUNCTION, LAND_GREATER);
    land_render_state(LAND_ALPHA_VALUE, 0.0);
    render_set_shaders();
    r.background_color = (LandColor) {.8, .9, 1, 1};
}
void play_song(void) {
    if (! r.music) {
        return ;
    }
    Application * a = app();
    LandBuffer * b = land_buffer_new();
    land_buffer_cat(b, "audio/");
    if (r.song == 0) {
        land_buffer_cat(b, "the_field_of_dreams.ogg");
    }
    else if (r.song == 1) {
        land_buffer_cat(b, "shades_of_a_forest.ogg");
    }
    else if (r.song == 2) {
        land_buffer_cat(b, "Red Curtain.ogg");
    }
    else if (r.song == 3) {
        land_buffer_cat(b, "Otto Halmén - Sylvan Waltz.ogg");
    }
    else if (r.song == 4) {
        land_buffer_cat(b, "one_0.ogg");
    }
    else if (r.song == 5) {
        land_buffer_cat(b, "GameMusic_ForestTheme_24.ogg");
    }
    char * musicpath = land_buffer_finish(b);
    print("now playing %s", musicpath);
    land_stream_music(r.music, musicpath);
    land_free(musicpath);
    land_stream_volume(r.music, a->music / 6.0);
    r.song++;
    if (r.song == 6) {
        r.song = 0;
    }
}
void song_volume(void) {
    Application * a = app();
    land_stream_volume(r.music, a->music / 6.0);
}
static void draw_move_controls(void) {
    Application * a = app();
    float w = land_display_width();
    float h = land_display_height();
    if (a->swipej && a->dpad > 3) {
        land_color(0.8, 0.1, 0, 0.5);
    }
    else {
        land_color(0.5, 0.4, 0, 0.5);
    }
    float rr = w / 8 * 0.8;
    if (a->dpad == 2 || a->dpad == 3) {
        rr *= 1.5;
    }
    float rx = rr;
    float ry = h - rr;
    if (a->dpad > 3) {
        if (! a->swipe) {
            return ;
        }
        rx = a->swipex;
        ry = a->swipey;
    }
    else if (a->dpad == 1 || a->dpad == 3) {
        rx = w - rr;
    }
    float xy [2 * 34];
    xy [0] = rx;
    xy [1] = ry;
    for (int i = 1; i < 34; i += 1) {
        double c = cos((i - 1.5) * pi / 16);
        double s = sin((i - 1.5) * pi / 16);
        float r2 = rr;
        if (i % 4 == 0 || i % 4 == 3) {
            r2 *= 0.75;
        }
        xy [i * 2 + 0] = rx + c * r2;
        xy [i * 2 + 1] = ry + s * r2;
    }
    land_filled_polygon(34, xy);
}
static void draw_jump_controls(void) {
    Application * a = app();
    if (a->dpad > 3) {
        return ;
    }
    land_color(0.5, 0.4, 0, 0.5);
    float w = land_display_width();
    float h = land_display_height();
    float rr = w / 8 * 0.8;
    if (a->dpad == 2 || a->dpad == 3) {
        rr *= 1.5;
    }
    float rx = w - rr;
    float ry = h - rr;
    if (a->dpad == 1 || a->dpad == 3) {
        rx = rr;
    }
    rr *= 0.6;
    land_filled_circle(rx - rr, ry - rr, rx + rr, ry + rr);
}
static void draw_pause_controls(void) {
    Application * a = app();
    land_color(0.5, 0.4, 0, 0.5);
    LandFloat x, y, w, h;
    if (a->show_map) {
        button_coordinates(0, & x, & y, & w, & h);
        land_filled_triangle(x + w * 7 / 8, y + h / 2, x, y + h, x, y);
        button_coordinates(1, & x, & y, & w, & h);
        land_filled_triangle(x, y + h / 2, x + w * 7 / 8, y, x + w * 7 / 8, y + h);
    }
    else {
        button_coordinates(0, & x, & y, & w, & h);
        land_filled_rectangle(x, y + h / 8, x + w * 3 / 8, y + h * 7 / 8);
        land_filled_rectangle(x + w * 4 / 8, y + h / 8, x + w * 7 / 8, y + h * 7 / 8);
    }
}
void render(Game * g, float w, float h) {
    Application * a = app();
    float fh = land_font_height(a->medium);
    //float z = g.viewport->zoom
    land_clear_depth(1);
    a->tint.a = 0;
    land_clear(r.background_color.r, r.background_color.g, r.background_color.b, r.background_color.a);
    if (a->overview) {
        overview_render(game->overview);
    }
    else if (! a->show_map) {
        render_blocks(g->blocks, g->viewport);
        land_render_state(LAND_DEPTH_TEST, 0);
        render_block_speech_bubbles(g->blocks, g->viewport);
    }
    land_reset_transform();
    land_render_state(LAND_DEPTH_TEST, 0);
    if (! a->render_screenshot) {
        if (! a->show_map && global_use_touch_input) {
            draw_move_controls();
            draw_jump_controls();
        }
        if (1) {
            draw_pause_controls();
        }
        if (game->menu_on) {
            menu_draw(game->menu);
        }
    }
    land_color(0, 0, 0, 1);
    land_font_set(a->medium);
    land_push_transform();
    land_scale(w / 960.0, w / 960.0);
    if (a->overview) {
        land_color(0, 0, 0, 1);
        land_text_pos(20, 0);
        land_print("%d", g->overview->selected);
    }
    if (a->show_map) {
        map_render();
    }
    if (! a->render_screenshot && ! a->overview) {
        land_font_set(a->medium);
        float v [8];
        float o = 72;
        float m = 480;
        Viewport * vp = game->viewport;
        if (a->editor) {
            project(vp, o - m, 0, o - m, v + 0, v + 1);
            project(vp, o - m, 0, o + m, v + 2, v + 3);
            project(vp, o + m, 0, o + m, v + 4, v + 5);
            project(vp, o + m, 0, o - m, v + 6, v + 7);
            land_color(.5, .5, .5, 1);
            land_polygon(4, v);
        }
        land_color(0, 0, 0, 1);
        land_text_pos(20, 0);
        land_print("%s", g->title);
        if (a->editor) {
            land_print("%d", g->level);
        }
        if (g->ticks < 180) {
            land_text_pos(960 * 3 / 4, fh);
            land_print_center("%s", TITLE);
            land_print_center("%s", "by elias");
        }
        if (! a->show_map && (g->ticks > 600 || a->editor)) {
            float y = h / (w / 960) - 3 * fh;
            land_text_pos(20, y);
            land_print_wordwrap(w, h, "%s", g->hint);
        }
    }
    land_pop_transform();
}
void render_block_scaled(Block * self, Viewport * viewport, double scaled) {
    Application * a = app();
    BlockType * bt = self->block_type;
    float x, y;
    project(viewport, self->x + self->xs / 2, self->y, self->z + self->zs / 2, & x, & y);
    //x += bt->x
    //y += bt->y
    y -= 36 /* when rendering in blender we add an offset, for historic reasons */;
    bool hide = bt->invisible;
    if (self->block_type->bitmaps && ! hide) {
        float cr = 1;
        float cg = 1;
        float cb = 1;
        float ca = 1;
        float scale = 24;
        float height = floor(self->y / scale + 4);
        if (bt->transparent) {
            height = 0;
        }
        if (height >= 0) {
            cb -= height / 64.0;
            cg -= height / 128.0;
        }
        else {
            cb += height / 64;
            cr += height / 64;
        }
        if (a->tint.a) {
            cr = a->tint.r;
            cg = a->tint.g;
            cb = a->tint.b;
            ca = a->tint.a;
        }
        LandImage * frame = land_array_get_nth(bt->bitmaps, self->frame);
        land_image_load_on_demand(frame);
        //x -= land_image_width(frame) / 4
        //y -= land_image_height(frame) / 4
        float xscaled = scaled;
        if (self->flipped) {
            xscaled = - xscaled;
        }
        land_image_draw_scaled_tinted(frame, x, y, xscaled, scaled, cr, cg, cb, ca);
    }
    bool show_bounds = debug_bounding_boxes;
    bool show_misaligned = 0;
    bool show_ground = self == editor->picked;
    if (a->editor) {
        if (self == editor->picked) {
            show_bounds = 1;
        }
        float s = 24;
        if (self->y < - s * 6) {
            show_ground = 1;
            show_bounds = 1;
        }
        float ix = self->x / s;
        float iy = self->y / s;
        float iz = self->z / s;
        int xi = floor(ix);
        int yi = floor(iy);
        int zi = floor(iz);
        if (xi != ix || yi != iy || zi != iz) {
            show_bounds = 1;
            show_misaligned = 1;
        }
    }
    if (show_bounds) {
        float x2 = self->x + self->xs;
        float y2 = self->y + self->ys;
        float z2 = self->z + self->zs;
        float v [22];
        project(viewport, self->x, y2, self->z, v + 0, v + 1);
        project(viewport, self->x, y2, z2, v + 2, v + 3);
        project(viewport, x2, y2, z2, v + 4, v + 5);
        project(viewport, x2, y2, self->z, v + 6, v + 7);
        project(viewport, self->x, self->y, z2, v + 8, v + 9);
        project(viewport, x2, self->y, z2, v + 10, v + 11);
        project(viewport, x2, self->y, self->z, v + 12, v + 13);
        //
        //      0
        //    /   .
        //  1       3
        //  | \   / |
        //  4   2   6
        //    \ | /
        //      5
        if (show_misaligned) {
            land_color(0.75, 0, 0.75, 1);
            land_line(v [0], v [1], v [4], v [5]);
            land_line(v [2], v [3], v [6], v [7]);
        }
        else {
            land_color(0, 0, 0, 1);
        }
        land_line(v [0], v [1], v [2], v [3]);
        land_line(v [2], v [3], v [8], v [9]);
        land_line(v [8], v [9], v [10], v [11]);
        land_line(v [10], v [11], v [12], v [13]);
        land_line(v [12], v [13], v [6], v [7]);
        land_line(v [6], v [7], v [0], v [1]);
        if (show_ground) {
            project(viewport, self->x, 0, self->z, v + 14, v + 15);
            project(viewport, self->x, 0, z2, v + 16, v + 17);
            project(viewport, x2, 0, z2, v + 18, v + 19);
            project(viewport, x2, 0, self->z, v + 20, v + 21);
            land_color(1, 0, 0, 1);
            land_line(v [14], v [15], v [16], v [17]);
            land_line(v [16], v [17], v [18], v [19]);
            land_line(v [18], v [19], v [20], v [21]);
            land_line(v [20], v [21], v [14], v [15]);
        }
    }
}
void render_block(Block * self, Viewport * viewport) {
    render_block_scaled(self, viewport, 0.5);
}
void render_blocks(Blocks * blocks, Viewport * viewport) {
    // Each block has a cache of other blocks which are drawn earlier but also
    // need to appear in front of the block. For static blocks this cache is
    // built only the first time they are drawn. Since all static blocks are
    // drawn before any dynamic blocks, that cache cannot possibly change after
    // all.
    // For dynamic blocks the cache has to be rebuilt whenever any of them moves
    // however.
    float z = viewport->zoom;
    land_reset_transform();
    land_scale(z, z);
    int n1 = land_array_count(blocks->fixed);
    int n2 = land_array_count(blocks->dynamic);
    int n3 = land_array_count(blocks->transparent);
    for (int i = 0; i < n1 + n2 + n3; i += 1) {
        Block * b;
        if (i < n1) {
            b = land_array_get_nth(blocks->fixed, i);
        }
        else if (i < n1 + n2) {
            b = land_array_get_nth(blocks->dynamic, i - n1);
        }
        else {
            b = land_array_get_nth(blocks->transparent, i - n1 - n2);
        }
        BlockType * bt = b->block_type;
        bool need_mask = 0;
        // Need to rebuild the cache for this block?
        if ((! bt->dynamic && blocks->rebuild_static_cache) || (bt->dynamic && blocks->rebuild_dynamic_cache) || (bt->transparent && blocks->rebuild_dynamic_cache)) {
            land_array_clear(b->cache);
            int n = n1;
            if (bt->dynamic || bt->transparent) {
                n += n2;
            }
            for (int j = 0; j < n; j += 1) {
                Block * already;
                if (j < n1) {
                    already = land_array_get_nth(blocks->fixed, j);
                }
                else {
                    already = land_array_get_nth(blocks->dynamic, j - n1);
                }
                if (debug_no_mask) {
                    break;
                }
                if (already == b) {
                    break;
                }
                if (block_sort_order(already, b, viewport) == 1) {
                    land_array_add(b->cache, already);
                }
            }
        }
        {
            LandArrayIterator __iter0__ = LandArrayIterator_first(b->cache);
            for (Block * already = LandArrayIterator_item(b->cache, &__iter0__); LandArrayIterator_next(b->cache, &__iter0__); already = LandArrayIterator_item(b->cache, &__iter0__)) {
                if (! debug_no_mask && ! need_mask) {
                    need_mask = 1;
                    // Unconditionally write into depth buffer only (to create
                    // mask). The mask will have all blocks which have been
                    // drawn already but are in front (and so should not be
                    // overdrawn).
                    land_render_state(LAND_DEPTH_TEST, 1);
                    land_render_state(LAND_DEPTH_FUNCTION, LAND_ALWAYS);
                    land_render_state(LAND_WRITE_MASK, LAND_DEPTH_MASK);
                    float x1, y1, x2, y2;
                    block_get_bounding_rect(b, viewport, & x1, & y1, & x2, & y2);
                    x1 -= 30;
                    y1 -= 30;
                    x2 += 60;
                    y2 += 60;
                    land_clip((int)(x1 * z), (int)(y1 * z), (int)(x2 * z) + 1, (int)(y2 * z) + 1);
                }
                render_block(already, viewport);
            }
        }
        if (! debug_no_mask && need_mask) {
            // Write only color (no depth), but only where there is no mask.
            land_render_state(LAND_DEPTH_FUNCTION, LAND_LESS);
            land_render_state(LAND_WRITE_MASK, LAND_RGBA_MASK);
        }
        //x1, y1, x2, y2 = b.get_bounding_rect(viewport)
        render_block(b, viewport);
        //if not debug->no_mask and need_mask:
        //    render_block(b, viewport)
        //c = al_map_rgb_f(1, 0, 0)
        //al_draw_line(x1 + 2, y1 + 2, x2 - 2, y1 + 2, c, 1)
        //al_draw_line(x1 + 2, y1 + 2, x1 + 2, y2 - 2, c, 1)
        if (! debug_no_mask && need_mask) {
            // Restore everything to how it was before this block was drawn.
            land_render_state(LAND_WRITE_MASK, LAND_DEPTH_MASK | LAND_RGBA_MASK);
            land_clear_depth(1);
            land_render_state(LAND_DEPTH_TEST, 0);
            land_unclip();
        }
    }
    //for Block *b in LandArray *blocks->transparent:
    //    render_block(b, viewport)
    blocks->rebuild_static_cache = 0;
    blocks->rebuild_dynamic_cache = 0;
}
void render_block_speech_bubbles(Blocks * blocks, Viewport * viewport) {
    Application * a = app();
    float z = viewport->zoom;
    land_reset_transform();
    land_scale(z, z);
    int n1 = land_array_count(blocks->fixed);
    int n2 = land_array_count(blocks->dynamic);
    int n3 = land_array_count(blocks->transparent);
    for (int i = 0; i < n1 + n2 + n3; i += 1) {
        Block * b;
        if (i < n1) {
            b = land_array_get_nth(blocks->fixed, i);
        }
        else if (i < n1 + n2) {
            b = land_array_get_nth(blocks->dynamic, i - n1);
        }
        else {
            b = land_array_get_nth(blocks->transparent, i - n1 - n2);
        }
        if (b->speech && b->text) {
            float x1, y1, x2, y2;
            block_get_bounding_rect(b, viewport, & x1, & y1, & x2, & y2);
            float sx = x2 - 10;
            float sy = y1 + 40;
            bool done = 1;
            if (b->thought) {
                int bubble_duration = 15;
                land_color(1, 1, 1, 1);
                land_thickness(2);
                for (int bi = 0; bi < 3; bi += 1) {
                    float bx = sx;
                    float by = sy;
                    float rad = 0;
                    float maxrad = 4 + bi * 3;
                    if (bi >= 1) {
                        bx += 8 * 2;
                        by -= 8;
                    }
                    if (bi >= 2) {
                        bx += 12 * 2;
                        by -= 12;
                    }
                    if (b->speech < bi * bubble_duration) {
                        rad = 0;
                    }
                    else if (b->speech < (bi + 1) * bubble_duration) {
                        rad = (b->speech - bi * bubble_duration) * maxrad / bubble_duration;
                    }
                    else {
                        rad = maxrad;
                    }
                    if (rad > 0) {
                        land_circle(bx - rad, by - rad, bx + rad, by + rad);
                    }
                }
                if (b->speech < 3 * bubble_duration) {
                    done = 0;
                }
            }
            land_thickness(0);
            float w, h;
            if (done) {
                float bx = x2 + 50;
                float by = y1;
                float attach_x = x2 + 55;
                land_font_set(a->medium);
                land_text_get_multiline_size(b->text, & w, & h);
                if (bx + w + 20 > 960) {
                    bx = x1 - w - 50;
                    sx = x1;
                    attach_x = x1 - 40;
                }
                h += 60;
                land_color(1, 1, 1, 1);
                land_filled_circle(bx, by - h / 2, bx + w + 20, by + h / 2);
                land_color(0, 0, 0, 1);
                land_text_pos(bx + 10 + w / 2, by - h / 2 + 30);
                land_print_multiline_centered(b->text);
                land_circle(bx, by - h / 2, bx + w + 20, by + h / 2);
                if (! b->thought) {
                    land_color(1, 1, 1, 1);
                    float cy = y1 - 5;
                    land_filled_triangle(sx, sy, attach_x, cy, attach_x, cy + 10);
                    land_color(0, 0, 0, 1);
                    land_line(sx, sy, attach_x, cy);
                    land_line(sx, sy, attach_x, cy + 10);
                }
            }
        }
    }
}
static str _vertex_shader = "\n"
    "attribute vec4 al_pos;\n"
    "attribute vec4 al_color;\n"
    "attribute vec2 al_texcoord;\n"
    "uniform mat4 al_projview_matrix;\n"
    "uniform bool al_use_tex_matrix;\n"
    "uniform mat4 al_tex_matrix;\n"
    "varying vec4 varying_color;\n"
    "varying vec2 varying_texcoord;\n"
    "void main()\n"
    "{\n"
    "  varying_color = al_color;\n"
    "  if (al_use_tex_matrix) {\n"
    "    vec4 uv = al_tex_matrix * vec4(al_texcoord, 0, 1);\n"
    "    varying_texcoord = vec2(uv.x, uv.y);\n"
    "  }\n"
    "  else\n"
    "    varying_texcoord = al_texcoord;\n"
    "  gl_Position = al_projview_matrix * al_pos;\n"
    "}\n"
    "";
static str _fragment_shader = "\n"
    "#ifdef GL_ES\n"
    "precision lowp float;\n"
    "#endif\n"
    "uniform sampler2D al_tex;\n"
    "uniform bool al_use_tex;\n"
    "varying vec4 varying_color;\n"
    "varying vec2 varying_texcoord;\n"
    "void main()\n"
    "{\n"
    "  vec4 c;\n"
    "  if (al_use_tex)\n"
    "    c = varying_color * texture2D(al_tex, varying_texcoord);\n"
    "  else\n"
    "    c = varying_color;\n"
    "  if (c.a < 0.5) discard;\n"
    "  else gl_FragColor = c;\n"
    "}\n"
    "";
void render_set_shaders(void) {
    if (r.shader) {
        return ;
    }
    r.shader = land_shader_new("shader", _vertex_shader, _fragment_shader);
}
