#include "vgaglout.hpp"

#include "config.h"

#ifdef benchmark_output
#include "bench.hpp"
#endif

#include "print.hpp"
#include "common.hpp"
#include "gettext.hpp"

#include "renderer.hpp"

// resize_image take to center_image, none draws at top left
#define resize_image
#undef center_image

// prevent guessing new mode(s) to fit needed resolution
#undef no_addmode

// Sorry for this hack, but this is the only way i found and it works!
int restore_mode;
#ifdef VGA_GOTOBACK
extern "C" void comefrombg(void)
{
  vga_lockvc();
  vga_setmode(restore_mode);
  vga_setpage(0);
  gl_setcontextvga(restore_mode);
  vga_unlockvc();
  
  Render::get_instance()->device->output_is_ready();
}
#endif

//----------------------------------------------------------------------
void VGAGLdev::deinit()
{
  if (initialized != -1)
  {
    vga_setmode(TEXT);
    initialized = -1;
  }
}

//----------------------------------------------------------------------
void VGAGLdev::init()
{

  if(vga_init() != 0)
  {
    DebugPrint perror(gettext("Could not initialize"),
		      Print::DEBUGGING, DebugPrint::INFO, "VGAGL");
    deinit();
    return;
  }

  initialized = 1;
//  atexit(deinit);

  int screen_height=32768, screen_width=32768;
  vga_modeinfo *info;

  for (int i = 1; i <= vga_lastmodenumber(); i++)
    if (vga_hasmode(i))
    {
        vga_modeinfo *info = vga_getmodeinfo(i);
        if ((info->bytesperpixel != 4) ||
	    (info->width < width) || (info->width > screen_width) ||
            (info->height < height) || (info->height > screen_height))
	    continue;
	screen_width = info->width;
	screen_height = info->height;
        mode = i;
        if ((info->width == width) && (info->height == height))
	    break;
    }

  if (mode < 1)
  {
    DebugPrint perror(gettext("Can't find any valid mode for ") +
		      conv::itos(screen_width) + 'x' + 
		      conv::itos(screen_height),
		      Print::DEBUGGING, DebugPrint::INFO, "VGAGL");
    deinit();
    return;
  }

#ifndef no_addmode
#define GTF_lockVF 1
#define GTF_lockHF 2
#define GTF_lockPF 3

// Guessing not full 4:3 modes needs my old patch to vgalib <= 1.9.17 to work
// So that I provide some extra precomputed timings
// Note that if your mode table is full, no mode will be added
// The patch also extend table to 200 modes (not usable, only valid ones)
// All of this depends on your hardware ...
// I sucessefuly used the best & the worth ones:
//   S3Virge TNT2-M64 GEFORCE4-MX GEFORCE4-TI
//
// Fail to guess/create does not hurt, use a closed enough mode

  if ((screen_height != height) || (screen_width != width))
  {
    DebugPrint perror("Trying to generate " + conv::itos(width) + "x" + 
		      conv::itos(height) + " video mode",
		      Print::INFO, DebugPrint::ALL, "VGAGL");

/* 0,1 only 4:3 ratio, find close mode, and up/down scale timing */
    vga_guesstiming(width, height, 0, 0);
    vga_guesstiming(width, height, 1, 0);
/* 2 Use GTF, caller provides all variable parameters. */
/*  caller provides all parameters but Vertical clock (Hz). */
    vga_guesstiming(width, height, 2, 60 | (GTF_lockVF<<16));
/* caller provides all parameters but Horizontal clock (Hz). */
    vga_guesstiming(width, height, 2, 42000 | (GTF_lockHF<<16));
/* caller provides all parameters but Pixel clock (kHz). */
    vga_guesstiming(width, height, 2, 38976 | (GTF_lockPF<<16));
/* find a 4:3 mode with y close to requested, and up/down scale timing */
    vga_guesstiming(width, height, 256,0);
    vga_guesstiming(width, height, 257,0);
/* find a 4:3 mode with x close to requested, and up/down scale timing */
    vga_guesstiming(width, height, 258,0);
    vga_guesstiming(width, height, 259,0);
    // Theoric modes (depend on hardware)
    vga_addtiming(72380,720,768,848,976,576,577,580,618,0);  // 120Hz allows stereo
    vga_addtiming(58656,720,760,840,960,576,577,580,611, 0); // 100Hz allows stereo
    vga_addtiming(38976,720,752,824,928,576,577,580,600,0);  //  70Hz
    vga_addtiming(36000,720,756,872,950,576,578,580,598,0);  //  64Hz
    vga_addtiming(26450,720,736,808,896,576,577,580,593, 0); //  50Hz
    restore_mode=vga_addmode(width,height,16777216,width*4,4);

    if (vga_hasmode(restore_mode))
    {
	screen_width = width;
	screen_height = height;
	mode = restore_mode;
    }
    else {
      DebugPrint perror(Print::INFO, DebugPrint::ALL, "VGAGL");
      perror.add_line("Adding " + conv::itos(width) + "x" + 
		      conv::itos(height) + " video mode failed");
      perror.add_line("Using" + conv::itos(screen_width) + "x" + 
		      conv::itos(screen_height) + " instead");
      perror.print();
    }
  }
#endif

  if(vga_setmode(mode) != 0)
  {
    DebugPrint perror(gettext("Failed to initialize mode ") + mode,
		      Print::DEBUGGING, DebugPrint::INFO, "VGAGL");
    deinit();
    return;
  }
  vga_setpage(0);
  gl_setcontextvga(mode);

  DebugPrint perror("Using " + conv::itos(screen_width) + "x" + 
		    conv::itos(screen_height) + "@32 video mode",
		    Print::INFO, DebugPrint::ALL, "VGAGL");

#ifndef resize_image
#ifdef center_image
  x_offset = (screen_width - width) / 2;
  y_offset = (screen_height - height) / 2;
#endif
#endif

#ifdef VGA_GOTOBACK
  restore_mode = mode;
  vga_runinbackground(vga_runinbackground_version());
  vga_runinbackground(VGA_COMEFROMBACK, &comefrombg);
#endif
}

//----------------------------------------------------------------------
VGAGLdev::VGAGLdev() : Render_device(), width(conf->p_v_res()), height(conf->p_h_res()), initialized(-1)
{
}


//----------------------------------------------------------------------
void VGAGLdev::run()
{
#ifdef benchmark_output
  Bench bench;
  Bench bench2;
#endif

  while (true)
  {

#ifdef benchmark_output
    bench.start();
    bench2.start();
#endif

    drawing = false;
    new_to_draw = false;

    output_done.signal();
    output_ready.wait();
    output_ready.reset();

    drawing = true;

#ifdef benchmark_output
    bench2.stop();
    bench2.print("scaled image for vgagldev");
    bench2.start();
#endif

    vga_lockvc();
    if (vga_oktowrite())
#ifdef resize_image
      gl_scalebox(width, height, imlib_image_get_data_for_reading_only(), vga_getxdim(), vga_getydim(), vga_getgraphmem());
#else
#ifdef center_image
      gl_putbox(x_offset, y_offset, width, height, imlib_image_get_data_for_reading_only());
#else
      gl_putbox(0, 0, width, height, imlib_image_get_data());
#endif
#endif
    vga_unlockvc();

#ifdef benchmark_output
    bench2.stop();
    bench2.print("send data to the vgagldev");

    bench.stop();
    bench.print("vgagldev complete draw");
#endif
  }

}

// What's this for?

void VGAGLdev::redraw()
{
  vga_lockvc();
  if (vga_oktowrite())
#ifdef resize_image
    gl_scalebox(width, height, imlib_image_get_data_for_reading_only(), vga_getxdim(), vga_getydim(), vga_getgraphmem());
#else
#ifdef center_image
    gl_putbox(x_offset, y_offset, width, height, imlib_image_get_data_for_reading_only());
#else
    gl_putbox(0, 0, width, height, imlib_image_get_data_for_reading_only());
#endif
#endif
  vga_unlockvc();
}
