#include <ultra64.h>

#include "sm64.h"
#include "engine/math_util.h"
#include "memory.h"
#include "area.h"
#include "save_file.h"
#include "segment2.h"
#include "level_update.h"
#include "geo_misc.h"

struct Struct8035FF50 {
    u16 unk0;
    s16 unk2;
    s32 unk4;
    s32 unk8;
    s32 unkC;
};

struct Struct8032FFC8 {
    u8 unk0;
    u8 unk1;
    u8 unk2;
    u8 filler3[1];
};

struct SkyboxList {
    struct Gfx *ptr[50];
};

extern struct SkyboxList water_skybox_ptrlist_0A020000;
extern struct SkyboxList bitfs_skybox_ptrlist_0A018800;
extern struct SkyboxList wdw_skybox_ptrlist_0A020000;
extern struct SkyboxList cloud_floor_skybox_ptrlist_0A020000;
extern struct SkyboxList ccm_skybox_ptrlist_0A020000;
extern struct SkyboxList ssl_skybox_ptrlist_0A020000;
extern struct SkyboxList bbh_skybox_ptrlist_0A014800;
extern struct SkyboxList bidw_skybox_ptrlist_0A020000;
extern struct SkyboxList clouds_skybox_ptrlist_0A014800;
extern struct SkyboxList bits_skybox_ptrlist_0A020000;

// TODO: These should be defined here
struct Struct8035FF50 D_8035FF50[2];

struct SkyboxList *gSkyboxLists[10] = {
    &water_skybox_ptrlist_0A020000,       // water_skybox
    &bitfs_skybox_ptrlist_0A018800,       // bitfs_skybox
    &wdw_skybox_ptrlist_0A020000,         // wdw_skybox
    &cloud_floor_skybox_ptrlist_0A020000, // cloud_floor_skybox
    &ccm_skybox_ptrlist_0A020000,         // ccm_skybox
    &ssl_skybox_ptrlist_0A020000,         // ssl_skybox
    &bbh_skybox_ptrlist_0A014800,         // bbh_skybox
    &bidw_skybox_ptrlist_0A020000,        // bidw_skybox
    &clouds_skybox_ptrlist_0A014800,      // clouds_skybox
    &bits_skybox_ptrlist_0A020000,        // bits_skybox
};
u8 gSkyboxColors[][3] = {
    { 0x50, 0x64, 0x5A },
    { 0xFF, 0xFF, 0xFF },
};

//! double literals are used instead of floats

int func_802CEAD0(s8 a, f32 b) {
    f32 spC = D_8035FF50[a].unk0;
    f32 sp8 = (115200.0 * spC) / (b * 65536.0);
    s32 sp4 = sp8 + 0.5;

    if (sp4 > 1280)
        sp4 -= sp4 / 1280 * 1280;
    return 1280 - sp4;
}

int func_802CEBBC(s8 a, UNUSED f32 b) {
    f32 sp24 = (f32) D_8035FF50[a].unk2 * 360.0 / 65535.0;
    f32 sp20 = 360.0f * sp24 / 90.0;
    s32 sp1C = round_float(sp20);
    s32 sp18 = sp1C + 0x258;

    if (sp18 > 960)
        sp18 = 960;
    if (sp18 < 240)
        sp18 = 240;
    return sp18;
}

int func_802CEC9C(s8 a) {
    s32 sp4 = D_8035FF50[a].unk4 / 160;
    s32 sp0 = (960 - D_8035FF50[a].unk8) / 120;

    return sp0 * 10 + sp4;
}

// generates vertices for some rectangle
Vtx *make_skybox_rect(s32 a, s8 b) {
    Vtx *verts = alloc_display_list(4 * sizeof(*verts));
    s16 x = a % 10 * 160;
    s16 y = 960 - a / 10 * 120;

    if (verts != NULL) {
        make_vertex(verts, 0, x, y, -1, 0, 0, gSkyboxColors[b][0], gSkyboxColors[b][1],
                    gSkyboxColors[b][2], 255);
        make_vertex(verts, 1, x, y - 0x78, -1, 0, 992, gSkyboxColors[b][0], gSkyboxColors[b][1],
                    gSkyboxColors[b][2], 255);
        make_vertex(verts, 2, x + 160, y - 0x78, -1, 992, 992, gSkyboxColors[b][0], gSkyboxColors[b][1],
                    gSkyboxColors[b][2], 255);
        make_vertex(verts, 3, x + 160, y, -1, 992, 0, gSkyboxColors[b][0], gSkyboxColors[b][1],
                    gSkyboxColors[b][2], 255);
    } else {
    }
    return verts;
}

void func_802CEF4C(Gfx **dlist, s8 b, s8 c, s8 d) {
    s32 sp54;
    s32 sp50;

    for (sp54 = 0; sp54 < 3; sp54++) {
        for (sp50 = 0; sp50 < 3; sp50++) {
            s32 sp4C = D_8035FF50[c].unkC + sp54 * 10 + sp50;
            struct Gfx *sp48 =
                ((struct SkyboxList *) segmented_to_virtual((void *) gSkyboxLists[b]))->ptr[sp4C];
            Vtx *vertices = make_skybox_rect(sp4C, d);

            // Why is the width 1 here?
            gDPSetTextureImage((*dlist)++, G_IM_FMT_RGBA, G_IM_SIZ_16b, 1, sp48);
            gDPTileSync((*dlist)++);
            gDPSetTile((*dlist)++, G_IM_FMT_RGBA, G_IM_SIZ_16b, 0, 0, 7, 0, G_TX_NOMIRROR | G_TX_WRAP,
                       G_TX_NOMASK, G_TX_NOLOD, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOLOD);
            gDPLoadSync((*dlist)++);
            gDPLoadBlock((*dlist)++, 7, 0, 0, 1023, 256);
            gSPVertex((*dlist)++, VIRTUAL_TO_PHYSICAL(vertices), 4, 0);
            gSPDisplayList((*dlist)++, dl_draw_quad_verts_0123);
        }
    }
}

void *func_802CF188(s8 a) {
    f32 left = D_8035FF50[a].unk4;
    f32 right = D_8035FF50[a].unk4 + 0x140;
    f32 bottom = D_8035FF50[a].unk8 - 0xF0;
    f32 top = D_8035FF50[a].unk8;
    Mtx *mtx = alloc_display_list(sizeof(*mtx));

    if (mtx != NULL) {
        guOrtho(mtx, left, right, bottom, top, 0.0f, 3.0f, 1.0f);
    } else {
    }

    return mtx;
}

Gfx *func_802CF2A8(s8 a, s8 b, s8 c) {
    s32 sp3C = 68;
    void *sp38 = alloc_display_list(sp3C * 8);
    Gfx *dlist = sp38;

    if (sp38 == NULL) {
        return NULL;
    } else {
        Mtx *mtx = func_802CF188(a);

        gSPDisplayList(dlist++, dl_skybox_begin);
        gSPMatrix(dlist++, VIRTUAL_TO_PHYSICAL(mtx), G_MTX_PROJECTION | G_MTX_MUL);
        gSPDisplayList(dlist++, dl_skybox_tex_settings);
        func_802CEF4C(&dlist, b, a, c);
        gSPDisplayList(dlist++, dl_skybox_end);
        gSPEndDisplayList(dlist);
    }
    return sp38;
}

Gfx *func_802CF414(s8 a, s8 b, f32 c, f32 d, f32 e, f32 f, f32 g, f32 h, f32 i) {
    f32 sp34 = g - d;
    f32 sp30 = h - e;
    f32 sp2C = i - f;
    s8 sp2B = 1;

    if (b == 8 && !(save_file_get_star_flags(gCurrSaveFileNum - 1, 2) & 1))
        sp2B = 0;

    c = 90.0f;
    D_8035FF50[a].unk0 = atan2s(sp2C, sp34);
    D_8035FF50[a].unk2 = atan2s(sqrtf(sp34 * sp34 + sp2C * sp2C), sp30);
    D_8035FF50[a].unk4 = func_802CEAD0(a, c);
    D_8035FF50[a].unk8 = func_802CEBBC(a, c);
    D_8035FF50[a].unkC = func_802CEC9C(a);

    return func_802CF2A8(a, b, sp2B);
}
