#include "pch.h"
#include "engine.h"

Texture *sky[6] = { 0, 0, 0, 0, 0, 0 }, *clouds[6] = { 0, 0, 0, 0, 0, 0 };

void loadsky(const char *basename, Texture *texs[6])
{
    loopi(6)
    {
        const char *side = cubemapsides[i].name;
        s_sprintfd(name)("packages/%s_%s.jpg", basename, side);
        if((texs[i] = textureload(name, 3, true, false))==notexture)
        {
            strcpy(name+strlen(name)-3, "png");
            if((texs[i] = textureload(name, 3, true, false))==notexture) conoutf(CON_ERROR, "could not load sky texture packages/%s_%s", basename, side);
        }
    }
}

Texture *cloudoverlay = NULL;

Texture *loadskyoverlay(const char *basename)
{
    s_sprintfd(name)("packages/%s.jpg", basename);
    Texture *t = textureload(name, 0, true, false);
    if(t!=notexture) return t;
    strcpy(name+strlen(name)-3, "png");
    t = textureload(name, 0, true, false);
    if(t==notexture) conoutf(CON_ERROR, "could not loda sky overlay texture packages/%s", basename);
    return t;
}

SVARF(skybox, "", { if(skybox[0]) loadsky(skybox, sky); }); 
FVAR(spinsky, 0);
VAR(yawsky, 0, 0, 360);
SVARF(cloudbox, "", { if(cloudbox[0]) loadsky(cloudbox, clouds); });
FVAR(spinclouds, 0);
VAR(yawclouds, 0, 0, 360);
FVAR(cloudclip, 0.5f);
SVARF(cloudlayer, "", { if(cloudlayer[0]) cloudoverlay = loadskyoverlay(cloudlayer); });
FVAR(cloudscrollx, 0);
FVAR(cloudscrolly, 0);
FVAR(cloudheight, 0.65f);
FVAR(cloudfade, 0.1f);
VAR(cloudsubdiv, 4, 8, 32);

void draw_envbox_face(float s0, float t0, int x0, int y0, int z0,
                      float s1, float t1, int x1, int y1, int z1,
                      float s2, float t2, int x2, int y2, int z2,
                      float s3, float t3, int x3, int y3, int z3,
                      GLuint texture)
{
    glBindTexture(GL_TEXTURE_2D, texture);
    glBegin(GL_QUADS);
    glTexCoord2f(s3, t3); glVertex3i(x3, y3, z3);
    glTexCoord2f(s2, t2); glVertex3i(x2, y2, z2);
    glTexCoord2f(s1, t1); glVertex3i(x1, y1, z1);
    glTexCoord2f(s0, t0); glVertex3i(x0, y0, z0);
    glEnd();
    xtraverts += 4;
}

void draw_envbox(int w, float zclip = 0.0f, int faces = 0x3F, Texture **sky = NULL)
{
    float vclip = 1-zclip;
    int z = int(ceil(2*w*(vclip-0.5f)));

    if(faces&0x01)
        draw_envbox_face(0.0f, 0.0f, -w, -w, -w,
                         1.0f, 0.0f, -w,  w, -w,
                         1.0f, vclip, -w,  w,  z,
                         0.0f, vclip, -w, -w,  z, sky[0] ? sky[0]->id : notexture->id);

    if(faces&0x02)
        draw_envbox_face(1.0f, vclip, +w, -w,  z,
                         0.0f, vclip, +w,  w,  z,
                         0.0f, 0.0f, +w,  w, -w,
                         1.0f, 0.0f, +w, -w, -w, sky[1] ? sky[1]->id : notexture->id);

    if(faces&0x04)
        draw_envbox_face(1.0f, vclip, -w, -w,  z,
                         0.0f, vclip,  w, -w,  z,
                         0.0f, 0.0f,  w, -w, -w,
                         1.0f, 0.0f, -w, -w, -w, sky[2] ? sky[2]->id : notexture->id);

    if(faces&0x08)
        draw_envbox_face(1.0f, vclip, +w,  w,  z,
                         0.0f, vclip, -w,  w,  z,
                         0.0f, 0.0f, -w,  w, -w,
                         1.0f, 0.0f, +w,  w, -w, sky[3] ? sky[3]->id : notexture->id);

    if(!zclip && faces&0x10)
        draw_envbox_face(0.0f, 1.0f, -w,  w,  w,
                         0.0f, 0.0f, +w,  w,  w,
                         1.0f, 0.0f, +w, -w,  w,
                         1.0f, 1.0f, -w, -w,  w, sky[4] ? sky[4]->id : notexture->id);

    if(faces&0x20)
        draw_envbox_face(0.0f, 1.0f, +w,  w, -w,
                         0.0f, 0.0f, -w,  w, -w,
                         1.0f, 0.0f, -w, -w, -w,
                         1.0f, 1.0f, +w, -w, -w, sky[5] ? sky[5]->id : notexture->id);
}

void draw_env_overlay(int w, Texture *overlay = NULL, float tx = 0, float ty = 0)
{
    float z = w - 2*w*cloudheight, tsz = 0.5f*(1-cloudfade), psz = w*(1-cloudfade);;
    glBindTexture(GL_TEXTURE_2D, overlay ? overlay->id : notexture->id);
    glColor3f(1, 1, 1);
    glBegin(GL_POLYGON);
    loopi(cloudsubdiv)
    {
        vec p(1, 1, 0);
        p.rotate_around_z((-2.0f*M_PI*i)/cloudsubdiv);
        glTexCoord2f(tx + p.x*tsz, ty + p.y*tsz); glVertex3f(p.x*psz, p.y*psz, z);
    }
    glEnd();
    glBegin(GL_QUAD_STRIP);
    loopi(cloudsubdiv+1)
    {
        vec p(1, 1, 0);
        p.rotate_around_z((-2.0f*M_PI*i)/cloudsubdiv);
        glColor4f(1, 1, 1, 1);
        glTexCoord2f(tx + p.x*tsz, ty + p.y*tsz); glVertex3f(p.x*psz, p.y*psz, z);
        glColor4f(1, 1, 1, 0);
        glTexCoord2f(tx + p.x*0.5f, ty + p.y*0.5f); glVertex3f(p.x*w, p.y*w, z);
    }
    glEnd();    
}

VARP(sparklyfix, 0, 0, 1);
VAR(showsky, 0, 1, 1); 
VAR(clipsky, 0, 1, 1);

bool drawskylimits(bool explicitonly)
{
    nocolorshader->set();

    glDisable(GL_TEXTURE_2D);
    glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
    bool rendered = rendersky(explicitonly);
    glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
    glEnable(GL_TEXTURE_2D);

    return rendered;
}

void drawskyoutline()
{
    notextureshader->set();

    glDisable(GL_TEXTURE_2D);
    glDepthMask(GL_FALSE);
    extern int wireframe;
    if(!wireframe)
    {
        enablepolygonoffset(GL_POLYGON_OFFSET_LINE);
        glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
    }
    glColor3f(0.5f, 0.0f, 0.5f);
    rendersky(true);
    if(!wireframe) 
    {
        glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
        disablepolygonoffset(GL_POLYGON_OFFSET_LINE);
    }
    glDepthMask(GL_TRUE);
    glEnable(GL_TEXTURE_2D);

    if(!glaring) defaultshader->set();
}

void drawskybox(int farplane, bool limited)
{
    extern int renderedskyfaces, renderedskyclip; // , renderedsky, renderedexplicitsky;
    bool alwaysrender = editmode || !insideworld(camera1->o) || reflecting,
         explicitonly = false;
    if(limited)
    {
        explicitonly = alwaysrender || !sparklyfix || refracting; 
        if(!drawskylimits(explicitonly) && !alwaysrender) return;
        extern int ati_skybox_bug;
        if(!alwaysrender && !renderedskyfaces && !ati_skybox_bug) explicitonly = false;
    }
    else if(!alwaysrender)
    {
        extern vtxarray *visibleva;
        renderedskyfaces = 0;
        renderedskyclip = INT_MAX;
        for(vtxarray *va = visibleva; va; va = va->next)
        {
            if(va->occluded >= OCCLUDE_BB && va->skyfaces&0x80) continue;
            renderedskyfaces |= va->skyfaces&0x3F;
            if(!(va->skyfaces&0x1F) || camera1->o.z < va->skyclip) renderedskyclip = min(renderedskyclip, va->skyclip);
            else renderedskyclip = 0;
        }
        if(!renderedskyfaces) return;
    }
    
    if(alwaysrender)
    {
        renderedskyfaces = 0x3F;
        renderedskyclip = 0;
    }

    if(glaring)
    {
        static Shader *skyboxglareshader = NULL;
        if(!skyboxglareshader) skyboxglareshader = lookupshaderbyname("skyboxglare");
        skyboxglareshader->set();
    }
    else defaultshader->set();

    bool fog = glIsEnabled(GL_FOG)==GL_TRUE;
    if(fog) glDisable(GL_FOG);

    if(limited) 
    {
        if(explicitonly) glDisable(GL_DEPTH_TEST);
        else glDepthFunc(GL_GEQUAL);
    }
    float skyclip = clipsky ? max(renderedskyclip-1, 0) : 0;
    if(reflectz<hdr.worldsize && reflectz>skyclip) skyclip = reflectz;

    glDepthMask(GL_FALSE);

    glColor3f(1, 1, 1);

    glPushMatrix();
    glLoadIdentity();
    glRotatef(camera1->roll, 0, 0, 1);
    glRotatef(camera1->pitch, -1, 0, 0);
    glRotatef(camera1->yaw+spinsky*lastmillis/1000.0f+yawsky, 0, 1, 0);
    glRotatef(90, 1, 0, 0);
    if(reflecting) glScalef(1, 1, -1);
    draw_envbox(farplane/2, skyclip ? 0.5f + 0.5f*(skyclip-camera1->o.z)/float(hdr.worldsize) : 0, renderedskyfaces | ((spinsky || yawsky) && renderedskyfaces&0x0F ? 0x0F : 0), sky);
    glPopMatrix();

    if(!glaring && cloudbox[0])
    {
        if((spinclouds || yawclouds) && renderedskyfaces&0x0F) renderedskyfaces |= 0x0F;

        if(fading) glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE);

        glEnable(GL_BLEND);
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

        glPushMatrix();
        glLoadIdentity();
        glRotatef(camera1->roll, 0, 0, 1);
        glRotatef(camera1->pitch, -1, 0, 0);
        glRotatef(camera1->yaw+spinclouds*lastmillis/1000.0f+yawclouds, 0, 1, 0);
        glRotatef(90, 1, 0, 0);
        if(reflecting) glScalef(1, 1, -1);
        draw_envbox(farplane/2, skyclip ? 0.5f + 0.5f*(skyclip-camera1->o.z)/float(hdr.worldsize) : cloudclip, renderedskyfaces | ((spinclouds || yawclouds) && renderedskyfaces&0x0F ? 0x0F : 0), clouds);
        glPopMatrix();

        glDisable(GL_BLEND);
    }

    if(!glaring && cloudlayer[0] && renderedskyfaces&0x2F)
    {
        if(fading) glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE);

        glEnable(GL_BLEND);
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

        glPushMatrix();
        glLoadIdentity();
        glRotatef(camera1->roll, 0, 0, 1);
        glRotatef(camera1->pitch, -1, 0, 0);
        glRotatef(camera1->yaw+spinsky*lastmillis/1000.0f+yawsky, 0, 1, 0);
        glRotatef(90, 1, 0, 0);
        if(reflecting) glScalef(1, 1, -1);
        draw_env_overlay(farplane/2, cloudoverlay, cloudscrollx * lastmillis/1000.0f, cloudscrolly * lastmillis/1000.0f);
        glPopMatrix();

        glDisable(GL_BLEND);
    }

    glDepthMask(GL_TRUE);

    if(limited)
    {
        if(explicitonly) glEnable(GL_DEPTH_TEST);
        else glDepthFunc(GL_LESS);
        if(!reflecting && !refracting && !envmapping && editmode && showsky) drawskyoutline();
    }

    if(fog) glEnable(GL_FOG);
}

VARN(skytexture, useskytexture, 0, 1, 1);

int explicitsky = 0;
double skyarea = 0;

bool limitsky()
{
    return (explicitsky && (useskytexture || editmode)) || (sparklyfix && skyarea / (double(hdr.worldsize)*double(hdr.worldsize)*6) < 0.9);
}

