/* gap_mov_exec.c
 * 1997.11.06 hof (Wolfgang Hofer)
 *
 * GAP ... Gimp Animation Plugins
 *
 * Move : procedures for copying source layer(s) to multiple frames
 * (varying Koordinates, opacity, size ...)
 *
 */
/* The GIMP -- an image manipulation program
 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
 *
 * 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.
 */

/* revision history:
 * version 0.93.04              hof: Window with Info Message if no Source Image was selected in MovePath
 * version 0.90.00;             hof: 1.st (pre) release 14.Dec.1997
 */

/* SYTEM (UNIX) includes */ 
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <errno.h>
#include <unistd.h>

/* GIMP includes */
#include "gtk/gtk.h"
#include "libgimp/gimp.h"

/* GAP includes */
#include "gap_layer_copy.h"
#include "gap_lib.h"
#include "gap_mov_dialog.h"
#include "gap_mov_exec.h"

extern      int gap_debug; /* ==0  ... dont print debug infos */

static int p_mov_call_render(t_mov_data *mov_ptr, t_mov_current *cur_ptr);
static void p_mov_advance_src_layer(t_mov_current *cur_ptr, int src_stepmode);
static long   p_mov_execute(t_mov_data *mov_ptr);

/* ============================================================================
 * p_mov_call_render
 *  load current frame, render and save back to disk
 * ============================================================================
 */

int p_mov_call_render(t_mov_data *mov_ptr, t_mov_current *cur_ptr)
{
  t_anim_info *ainfo_ptr;
  gint32  l_tmp_image_id;
  int     l_rc;

  l_rc = 0;
  ainfo_ptr = mov_ptr->dst_ainfo_ptr;
  

  if(ainfo_ptr->new_filename != NULL) g_free(ainfo_ptr->new_filename);
  ainfo_ptr->new_filename = p_alloc_fname(ainfo_ptr->basename,
                                      cur_ptr->dst_frame_nr,
                                      ainfo_ptr->extension);
  if(ainfo_ptr->new_filename == NULL)
     return -1;

  /* load next frame to render */
  l_tmp_image_id = p_load_image(ainfo_ptr->new_filename);
  if(l_tmp_image_id < 0)
    return -1;

  /* call render procedure for current image */
  if(0 == p_mov_render(l_tmp_image_id, mov_ptr->val_ptr, cur_ptr))
  {
    /* if OK: save the rendered frame back to disk */
    if(p_save_named_frame(l_tmp_image_id, ainfo_ptr->new_filename) < 0)
      l_rc = -1;
  }
  else l_rc = -1;
  

  /* destroy the tmp image */
  gimp_image_delete(l_tmp_image_id);

  return l_rc;

}	/* end p_mov_call_render */
 


/* ============================================================================
 * p_mov_advance_src_layer
 * advance layer index according to stepmode
 * ============================================================================
 */
void p_mov_advance_src_layer(t_mov_current *cur_ptr, int src_stepmode)
{
  static int l_ping = -1;

  if(gap_debug) printf("p_mov_advance_src_layer: stepmode=%d last_layer=%d idx=%d\n",
                       (int)src_stepmode,
                       (int)cur_ptr->src_last_layer,
                       (int)cur_ptr->src_layer_idx
                      ); 

  /* note: top layer has index 0
   *       therfore reverse loops have to count up
   */
  if((cur_ptr->src_last_layer > 0 ) && (src_stepmode != GAP_STEP_NONE))
  {
    switch(src_stepmode)
    {
      case GAP_STEP_ONCE_REV:
        cur_ptr->src_layer_idx++;
        if(cur_ptr->src_layer_idx > cur_ptr->src_last_layer)
        {
           cur_ptr->src_layer_idx = cur_ptr->src_last_layer;
        }
        break;
      case GAP_STEP_ONCE:
        cur_ptr->src_layer_idx--;
        if(cur_ptr->src_layer_idx < 0)
        {
           cur_ptr->src_layer_idx = 0;
        }
        break;
      case GAP_STEP_PING_PONG:
        cur_ptr->src_layer_idx += l_ping;
        if(l_ping < 0)
        {
          if(cur_ptr->src_layer_idx < 0)
          {
             cur_ptr->src_layer_idx = 1;
             l_ping = 1;
          }
        }
        else
        {
          if(cur_ptr->src_layer_idx > cur_ptr->src_last_layer)
          {
             cur_ptr->src_layer_idx = cur_ptr->src_last_layer - 1;
             l_ping = -1;
          }
        }

        break;
      case GAP_STEP_LOOP_REV:
        cur_ptr->src_layer_idx++;
        if(cur_ptr->src_layer_idx > cur_ptr->src_last_layer)
        {
           cur_ptr->src_layer_idx = 0;
        }
        break;
      case GAP_STEP_LOOP:
      default:
        cur_ptr->src_layer_idx--;
        if(cur_ptr->src_layer_idx < 0)
        {
           cur_ptr->src_layer_idx = cur_ptr->src_last_layer;
        }
        break;

    }
  }
}	/* end  p_advance_src_layer */

/* ============================================================================
 * p_mov_execute
 * Copy layer(s) from Sourceimage to given destination frame range,
 * varying koordinates and opacity of the copied layer.
 * To each affected destination frame exactly one is added.
 * The source layer is iterated through all layers of the sourceimage
 * according to stemmode parameter.
 * For the placement the layers act as if their size is equal to their
 * Sourceimages size.
 * ============================================================================
 */

long   p_mov_execute(t_mov_data *mov_ptr)
{
  int l_idx;
   t_mov_current l_current_data;
   t_mov_current *cur_ptr;
   t_mov_values  *val_ptr;

   gdouble  l_percentage;  
   gdouble  l_fpl;             /* frames_per_line */
   long     l_fpl2;            /* frames_per_line  rounded to integer*/
   long     l_frame_step;
   gdouble  l_frames;
   long     l_cnt;
   long     l_points;
   long     l_ptidx;
   long     l_fridx;
   gdouble  l_flt_count;
   int      l_rc;
   int      l_nlayers;

   if(mov_ptr->val_ptr->src_image_id < 0)
   {
      p_msg_win(mov_ptr->dst_ainfo_ptr->run_mode,
        "No Source Image was selected\n(Please open a 2nd Image of the same type before opening Move Path)");
      return -1;
   }

    
  l_percentage = 0.0;  
  if(mov_ptr->dst_ainfo_ptr->run_mode == RUN_INTERACTIVE)
  { 
    gimp_progress_init("Copying Layers into Frames ..");
  }

  if(gap_debug)
  {
    printf("p_mov_execute: values got from dialog:\n");
    printf("src_image_id :%ld\n", (long)mov_ptr->val_ptr->src_image_id);
    printf("src_layer_id :%ld\n", (long)mov_ptr->val_ptr->src_layer_id);
    printf("src_handle :%d\n", mov_ptr->val_ptr->src_handle);
    printf("src_stepmode :%d\n", mov_ptr->val_ptr->src_stepmode);
    printf("src_paintmode :%d\n", mov_ptr->val_ptr->src_paintmode);
    printf("clip_to_img :%d\n", mov_ptr->val_ptr->clip_to_img);
    printf("dst_range_start :%d\n", (int)mov_ptr->val_ptr->dst_range_start);
    printf("dst_range_end :%d\n", (int)mov_ptr->val_ptr->dst_range_end);
    printf("dst_layerstack :%d\n", (int)mov_ptr->val_ptr->dst_layerstack);
    for(l_idx = 0; l_idx <= mov_ptr->val_ptr->point_idx_max; l_idx++)
    {
      printf("p_x[%d] :%d\n", l_idx, mov_ptr->val_ptr->point[l_idx].p_x);
      printf("p_y[%d] : :%d\n", l_idx, mov_ptr->val_ptr->point[l_idx].p_y);
      printf("opacity[%d] :%d\n", l_idx, mov_ptr->val_ptr->point[l_idx].opacity);
      printf("w_resize[%d] :%d\n", l_idx, mov_ptr->val_ptr->point[l_idx].w_resize);
      printf("h_resize[%d] :%d\n", l_idx, mov_ptr->val_ptr->point[l_idx].h_resize);
      printf("rotation[%d] :%d\n", l_idx, mov_ptr->val_ptr->point[l_idx].rotation);
    }
    printf("\n");
  }

   l_rc    = 0;
   cur_ptr = &l_current_data;
   val_ptr = mov_ptr->val_ptr;

   /* set offsets (in cur_ptr)  according to handle mode and src_img dimension */
   p_set_handle_offsets(val_ptr, cur_ptr);
    
   
   /* test for invers range */
   if(val_ptr->dst_range_start > val_ptr->dst_range_end)
   {
      /* step down */
      l_frame_step = -1;
      l_cnt = 1 + (val_ptr->dst_range_start - val_ptr->dst_range_end);
   }
   else
   {
      l_frame_step = 1;
      l_cnt = 1 + (val_ptr->dst_range_end - val_ptr->dst_range_start);
   }

   l_frames = (gdouble)l_cnt;              /* nr. of affected frames */
   l_points = val_ptr->point_idx_max +1;   /* nr. of available points */
   
   if(l_points > l_frames)
   {
      /* cut off some points if we got more than frames */
      val_ptr->point[l_cnt].p_x = val_ptr->point[l_cnt].p_x;
      val_ptr->point[l_cnt].p_y = val_ptr->point[l_cnt].p_y;
      val_ptr->point[l_cnt].opacity = val_ptr->point[l_cnt].opacity;
      val_ptr->point[l_cnt].w_resize = val_ptr->point[l_cnt].w_resize;
      val_ptr->point[l_cnt].h_resize = val_ptr->point[l_cnt].h_resize;
      val_ptr->point[l_cnt].rotation = val_ptr->point[l_cnt].rotation;
      l_points = l_cnt;
   }
   
   
   cur_ptr->dst_frame_nr = val_ptr->dst_range_start;
   
   cur_ptr->src_layers = gimp_image_get_layers (val_ptr->src_image_id, &l_nlayers);
   if(cur_ptr->src_layers == NULL)
   {
     printf("ERROR (in p_mov_execute): Got no layers from SrcImage\n");
     return -1;
   }
   if(l_nlayers < 1)
   {
     printf("ERROR (in p_mov_execute): Source Image has no layers\n");
     return -1;
   }
   cur_ptr->src_last_layer = l_nlayers -1;

   /* findout index of src_layer_id */
   for(cur_ptr->src_layer_idx = 0; 
       cur_ptr->src_layer_idx  < l_nlayers;
       cur_ptr->src_layer_idx++)
   {
      if(cur_ptr->src_layers[cur_ptr->src_layer_idx] == val_ptr->src_layer_id)
         break;
   }
   cur_ptr->src_last_layer = l_nlayers -1;   /* index of last layer */
   
   cur_ptr->currX   = (gdouble)val_ptr->point[0].p_x;
   cur_ptr->currY   = (gdouble)val_ptr->point[0].p_y;
   cur_ptr->currOpacity  = (gdouble)val_ptr->point[0].opacity;
   cur_ptr->currWidth    = (gdouble)val_ptr->point[0].w_resize;
   cur_ptr->currHeight   = (gdouble)val_ptr->point[0].h_resize;
   cur_ptr->currRotation = (gdouble)val_ptr->point[0].rotation;

   /* RENDER add current src_layer to current frame */
   l_rc = p_mov_call_render(mov_ptr, cur_ptr);


   /* how many frames are affected from one line of the moving path */   
   l_fpl = ((gdouble)l_frames - 1.0) / ((gdouble)(l_points -1));
   l_fpl2 = (l_fpl + 0.5);
   
   l_ptidx = 1;
   l_flt_count =  0.0;
   
   
  /* loop for each frame within the range (may step up or down) */
  cur_ptr->dst_frame_nr = val_ptr->dst_range_start;
  for(l_fridx = 1; l_fridx < l_cnt; l_fridx++)
  {
     
     if(gap_debug) printf("p_mov_execute: l_fridx=%ld, l_flt_count=%f, l_rc=%d\n",
                          l_fridx, l_flt_count, (int)l_rc);

     if(l_rc != 0) break;

      /* advance frame_nr, (1st frame was done outside this loop) */
      cur_ptr->dst_frame_nr += l_frame_step;  /* +1  or -1 */

      if((gdouble)l_fridx > l_flt_count)
      {
         /* re-adjust current values. (only to avoid rounding errors)
          * current values should already contain the point values[l_ptidx]
          */
         cur_ptr->currX   = (gdouble)val_ptr->point[l_ptidx -1].p_x;
         cur_ptr->currY   = (gdouble)val_ptr->point[l_ptidx -1].p_y;
         cur_ptr->currOpacity  = (gdouble)val_ptr->point[l_ptidx -1].opacity;
         cur_ptr->currWidth  = (gdouble)val_ptr->point[l_ptidx -1].w_resize;
         cur_ptr->currHeight  = (gdouble)val_ptr->point[l_ptidx -1].h_resize;
         cur_ptr->currRotation  = (gdouble)val_ptr->point[l_ptidx -1].rotation;
         
         /* change deltas for next line of the move path */
         cur_ptr->deltaX  = ((gdouble)val_ptr->point[l_ptidx].p_x  - (gdouble)val_ptr->point[l_ptidx -1].p_x) / (gdouble)l_fpl2;
         cur_ptr->deltaY  = ((gdouble)val_ptr->point[l_ptidx].p_y  - (gdouble)val_ptr->point[l_ptidx -1].p_y) / (gdouble)l_fpl2;
         cur_ptr->deltaOpacity = ((gdouble)val_ptr->point[l_ptidx].opacity  - (gdouble)val_ptr->point[l_ptidx -1].opacity) / (gdouble)l_fpl2;
         cur_ptr->deltaWidth = ((gdouble)val_ptr->point[l_ptidx].w_resize  - (gdouble)val_ptr->point[l_ptidx -1].w_resize) / (gdouble)l_fpl2;
         cur_ptr->deltaHeight = ((gdouble)val_ptr->point[l_ptidx].h_resize  - (gdouble)val_ptr->point[l_ptidx -1].h_resize) / (gdouble)l_fpl2;
         cur_ptr->deltaRotation = ((gdouble)val_ptr->point[l_ptidx].rotation  - (gdouble)val_ptr->point[l_ptidx -1].rotation) / (gdouble)l_fpl2;

         l_ptidx++;
         l_flt_count += l_fpl;
      }

       /* advance settings for next frame */
       p_mov_advance_src_layer(cur_ptr, val_ptr->src_stepmode);

       cur_ptr->currX        += cur_ptr->deltaX;
       cur_ptr->currY        += cur_ptr->deltaY;
       cur_ptr->currOpacity  += cur_ptr->deltaOpacity;
       cur_ptr->currWidth    += cur_ptr->deltaWidth;
       cur_ptr->currHeight   += cur_ptr->deltaHeight;
       cur_ptr->currRotation += cur_ptr->deltaRotation;

       /* RENDER add current src_layer to current frame */
       l_rc = p_mov_call_render(mov_ptr, cur_ptr);

       /* show progress */
       if(mov_ptr->dst_ainfo_ptr->run_mode == RUN_INTERACTIVE)
       { 
         l_percentage = (gdouble)l_fridx / (gdouble)(l_cnt -1);
         gimp_progress_update (l_percentage);
       }
           
   }

   if(cur_ptr->src_layers != NULL) g_free(cur_ptr->src_layers);
   
   return l_rc;  

}	/* end p_mov_execute */



/* ============================================================================
 * gap_move
 * ============================================================================
 */
int gap_move(GRunModeType run_mode, gint32 image_id)
{
  int l_rc;
  t_anim_info *ainfo_ptr;
  t_mov_data  l_mov_data;

  l_rc = -1;
  ainfo_ptr = p_alloc_ainfo(image_id, run_mode);
  if(ainfo_ptr != NULL)
  {
    l_mov_data.val_ptr = g_malloc(sizeof(t_mov_values));
    if(NULL != l_mov_data.val_ptr)
    {
      if (0 == p_dir_ainfo(ainfo_ptr))
      {
        if(0 != p_chk_framerange(ainfo_ptr))   return -1;

        l_mov_data.dst_ainfo_ptr = ainfo_ptr;
        if(run_mode == RUN_INTERACTIVE)
        {
           l_rc = p_move_dialog (&l_mov_data);
           if(0 != p_chk_framechange(ainfo_ptr))
           {
              l_rc = -1;
           }
        }
        else
        {
           l_rc = -1;  /* run NON_INTERACTIVE not implemented (define args) */
        }

        if(l_rc >= 0)
        {
           l_rc = p_save_named_frame(ainfo_ptr->image_id, ainfo_ptr->old_filename);
           if(l_rc >= 0)
           {
              l_rc = p_mov_execute(&l_mov_data);
           
              /* go back to the frame_nr where move operation was started from */
              p_load_named_frame(ainfo_ptr->image_id, ainfo_ptr->old_filename);
           }
        }
      }
      g_free(l_mov_data.val_ptr);
    }

    p_free_ainfo(&ainfo_ptr);
  }
  
  return(l_rc);    
}	/* end gap_move */

