/***********************************************************************/
/*                                                                     */
/* Project      : GRAV                                                 */
/* Current Rel. : 3.1                                                  */
/* Creator      : Michael Knigge                                       */
/* Creation Date: 01/30/95                                             */
/* Description  : viewing files                                        */
/*                                                                     */
/* Functions    : grav_view_file()                                     */
/*                grav_view_single_file()                              */
/*                grav_view_on_screen()                                */
/*                grav_display()                                       */
/*                grav_get_bitmap_width()                              */
/*                grav_set_palette()                                   */
/*                grav_set_blank_palette()                             */
/*                grav_verify_mode()                                   */
/*                grav_new_mode()                                      */
/*                grav_get_screen_width()                              */
/*                grav_get_scroll_amount()                             */
/*                                                                     */
/***********************************************************************/

#include <stdio.h>
#include <ncurses.h>
#include <vga.h>
#include <vgamouse.h>
#include <vgagl.h>
#include "standard.h"
#include "gbm.h"
#include "defines.h"
#include "keys.h"
#include "error.h"
#include "view.h"
#include "read.h"
#include "trans.h"
#include "effects.h"


int grav_view_file(int use_color, int *count_tagged, DIRTAB dirtab[], int mode_index, GMODE gmode[])
{
   int    local;
   int    rc;
   int    error_id;
   int    colors;
   char   *bitmap;
   char   *new_bitmap;
   
   GBMRGB gbmrgb[256];
   GBMFT  gbmft;
   GBM    gbm;

   /*
    * here we will do a loop over the entire dirtab[] table searching
    * for tagged entries. if a tagged entry was found, we look for the
    * filetype and read the data from the file. the function for reading
    * will always return a pointer to a bitmap....
    */

   for(local=0; local < MAX_DIRTAB; local++)
   {
      if (dirtab[local].tagged == TRUE)
      {
         grav_reading_msg(use_color); 

/******************************************************************/
/*****************  vga_runinbackground(TRUE); ********************/
/******************************************************************/

         if (gmode[mode_index].colors <= 256)
            gbm.bpp = 8;
         else
            gbm.bpp = 24;
         
         bitmap = grav_read_file(&error_id,
                                 READ_ENTIRE_FILE,
                                 local,
                                 dirtab,
                                 &gbm,
                                 &gbmft,
                                 gbmrgb);
                                    

/***********************************************************************/
/******* vga_runinbackground(FALSE); ***********************************/
/***********************************************************************/
         
         if (error_id != GRAV_NO_ERROR)
            return(error_id);


         /*
          * check if we have to transform the bitmap to another
          * "bit-per-pixel-format". if so, do it and return the 
          * address of the new bitmap...
          */

         colors     = ( (1 << gbm.bpp) & 0x1ff );          
         new_bitmap = grav_transform(&error_id, bitmap, &gbm, gbmrgb, mode_index, gmode);
         
         /*
          * check if we got a new bitmap...
          */
          
         if (new_bitmap != NULL)
         {
            free(bitmap);
            bitmap = new_bitmap;
         }
         
         if (error_id != GRAV_NO_ERROR)
         {
            free(bitmap);
            return(error_id);
         }
            
         /*
          * if everything is all right, view the graphic on the screen
          */
          
         rc = grav_view_on_screen(bitmap, &gbm, gbmrgb, mode_index, gmode, colors);
         
         /*
          * remove the tagged-flag (if needed)
          */
         
         if (dirtab[local].tagged == TRUE)
         {
            dirtab[local].tagged  = FALSE;
            *count_tagged         = *count_tagged - 1;
         }
         
         /*
          * if all tagged files were processed, break-out!
          */
         if (*count_tagged == 0 || rc == VIEW_ABORT)
            break;
      }

      /*
       * we need to call this here because (if we use colors) want the
       * "process-window" become colred (colors are gone because we
       * called endwin() 
       */
      grav_init_colors();
   }

  /*
   * we have to init the colors again, because we had to call
   * endwin() before switching to VGA-Mode.... (if all tagged
   * graphics are shown, we "breaked out" of the for() above and
   * grav_init_colors() wasn't called within for().....
   */
   grav_init_colors();

   return(GRAV_NO_ERROR);
}

int grav_view_single_file(char *filename, int mode_index, GMODE gmode[])
{

   int     type;
   int     error_id;
   int     colors;
   
   char    *new_bitmap;
   char    *bitmap;
   
   DIRTAB  single[1];
   
   GBM     gbm;
   GBMFT   gbmft;
   GBMRGB  gbmrgb[256];


   /*
    * can we view that file?
    */
   if ((grav_guess_filetype(filename, &type)) == FALSE)
      return(GRAV_ERROR_TYPE);

   /*
    * if we got here, everything is all right!
    */

   single[0].filetype = IS_FILE;
   single[0].gfx_type = type;
   single[0].tagged   = TRUE;
   strcpy(single[0].filename, filename);

   
   /*
    * set bpp format
    */
   if (gmode[mode_index].colors <= 256)
      gbm.bpp = 8;
   else
      gbm.bpp = 24;
         
   /*
    * and read the file
    */
   bitmap = grav_read_file(&error_id,
                           READ_ENTIRE_FILE,
                           0,
                           single,
                           &gbm,
                           &gbmft,
                           gbmrgb);

                                    
   if(error_id != GRAV_NO_ERROR)
      return(error_id);

   /*
    * check if we have to transform the bitmap to another
    * "bit-per-pixel-format". if so, do it and return the 
    * address of the new bitmap...
    */

   colors     = ( (1 << gbm.bpp) & 0x1ff );           
   new_bitmap = grav_transform(&error_id, bitmap, &gbm, gbmrgb, mode_index, gmode);
         
   /*
    * check if we got a new bitmap...
    */
          
   if (new_bitmap != NULL)
   {
      free(bitmap);
      bitmap = new_bitmap;
   }
         
   if (error_id != GRAV_NO_ERROR)
   {
      free(bitmap);
      return(error_id);
   }
            
   /*
    * if everything is all right, view the graphic on the screen
    */
    
   grav_view_on_screen(bitmap, &gbm, gbmrgb, mode_index, gmode, colors);

   return(GRAV_NO_ERROR);
}


int grav_view_on_screen(char *bitmap, GBM *gbm, GBMRGB gbmrgb[], int mode_index, GMODE gmode[], int colors)
{
   int  local;
   int  rc;
   int  top;
   int  left;
   int  lines;
   int  from_top;
   int  from_left;
   int  saved_from_top;
   int  saved_from_left;
   int  skip_pixels;
   int  vga_mode;
   int  bitmap_width;
   int  screen_width;
   int  max_lines;
   int  max_cols;
   int  loop_again;
   int  need_redraw;
   int  grey;
   int  key;

   int  step_x;
   int  step_y;

   int  mouse_x_pos;
   int  mouse_y_pos;
   int  mouse_x_saved;
   int  mouse_y_saved;

   char *new_bitmap;
   char *work_bitmap;
   char *start_bitmap;

   GBM save_gbm;

   /*
    * check, if we can use the current video-mode or if we have to
    * fall back to a 256-color mode (perhaps if we have a 8 bit
    * picture and the current video-mode have more than 256 colors....
    */

    vga_mode = grav_verify_mode(mode_index, gmode, gbm);

   /*
    * first, we have to "switch off" ncurses....
    */

   endwin();
   fprintf(stderr,"\x1b[H\x1b[J");
      
   /*
    * switch to vga-mode, clear the screen and "power off" the monitor...
    */

   vga_setmousesupport(1); 
   vga_setmode(gmode[vga_mode].index);
   vga_setdisplaystart(0);
   vga_clear();
   vga_screenoff();
                   
   /*
    * set a "blank" (black only) palette....
    */
   grav_set_blank_palette(gbm); 
   
   /*
    * set default values for skipped lines / pixels and
    * for the top / left border....
    */

   if (gbm->h > gmode[vga_mode].height)
      lines = gmode[vga_mode].height;
   else
      lines = gbm->h;
    
   loop_again      = TRUE;
   need_redraw     = FALSE;
   from_top        = 0;
   from_left       = 0;
   saved_from_left = 0;
   saved_from_top  = 0;
   top             = 0;
   left            = 0;
   grey            = FALSE;

   save_gbm.h      = 0;
   save_gbm.w      = 0;
   save_gbm.bpp    = 0;

   bitmap_width    = grav_get_bitmap_width(gbm);
   
   /*
    * compute the maximum scroll amount....
    */
   grav_get_scroll_amount(&top, &left, &max_lines, &max_cols, vga_mode, gmode, gbm);

   /*
    * calculate the "start-point" within the bitmap. this point is at the
    * end of the allocated space, because bitmaps are stored botton line
    * up.....
    */
    
   work_bitmap  = bitmap + (gbm->h * bitmap_width);
   work_bitmap  = work_bitmap - ((from_top + 1) * bitmap_width) + from_left;
   start_bitmap = work_bitmap;
   
   /*
    * what screen-width will we use?
    */
   screen_width = grav_get_screen_width(vga_mode, gmode, gbm);
   
   /*
    * display the bitmap.....
    */
    
   grav_display(work_bitmap, top, left, bitmap_width, screen_width, lines);

   /*
    * and switch on the screen....
    */
    
   vga_screenon();
   
   
   /*
    * set palette...
    */
   grav_fade_in(gbm, gbmrgb);
   grav_set_palette(gbm, gbmrgb);
            
   /*
    * set up the mouse
    */
#ifdef USE_MOUSE
   mouse_setxrange(0, max_cols);
   mouse_setyrange(0, max_lines);
   mouse_setposition(0, 0);
   mouse_x_saved = 0;
   mouse_y_saved = 0;
#endif

   /*
    * calculate steps for scrolling
    */
   step_x      = gmode[vga_mode].width   / 10 / 2;
   step_y      = gmode[vga_mode].height  / 10;
            
   do
   {
      do
      {
         key = vga_getkey();
         if (key == 0x1B)
         {
            key = vga_getkey();    /* will be 0x5B */
            key = vga_getkey();
            switch(key)
            {
               case 0x41: key = KEY_UP;     break;
               case 0x42: key = KEY_DOWN;   break;
               case 0x44: key = KEY_LEFT;   break;
               case 0x43: key = KEY_RIGHT;  break;
            }
         }

#ifdef USE_MOUSE
               
         if (key == 0)
         {
            mouse_update();
            
            mouse_x_pos = mouse_getx();
            mouse_y_pos = mouse_gety();
            
            if (mouse_x_pos != mouse_x_saved ||
                mouse_y_pos != mouse_y_saved)
            {
               from_left     = mouse_x_pos;
               from_top      = mouse_y_pos;
               mouse_x_saved = mouse_x_pos;
               mouse_y_saved = mouse_y_pos;
               key           = MOUSE_MOVED;
            }   
         }

#endif         
         
      }
      while(key == 0); 
      
      switch(key)
      {
      case 43:        if ((grav_new_mode(+1, &vga_mode, gmode)) == TRUE)
                      {
                         if (gbm->h > gmode[vga_mode].height)
                            lines = gmode[vga_mode].height;
                         else
                            lines = gbm->h;      
                         grav_get_scroll_amount(&top, &left, &max_lines, &max_cols, vga_mode, gmode, gbm);
                         work_bitmap  = start_bitmap;
                         screen_width = grav_get_screen_width(vga_mode, gmode, gbm);
                         step_x       = gmode[vga_mode].width   / 10 / 2;
                         step_y       = gmode[vga_mode].height  / 10;
                         from_top     = 0;
                         from_left    = 0;
                         need_redraw  = TRUE;
                         vga_setmode(gmode[vga_mode].index);
                         mouse_setxrange(0, max_cols);
                         mouse_setyrange(0, max_lines);
                         mouse_setposition(0, 0);
                         grav_set_palette(gbm, gbmrgb);

                      }
                      break;

      case 45:        if ((grav_new_mode(-1, &vga_mode, gmode)) == TRUE)
                      {
                         if (gbm->h > gmode[vga_mode].height)
                            lines = gmode[vga_mode].height;
                         else
                            lines = gbm->h;      
                         grav_get_scroll_amount(&top, &left, &max_lines, &max_cols, vga_mode, gmode, gbm);
                         work_bitmap  = start_bitmap;
                         screen_width = grav_get_screen_width(vga_mode, gmode, gbm);
                         step_x       = gmode[vga_mode].width   / 10 / 2;
                         step_y       = gmode[vga_mode].height  / 10;
                         from_top     = 0;
                         from_left    = 0;
                         need_redraw  = TRUE;
                         mouse_setxrange(0, max_cols);
                         mouse_setyrange(0, max_lines);
                         mouse_setposition(0, 0);
                         vga_setmode(gmode[vga_mode].index);
                         grav_set_palette(gbm, gbmrgb);
                      }
                      break;

      case  KEY_S:
      case  KEY_s:    if (save_gbm.bpp == 0)
                      {
                         save_gbm.bpp = gbm->bpp;
                         save_gbm.w   = gbm->w;
                         save_gbm.h   = gbm->h;
                         new_bitmap = grav_scale(&rc,
                                                 bitmap, 
                                                 gbm, 
                                                 gmode[vga_mode].width,
                                                 gmode[vga_mode].height);
                      }
                      else
                      {
                         new_bitmap = grav_scale(&rc,
                                                 bitmap, 
                                                 gbm, 
                                                 save_gbm.w,
                                                 save_gbm.h);

                         gbm->bpp   = save_gbm.bpp;  save_gbm.bpp = 0;
                         gbm->w     = save_gbm.w;    save_gbm.w   = 0;
                         gbm->h     = save_gbm.h;    save_gbm.h   = 0;                                                                       
                      }                           
                                              
                      if (rc == GRAV_NO_ERROR)
                      {
                         free(bitmap);
                         grav_get_scroll_amount(&top, &left, &max_lines, &max_cols, vga_mode, gmode, gbm);

                         bitmap_width = grav_get_bitmap_width(gbm);
                         screen_width = grav_get_screen_width(vga_mode, gmode, gbm);                         

                         bitmap       = new_bitmap;
                         from_top     = 0;
                         from_left    = 0;
                         need_redraw  = TRUE;

                         if (gbm->h > gmode[vga_mode].height)
                            lines = gmode[vga_mode].height;
                         else
                            lines = gbm->h;
                      }
                      else
                      {
                         loop_again = FALSE;
                         rc         = VIEW_ABORT;
                      }
                      break;

      case  70:
      case 102:       grav_flip_image(gbm, bitmap);
                      need_redraw = TRUE;
                      break;
                      
      case KEY_M:
      case KEY_m:     grav_mirror_image(gbm, bitmap);
                      need_redraw = TRUE;
                      break;
      
      case KEY_I:
      case KEY_i:     grav_invert(bitmap, gbm, gbmrgb);
                      grav_set_palette(gbm, gbmrgb);
                      need_redraw = TRUE;
                      break;

      case KEY_P:
      case KEY_p:     grav_rotate_palette(gbm, gbmrgb, colors);
                      grav_set_palette(gbm, gbmrgb);
                      break;
                      
      case KEY_G:
      case KEY_g:     if (grey == FALSE)
                      {
                         need_redraw = TRUE;
                         grey        = TRUE;
                         bitmap      = grav_to_grey(bitmap, gbm, gbmrgb);
                         grav_set_palette(gbm, gbmrgb);
                      }
                      break;

      case KEY_HOME:  from_top  = 0;
                      from_left = 0;
                      break;
                      
      case KEY_END:  from_top  = max_lines;
                     from_left = max_cols;
                     break;
                      
      case KEY_PPAGE: from_top = from_top - gmode[vga_mode].height / 2;
                      if (from_top < 0)
                         from_top = 0;
                      break;

      case KEY_NPAGE: from_top = from_top + gmode[vga_mode].height / 2;
                      if (from_top > max_lines)
                         from_top = max_lines;
                      break;

      case KEY_UP:    
      case    107:                 
      case     75:    from_top = from_top - step_y;
                      if (from_top < 0)
                         from_top = 0;
                      break;
      
      case KEY_DOWN:  
      case      106:
      case       74:  from_top = from_top + step_y;
                      if (from_top > max_lines)
                         from_top = max_lines;
                      break;
      
      case KEY_LEFT:  
      case      104:
      case       72:  from_left = from_left - step_x;
                      if (from_left < 0)
                         from_left = 0;
                      break;
      
      case KEY_RIGHT:
      case       108:
      case        76: from_left = from_left + step_x;
                      if (from_left > max_cols)
                         from_left = max_cols;
                      break;
      
      case KEY_SPACE: rc         = VIEW_NEXT;
                      loop_again = FALSE;
                      break;

      case KEY_F(10):
      case KEY_ENTER:
      case KEY_q:
      case KEY_Q:     loop_again = FALSE;
                      rc         = VIEW_ABORT;
                      break;
      }

      /*
       * check, if we _REALLY_ need to redraw the screen....
       */
       
      if (saved_from_top != from_top)
      {
         saved_from_top = from_top;
         need_redraw    = TRUE;
      }

      if (saved_from_left != from_left)
      {
         saved_from_left = from_left;
         need_redraw     = TRUE;
      }
      
      if (need_redraw == TRUE)
      {
         switch(gbm->bpp)
         {
         case 24: if (gmode[vga_mode].colors == 16777216)
                     skip_pixels = from_left * 3;
                  else
                     skip_pixels = from_left * 2;
                  break;
                  
         case 8:  skip_pixels = from_left;              break;
         case 4:  skip_pixels = (from_left + 1) / 2;    break;
         case 1:  skip_pixels = (from_left + 7) / 8;    break;
         }
         
         work_bitmap = bitmap + (gbm->h * bitmap_width);
         work_bitmap = work_bitmap - ((from_top + 1) * bitmap_width) + skip_pixels;
         grav_display(work_bitmap, top, left, bitmap_width, screen_width, lines);

#ifdef USE_MOUSE
         mouse_setposition(from_left, from_top);
         mouse_x_saved = from_left;
         mouse_y_saved = from_top;
#endif         

         need_redraw = FALSE;
      }
   }
   while(loop_again == TRUE);
   
   grav_fade_out(gbm, gbmrgb);

   /*
    * restore Text-Mode and initialize ncurses again....
    */
   vga_setmousesupport(0);
   vga_setmode(TEXT);
   grav_init_ncurses();
   
   /*
    * free memory
    */
   free(bitmap);
   
   return(rc);
}

int grav_display(char *work_bitmap, int top, int left, int bitmap_width, int screen_width, int lines)
{
   int  local;

   for(local=1;local<lines;local++)
   {
      vga_drawscansegment(work_bitmap,
                          0 + left,
                          local + top,
                          screen_width);
                          
      work_bitmap = work_bitmap - bitmap_width;
   }


   return(TRUE);
}


int grav_get_bitmap_width(GBM *gbm)
{
   switch (gbm->bpp)
   {            
      case 24: return((((gbm->w * 3) + 3) / 4) * 4);
               break;
                                                      
      case 8:  return(((gbm->w + 3) / 4) * 4);
               break;
                       
      case 4:  return((((gbm->w / 2) + 3) / 4) * 4);
               break;
               
      case 1:  return(((((gbm->w + 7) / 8) + 3) / 4) * 4);
               break;
  }
}

int  grav_set_palette(GBM *gbm, GBMRGB gbmrgb[])
{
   int local;
   int colors;

   /*
    * if the graphic is _NOT_ a 24bit picture, set the palette. we have
    * to devide each entry trough 4, because GBM returns a 24bit palette
    * in GBMRGB as default......
    */
   
   colors = ( (1 << gbm->bpp) & 0x1ff );

   for (local=0;local<colors;local++)
   {
      vga_setpalette(local,
                     gbmrgb[local].r / 4,
                     gbmrgb[local].g / 4,
                     gbmrgb[local].b / 4);
   }   

   return(TRUE);
}

int grav_verify_mode(int mode_index, GMODE gmode[], GBM *gbm)
{
   int local;
   int vga_mode;
   
   /*
    * save current mode....
    */
   vga_mode = mode_index;

   /*
    * if we got a 24Bit picture we will fall back to a mode using
    * the same resolution, but supports only 256 colors...
    */
    
   if (gbm->bpp != 24 && gmode[mode_index].colors > 256)
   {
      for (local=0; local<=MAX_MODES; local++)
      {
         if (gmode[local].colors  == 256 &&
             gmode[local].width   == gmode[mode_index].width &&
             gmode[local].height  == gmode[mode_index].height)
            return(local);
      }
   }
   
   return(vga_mode);
}

int grav_new_mode(int skip, int *mode_index, GMODE gmode[])
{
   int work_mode;
   
   work_mode = *mode_index;
   
   do
   {
      work_mode = work_mode + skip;
      
      if (work_mode > MAX_MODES || 
          work_mode < 0         ||
          gmode[work_mode].colors != gmode[*mode_index].colors)
         return(FALSE);
      
      if (gmode[*mode_index].colors == gmode[work_mode].colors)
      {
         if (gmode[work_mode].supported == TRUE)
         {
            *mode_index = work_mode;
            return(TRUE);
         }
      }
   }
   while (TRUE);
}

int grav_get_screen_width(int mode_index, GMODE gmode[], GBM *gbm)
{
   int screen_width;
   

   if (gmode[mode_index].width < gbm->w)
      screen_width = gmode[mode_index].width;
   else
      screen_width = gbm->w;

   switch(gbm->bpp)
   {
   case 24: if (gmode[mode_index].colors == 16777216)
               screen_width = screen_width * 3;
            else
               screen_width = screen_width * 2;
            break;
   
   case  4: screen_width = screen_width / 2;     break;
   }

   return(screen_width);
}

int grav_get_scroll_amount(int *top, int *left, int *max_lines, int *max_cols, int mode_index, GMODE gmode[], GBM *gbm)
{

   if (gbm->h > gmode[mode_index].height)
      *max_lines = gbm->h - gmode[mode_index].height;
   else
   {
      *max_lines  = 0;
      *top        = (gmode[mode_index].height - gbm ->h) / 2;
   }
      
   if (gbm->w > gmode[mode_index].width)   
      *max_cols  = gbm->w - gmode[mode_index].width;
   else
   {
      *max_cols  = 0;
      *left      = (gmode[mode_index].width - gbm ->w) / 2;      
   }
}

int grav_set_blank_palette(GBM *gbm)
{
   int local;
   
   if (gbm->bpp > 8)
      return(FALSE);
      
   for(local=0; local<256; local++)
      vga_setpalette(local, 0, 0, 0);

   return(TRUE);
}

