#ifdef TARGET_DREAMCAST
#include "Win95/stdDisplay.h"

#include "stdPlatform.h"
#include "jk.h"
#include "Win95/Video.h"
#include "Win95/Window.h"
#include "General/stdColor.h"

uint32_t Video_menuTexId = 0;
uint32_t Video_overlayTexId = 0;
rdColor24 stdDisplay_masterPalette[256];
int Video_bModeSet = 0;

int stdDisplay_Startup()
{
    stdDisplay_bStartup = 1;
    return 1;
}

int stdDisplay_FindClosestDevice(void* a)
{
    Video_dword_866D78 = 0;
    return 0;
}

int stdDisplay_Open(int a)
{
    stdDisplay_pCurDevice = &stdDisplay_aDisplayDevices[0];
    stdDisplay_bOpen = 1;
    return 1;
}

void stdDisplay_Close()
{
    stdDisplay_bOpen = 0;
}

int stdDisplay_FindClosestMode(render_pair *a1, struct StdVideoMode *render_surface, unsigned int numModes)
{
    Video_curMode = 0;
    stdDisplay_bPaged = 1;
    stdDisplay_bModeSet = 1;
    return 0;
}

int stdDisplay_SetMode(unsigned int modeIdx, const void *palette, int paged)
{
    uint32_t newW = Window_xSize;
    uint32_t newH = Window_ySize;

    //if (jkGame_isDDraw)
    {
        newW = (uint32_t)((flex_t)Window_xSize * ((480.0*2.0)/Window_ySize));
        newH = 480*2;
    }

    if (newW > Window_xSize)
    {
        newW = Window_xSize;
        newH = Window_ySize;
    }

    if (newW < 640)
        newW = 640;
    if (newH < 480)
        newH = 480;

    stdDisplay_pCurVideoMode = &Video_renderSurface[modeIdx];
    
    stdDisplay_pCurVideoMode->format.format.bpp = 8;
    stdDisplay_pCurVideoMode->format.rowWidth = newW;
    stdDisplay_pCurVideoMode->format.width = newW;
    stdDisplay_pCurVideoMode->format.height = newH;
    
    _memcpy(&Video_otherBuf.format, &stdDisplay_pCurVideoMode->format, sizeof(Video_otherBuf.format));
    _memcpy(&Video_menuBuffer.format, &stdDisplay_pCurVideoMode->format, sizeof(Video_menuBuffer.format));
    
    //_memcpy(&Video_overlayMapBuffer.format, &stdDisplay_pCurVideoMode->format, sizeof(Video_overlayMapBuffer.format));
    

    if (Video_bModeSet)
    {
        //glDeleteTextures(1, &Video_menuTexId);
        //glDeleteTextures(1, &Video_overlayTexId);
        
        //if (Video_otherBuf.sdlSurface)
        //    SDL_FreeSurface(Video_otherBuf.sdlSurface);
        //if (Video_menuBuffer.sdlSurface)
        //    SDL_FreeSurface(Video_menuBuffer.sdlSurface);
        //if (Video_overlayMapBuffer.sdlSurface)
        //    SDL_FreeSurface(Video_overlayMapBuffer.sdlSurface);
        
        //Video_otherBuf.sdlSurface = 0;
        //Video_menuBuffer.sdlSurface = 0;
        //Video_overlayMapBuffer.sdlSurface = 0;
    }
    
#if 0
    SDL_Surface* otherSurface = SDL_CreateRGBSurface(0, newW, newH, 8,
                                        0,
                                        0,
                                        0,
                                        0);
    SDL_Surface* menuSurface = SDL_CreateRGBSurface(0, newW, newH, 8,
                                        0,
                                        0,
                                        0,
                                        0);
    SDL_Surface* overlaySurface = SDL_CreateRGBSurface(0, newW, newH, 8, 0, 0, 0, 0);
    
    if (palette)
    {
        memcpy(stdDisplay_gammaPalette, palette, 0x300);
        const rdColor24* pal24 = palette;
        SDL_Color* tmp = malloc(sizeof(SDL_Color) * 256);
        for (int i = 0; i < 256; i++)
        {
            tmp[i].r = pal24[i].r;
            tmp[i].g = pal24[i].g;
            tmp[i].b = pal24[i].b;
            tmp[i].a = 0xFF;
        }
        
        SDL_SetPaletteColors(otherSurface->format->palette, tmp, 0, 256);
        SDL_SetPaletteColors(menuSurface->format->palette, tmp, 0, 256);
        SDL_SetPaletteColors(overlaySurface->format->palette, tmp, 0, 256);
        free(tmp);
    }
#endif
    //SDL_SetSurfacePalette(otherSurface, palette);
    //SDL_SetSurfacePalette(menuSurface, palette);
    uint32_t pitch = newW;

    //Video_otherBuf.sdlSurface = otherSurface;
    //Video_menuBuffer.sdlSurface = menuSurface;
    //Video_overlayMapBuffer.sdlSurface = overlaySurface;

    Video_menuBuffer.format.rowSize = pitch;
    Video_otherBuf.format.rowSize = pitch;
    //Video_overlayMapBuffer.format.rowSize = overlaySurface->pitch;
    
    Video_menuBuffer.format.rowWidth = pitch;
    Video_otherBuf.format.rowWidth = pitch;
    //Video_overlayMapBuffer.format.rowWidth = overlaySurface->pitch;

    Video_menuBuffer.format.width = newW;
    Video_otherBuf.format.width = newW;
    //Video_overlayMapBuffer.format.width = newW;
    Video_menuBuffer.format.height = newH;
    Video_otherBuf.format.height = newH;
    //Video_overlayMapBuffer.format.height = newH;
    
    Video_menuBuffer.format.format.bpp = 8;
    Video_otherBuf.format.format.bpp = 8;
    //Video_overlayMapBuffer.format.format.bpp = 8;

    Video_menuBuffer.format.size = pitch * newH;
    Video_otherBuf.format.size = pitch * newH;

    //out->format.width = 0;
    //out->format.rowSize = 0;
    if (!Video_menuBuffer.surface_lock_alloc)
        Video_menuBuffer.surface_lock_alloc = (char*)STD_ALLOC(Video_menuBuffer.format.size);
    // Added: the overlay buffer needs a surface too -- cutscene subtitles are
    // drawn here and composited into the menu buffer. Without it stdFont_Draw3
    // writes to a NULL surface and the subtitles silently never appear.
    if (!Video_otherBuf.surface_lock_alloc)
        Video_otherBuf.surface_lock_alloc = (char*)STD_ALLOC(Video_otherBuf.format.size);

#if 0
    glGenTextures(1, &Video_menuTexId);
    glBindTexture(GL_TEXTURE_2D, Video_menuTexId);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, newW, newH, 0, GL_RED, GL_UNSIGNED_BYTE, Video_menuBuffer.sdlSurface->pixels);
    
    glGenTextures(1, &Video_overlayTexId);
    glBindTexture(GL_TEXTURE_2D, Video_overlayTexId);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, newW, newH, 0, GL_RED, GL_UNSIGNED_BYTE, Video_overlayMapBuffer.sdlSurface->pixels);
#endif

    Video_bModeSet = 1;
    
    return 1;
}

int stdDisplay_ClearRect(tVBuffer *buf, int fillColor, rdRect *rect)
{
    return stdDisplay_VBufferFill(buf, fillColor, rect);
}



int stdDisplay_DDrawGdiSurfaceFlip()
{
    extern int Window_bFlipRequested;

    if (!jkCutscene_isRendering && !sithWorld_g_pLastLoadedWorld && !jkGame_isDDraw) {
        Window_bFlipRequested = 1;
    }
    else {
        Window_SdlUpdate();
    }
    return 1;
}

int stdDisplay_ddraw_waitforvblank()
{
    Window_SdlVblank();
    return 1;
}

int stdDisplay_SetMasterPalette(uint8_t* pal)
{
    rdColor24* pal24 = (rdColor24*)pal;
    
    if (!stdDisplay_masterPalette || !pal) return 0;

    memcpy(stdDisplay_masterPalette, pal24, sizeof(stdDisplay_masterPalette));
#if 0    
    SDL_Color* tmp = malloc(sizeof(SDL_Color) * 256);
    for (int i = 0; i < 256; i++)
    {
        tmp[i].r = pal24[i].r;
        tmp[i].g = pal24[i].g;
        tmp[i].b = pal24[i].b;
        tmp[i].a = 0xFF;
    }
    
    free(tmp);
#endif
    return 1;
}

tVBuffer* stdDisplay_VBufferNew(tRasterInfo *fmt, int create_ddraw_surface, int gpu_mem, const void* palette)
{
    tVBuffer* out = (tVBuffer*)STD_ALLOC(sizeof(tVBuffer));
    if (!out) {
        return NULL;
    }
    
    _memset(out, 0, sizeof(*out));
    
    _memcpy(&out->format, fmt, sizeof(out->format));
    
    // TODO
    out->format.rowSize = fmt->width;
    out->format.rowWidth = fmt->width;
    out->format.size = fmt->width * fmt->height;

    if (fmt->format.bpp == 16) {
        out->format.size *= 2;
    }

    //out->format.width = 0;
    //out->format.rowSize = 0;
    // pVBuffer pixel data is only accessed word-safe on this port, so allow
    // placement in word-addressable-only memory (the VRAM overflow arena).
    int prevSuggest = std_g_pHS->suggestHeap(HEAP_WORD_ADDRESSABLE);
    out->surface_lock_alloc = (char*)STD_ALLOC(out->format.size);
    std_g_pHS->suggestHeap(prevSuggest);
    if (!out->surface_lock_alloc) {
        STD_FREE(out);
        return NULL;
    }
    
#ifndef RDMATERIAL_MINIMIZE_STRUCTS
    //if (fmt->format.g_bits == 6) // RGB565
    {
        fmt->format.r_bits = 0;
        fmt->format.g_bits = 0;
        fmt->format.b_bits = 0;
        fmt->format.r_shift = 0;
        fmt->format.g_shift = 0;
        fmt->format.b_shift = 0;
    }

    uint32_t rbitmask = ((1 << fmt->format.r_bits) - 1) << fmt->format.r_shift;
    uint32_t gbitmask = ((1 << fmt->format.g_bits) - 1) << fmt->format.g_shift;
    uint32_t bbitmask = ((1 << fmt->format.b_bits) - 1) << fmt->format.b_shift;
    uint32_t abitmask = 0;//((1 << fmt->format.a_bits) - 1) << fmt->format.a_shift;
    if (fmt->format.bpp == 8)
    {
        rbitmask = 0;
        gbitmask = 0;
        bbitmask = 0;
        abitmask = 0;
    }
#endif

#if 0
    SDL_Surface* surface = SDL_CreateRGBSurface(0, fmt->width, fmt->height, fmt->format.bpp, rbitmask, gbitmask, bbitmask, abitmask);
    
    if (surface)
    {
        static int num = 0;
        //printf("Allocated VBuffer %u, w %u h %u bpp %u %x %x %x\n", num++, fmt->width, fmt->height, fmt->format.bpp, rbitmask, gbitmask, bbitmask);
        out->format.rowSize = surface->pitch;
        out->format.rowWidth = fmt->width;
        out->format.size = surface->pitch * fmt->height;
    }
    else
    {
        //printf("asdf\n");
        stdPlatform_Printf("Failed to allocate VBuffer! %s, w %u h %u bpp %u, rmask %x gmask %x bmask %x amask %x, %x %x %x, %x %x %x\n", SDL_GetError(), fmt->width, fmt->height, fmt->format.bpp, rbitmask, gbitmask, bbitmask, abitmask, fmt->format.r_bits, fmt->format.g_bits, fmt->format.b_bits, fmt->format.r_shift, fmt->format.g_shift, fmt->format.b_shift);
        assert(0);
    }
    //printf("Failed to allocate VBuffer! %s, w %u h %u bpp %u, rmask %x gmask %x bmask %x amask %x, %x %x %x, %x %x %x\n", SDL_GetError(), fmt->width, fmt->height, fmt->format.bpp, rbitmask, gbitmask, bbitmask, abitmask, fmt->format.r_bits, fmt->format.g_bits, fmt->format.b_bits, fmt->format.r_shift, fmt->format.g_shift, fmt->format.b_shift);
    
    out->sdlSurface = surface;
#endif
    
    return out;
}

int stdDisplay_VBufferLock(tVBuffer *buf)
{
    if (!buf) return 0;
    if (!buf->surface_lock_alloc) return 0;

    //SDL_LockSurface(buf->sdlSurface);
    //buf->surface_lock_alloc = buf->sdlSurface->pixels;
    return 1;
}

void stdDisplay_VBufferUnlock(tVBuffer *buf)
{
    if (!buf) return;

    /*if (buf->format.width == 640 && buf->format.height == 480) {
        if (buf->surface_lock_alloc)
            STD_FREE(buf->surface_lock_alloc);
        
        buf->surface_lock_alloc = NULL;
    }*/
    
    //SDL_UnlockSurface(buf->sdlSurface);
}

int stdDisplay_VBufferCopy(tVBuffer *vbuf, tVBuffer *vbuf2, unsigned int blit_x, int blit_y, rdRect *rect, int alpha_maybe)
{
    if (!vbuf) return 1;
    
    rdRect fallback = {0,0,vbuf2->format.width, vbuf2->format.height};
    if (!rect)
    {
        rect = &fallback;
        //memcpy(vbuf->sdlSurface->pixels, vbuf2->sdlSurface->pixels, 640*480);
        //return;
    }
    
    //if (vbuf == &Video_menuBuffer)
        //stdPlatform_Printf("Vbuffer copy to menu %u,%u %ux%u %u,%u\n", rect->x, rect->y, rect->width, rect->height, blit_x, blit_y);

    rdRect dstRect = {blit_x, blit_y, rect->width, rect->height};
    rdRect srcRect = {rect->x, rect->y, rect->width, rect->height};
    
    uint8_t* srcPixels = vbuf2 ? (uint8_t*)vbuf2->surface_lock_alloc : NULL;
    uint8_t* dstPixels = (uint8_t*)vbuf->surface_lock_alloc;
    uint32_t srcStride = vbuf2 ? vbuf2->format.rowSize : 0;
    uint32_t dstStride = vbuf->format.rowSize;

    if (!srcPixels && dstPixels) {
        int has_alpha = !(rect->width == 640) && (alpha_maybe & 1);
        for (int j = 0; j < rect->height; j++)
        {
            for (int i = 0; i < rect->width; i++)
            {
                //if ((uint32_t)(i + srcRect.x) >= (uint32_t)vbuf2->format.width) continue;
                //if ((uint32_t)(j + srcRect.y) >= (uint32_t)vbuf2->format.height) continue;

                uint8_t pixel = 0;//srcPixels[(i + srcRect.x) + ((j + srcRect.y)*srcStride)];

                if (!pixel && has_alpha) continue;
                if ((uint32_t)(i + dstRect.x) >= (uint32_t)vbuf->format.width) continue;
                if ((uint32_t)(j + dstRect.y) >= (uint32_t)vbuf->format.height) continue;

                // word-safe store (vbuffers may be word-addressable-only)
                stdPlatform_WriteByte16(&dstPixels[(i + dstRect.x) + ((j + dstRect.y)*dstStride)], pixel);
            }
        }
        return 1;
    }

    if (!srcPixels || !dstPixels) {
        //stdPlatform_Printf("Vbuffer copy missing src or dst %p %p\n", srcPixels, dstPixels);
        return 0;
    }

    int self_copy = 0;

    if (dstPixels == srcPixels)
    {
        size_t buf_len = srcStride * dstRect.width * dstRect.height;
        uint8_t* dstPixels = (uint8_t*)STD_ALLOC(buf_len);
        int has_alpha = 0;//!(rect->width == 640);

        rdRect dstRect_inter = {0, 0, rect->width, rect->height};

        for (int j = 0; j < rect->height; j++)
        {
            for (int i = 0; i < rect->width; i++)
            {
                if ((uint32_t)(i + srcRect.x) > (uint32_t)vbuf2->format.width) continue;
                if ((uint32_t)(j + srcRect.y) > (uint32_t)vbuf2->format.height) continue;
                
                uint8_t pixel = srcPixels[(i + srcRect.x) + ((j + srcRect.y)*srcStride)];

                if (!pixel && has_alpha) continue;
                if ((uint32_t)(i + dstRect_inter.x) > (uint32_t)vbuf->format.width) continue;
                if ((uint32_t)(j + dstRect_inter.y) > (uint32_t)vbuf->format.height) continue;

                dstPixels[(i + dstRect_inter.x) + ((j + dstRect_inter.y)*srcStride)] = pixel;
            }
        }
        
        

        srcPixels = dstPixels;
        srcRect.x = 0;
        srcRect.y = 0;

        self_copy = 1;
    }
    
    int once = 0;
    int has_alpha = !(rect->width == 640) && (alpha_maybe & 1);

    if (!has_alpha) {
        // no transparency skip -> contiguous rows, word-safe row copies
        for (int j = 0; j < rect->height; j++)
        {
            if ((uint32_t)(j + srcRect.y) >= (uint32_t)vbuf2->format.height) continue;
            if ((uint32_t)(j + dstRect.y) >= (uint32_t)vbuf->format.height) continue;

            int iStart = 0;
            if (iStart + srcRect.x < 0) iStart = -srcRect.x;
            if (iStart + dstRect.x < 0) iStart = -dstRect.x;
            int iEnd = rect->width;
            if (iEnd + srcRect.x > vbuf2->format.width) iEnd = vbuf2->format.width - srcRect.x;
            if (iEnd + dstRect.x > vbuf->format.width)  iEnd = vbuf->format.width - dstRect.x;
            if (iEnd <= iStart) continue;

            stdPlatform_Memcpy32(&dstPixels[(iStart + dstRect.x) + ((j + dstRect.y)*dstStride)],
                                 &srcPixels[(iStart + srcRect.x) + ((j + srcRect.y)*srcStride)],
                                 iEnd - iStart);
        }
    }
    else
    for (int j = 0; j < rect->height; j++)
    {
        for (int i = 0; i < rect->width; i++)
        {
            if ((uint32_t)(i + srcRect.x) >= (uint32_t)vbuf2->format.width) continue;
            if ((uint32_t)(j + srcRect.y) >= (uint32_t)vbuf2->format.height) continue;

            uint8_t pixel = srcPixels[(i + srcRect.x) + ((j + srcRect.y)*srcStride)];

            if (!pixel && has_alpha) continue;
            if ((uint32_t)(i + dstRect.x) >= (uint32_t)vbuf->format.width) continue;
            if ((uint32_t)(j + dstRect.y) >= (uint32_t)vbuf->format.height) continue;

            // word-safe store (vbuffers may be word-addressable-only)
            stdPlatform_WriteByte16(&dstPixels[(i + dstRect.x) + ((j + dstRect.y)*dstStride)], pixel);
        }
    }

    if (self_copy)
    {
        STD_FREE(srcPixels);
    }

    //SDL_BlitSurface(vbuf2->sdlSurface, &srcRect, vbuf->sdlSurface, &dstRect); //TODO error check
    return 1;
}

int stdDisplay_VBufferFill(tVBuffer *vbuf, int fillColor, rdRect *rect)
{
    if (!vbuf) return 1;

    rdRect fallback = {0,0,vbuf->format.width, vbuf->format.height};
    if (!rect)
    {
        rect = &fallback;
    }
    
    //if (vbuf == &Video_menuBuffer)
    //    stdPlatform_Printf("Vbuffer fill to menu %u,%u %ux%u\n", rect->x, rect->y, rect->width, rect->height);

    rdRect dstRect = {rect->x, rect->y, rect->width, rect->height};
    
    //printf("%x; %u %u %u %u\n", fillColor, rect->x, rect->y, rect->width, rect->height);
    
    uint8_t* dstPixels = (uint8_t*)vbuf->surface_lock_alloc;
    uint32_t dstStride = vbuf->format.rowSize;
    uint32_t max_idx = dstStride * vbuf->format.height;

    if (!dstPixels) {
        //stdPlatform_Printf("Vbuffer copy missing buffer %p %p\n", dstPixels);
        return 0;
    }

    for (int j = 0; j < rect->height; j++)
    {
        // row-wise word-safe fill (vbuffers may be word-addressable-only)
        for (int i = 0; i < rect->width; )
        {
            uint32_t idx = (i + dstRect.x) + ((j + dstRect.y)*dstStride);
            if (idx > max_idx) {
                i++;
                continue;
            }
            uint32_t span = rect->width - i;
            if (idx + span - 1 > max_idx) span = max_idx - idx + 1;
            stdPlatform_Memset32(&dstPixels[idx], (uint8_t)fillColor, span);
            i += span;
        }
    }

    //SDL_FillRect(vbuf, &dstRect, fillColor); //TODO error check
    return 1;
}

int stdDisplay_VBufferSetColorKey(tVBuffer *vbuf, int color)
{
    if (!vbuf) return 1;
    //DDCOLORKEY v3; // [esp+0h] [ebp-8h] BYREF

    if ( vbuf->lockRefCount )
    {
        /*if ( vbuf->lockRefCount == 1 )
        {
            v3.dwColorSpaceLowValue = color;
            v3.dwColorSpaceHighValue = color;
            vbuf->ddraw_surface->lpVtbl->SetColorKey(vbuf->ddraw_surface, 8, &v3);
            return 1;
        }*/
        vbuf->transparent_color = color;
    }
    else
    {
        vbuf->transparent_color = color;
    }
    return 1;
}

void stdDisplay_VBufferFree(tVBuffer *vbuf)
{
    // Added: Safety fallbacks
    if (!vbuf) {
        return;
    }
    stdDisplay_VBufferUnlock(vbuf);
    //SDL_FreeSurface(vbuf->sdlSurface);
    if (vbuf->surface_lock_alloc)
        STD_FREE(vbuf->surface_lock_alloc);
    vbuf->surface_lock_alloc = NULL;
    STD_FREE(vbuf);
}

void stdDisplay_ddraw_surface_flip2()
{
}

void stdDisplay_RestoreDisplayMode()
{

}

tVBuffer* stdDisplay_VBufferConvertColorFormat(void* a, tVBuffer* b)
{
    return b;
}

int stdDisplay_GammaCorrect3(int a1)
{
    jk_printf("STUB: stdDisplay_GammaCorrect3\n");
    return 1;
}

int stdDisplay_SetCooperativeLevel(uint32_t a){return 0;}
int stdDisplay_DrawAndFlipGdi(uint32_t a){return 0;}
void stdDisplay_422A50(){}
#endif