/**
 * SACD Ripper - https://github.com/sacd-ripper/
 *
 * Copyright (c) 2010-2015 by respective authors.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <malloc.h>

#include "rsxutil.h"

#define GCM_LABEL_INDEX    255

videoResolution res;
gcmContextData  *context = NULL;

u32             curr_fb  = 0;
u32             first_fb = 1;

u32             display_width;
u32             display_height;

u32             depth_pitch;
u32             depth_offset;
u32             *depth_buffer;

u32             color_pitch;
u32             color_offset[2];
u32             *color_buffer[2];

static u32      sLabelVal = 1;

static void wait_finish()
{
    rsxSetWriteBackendLabel(context, GCM_LABEL_INDEX, sLabelVal);

    rsxFlushBuffer(context);

    while (*(vu32 *) gcmGetLabelAddress(GCM_LABEL_INDEX) != sLabelVal)
        usleep(30);

    ++sLabelVal;
}

static void wait_rsx_idle()
{
    rsxSetWriteBackendLabel(context, GCM_LABEL_INDEX, sLabelVal);
    rsxSetWaitLabel(context, GCM_LABEL_INDEX, sLabelVal);

    ++sLabelVal;

    wait_finish();
}

void set_render_target(u32 index)
{
    gcmSurface sf;

    sf.colorFormat      = GCM_TF_COLOR_X8R8G8B8;
    sf.colorTarget      = GCM_TF_TARGET_0;
    sf.colorLocation[0] = GCM_LOCATION_RSX;
    sf.colorOffset[0]   = color_offset[index];
    sf.colorPitch[0]    = color_pitch;

    sf.colorLocation[1] = GCM_LOCATION_RSX;
    sf.colorLocation[2] = GCM_LOCATION_RSX;
    sf.colorLocation[3] = GCM_LOCATION_RSX;
    sf.colorOffset[1]   = 0;
    sf.colorOffset[2]   = 0;
    sf.colorOffset[3]   = 0;
    sf.colorPitch[1]    = 64;
    sf.colorPitch[2]    = 64;
    sf.colorPitch[3]    = 64;

    sf.depthFormat   = GCM_TF_ZETA_Z16;
    sf.depthLocation = GCM_LOCATION_RSX;
    sf.depthOffset   = depth_offset;
    sf.depthPitch    = depth_pitch;

    sf.type      = GCM_TF_TYPE_LINEAR;
    sf.antiAlias = GCM_TF_CENTER_1;

    sf.width  = display_width;
    sf.height = display_height;
    sf.x      = 0;
    sf.y      = 0;

    rsxSetSurface(context, &sf);
}

void init_screen(void *host_addr, u32 size)
{
    context = rsxInit(CB_SIZE, size, host_addr);

    videoState state;
    videoGetState(0, 0, &state);

    videoGetResolution(state.displayMode.resolution, &res);

    videoConfiguration vconfig;
    memset(&vconfig, 0, sizeof(videoConfiguration));

    vconfig.resolution = state.displayMode.resolution;
    vconfig.format     = VIDEO_BUFFER_FORMAT_XRGB;
    vconfig.pitch      = res.width * sizeof(u32);

    wait_rsx_idle();

    videoConfigure(0, &vconfig, NULL, 0);
    videoGetState(0, 0, &state);

    gcmSetFlipMode(GCM_FLIP_VSYNC);

    display_width  = res.width;
    display_height = res.height;

    color_pitch     = display_width * sizeof(u32);
    color_buffer[0] = (u32 *) rsxMemalign(64, (display_height * color_pitch));
    color_buffer[1] = (u32 *) rsxMemalign(64, (display_height * color_pitch));

    rsxAddressToOffset(color_buffer[0], &color_offset[0]);
    rsxAddressToOffset(color_buffer[1], &color_offset[1]);

    gcmSetDisplayBuffer(0, color_offset[0], color_pitch, display_width, display_height);
    gcmSetDisplayBuffer(1, color_offset[1], color_pitch, display_width, display_height);

    depth_pitch  = display_width * sizeof(u32);
    depth_buffer = (u32 *) rsxMemalign(64, (display_height * depth_pitch) * 2);
    rsxAddressToOffset(depth_buffer, &depth_offset);
}

void waitflip()
{
    while (gcmGetFlipStatus() != 0)
        usleep(200);
    gcmResetFlipStatus();
}

void flip()
{
    if (!first_fb)
        waitflip();
    else
        gcmResetFlipStatus();

    gcmSetFlip(context, curr_fb);
    rsxFlushBuffer(context);

    gcmSetWaitFlip(context);

    curr_fb ^= 1;
    set_render_target(curr_fb);

    first_fb = 0;
}
