/* vim:expandtab:ts=2 sw=2:
*/
/*  Grafx2 - The Ultimate 256-color bitmap paint program
    Copyright 2007 Adrien Destugues
    Copyright 2009 Franck Charlet
    Copyright 1996-2001 Sunset Design (Guillaume Dorme & Karl Maritaud)
    Grafx2 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; version 2
    of the License.
    Grafx2 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 Grafx2; if not, see 
*/
//////////////////////////////////////////////////////////////////////////////
///@file brush_ops.c
/// Code for operations about the brush (grabbing, rotating, ...) and magnifier
//////////////////////////////////////////////////////////////////////////////
#include 
#include "brush.h"
#include "buttons.h"
#include "engine.h"
#include "global.h"
#include "graph.h"
#include "misc.h"
#include "operatio.h"
#include "pages.h"
#include "windows.h"
/// Simulates clicking the "Draw" button.
void Return_to_draw_mode(void)
{
  // Comme l'enclenchement du bouton efface le curseur, il faut l'afficher au
  // préalable:
  Display_cursor();
  if (Mouse_K)
    Wait_end_of_click();
  // !!! Efface la croix puis affiche le viseur !!!
  Select_button(BUTTON_DRAW,LEFT_SIDE); // Désenclenche au passage le bouton brosse
  if (Config.Auto_discontinuous)
  {
    // On se place en mode Dessin discontinu à la main
    while (Current_operation!=OPERATION_DISCONTINUOUS_DRAW)
      Select_button(BUTTON_DRAW,RIGHT_SIDE);
  }
  // Maintenant, il faut réeffacer le curseur parce qu'il sera raffiché en fin
  // d'appel à cette action:
  Hide_cursor();
  // On passe en brosse couleur:
  Change_paintbrush_shape(PAINTBRUSH_SHAPE_COLOR_BRUSH);
  if ((Config.Coords_rel) && (Menu_is_visible))
    Print_in_menu("X:       Y:",0);
  Print_coordinates();
}
// ---------------------------------------------------------- OPERATION_MAGNIFY
void Magnifier_12_0(void)
// Opération   : 4      (item d'une Loupe)
// Click Souris: 1 ou 2
// Taille_Pile : 0
//
// Souris effacée: Oui
{
  // On passe en mode loupe
  Main_magnifier_mode=1;
  // La fonction d'affichage dans la partie image est désormais un affichage
  // spécial loupe.
  Pixel_preview=Pixel_preview_magnifier;
  // On calcule l'origine de la loupe
  Main_magnifier_offset_X=Mouse_X-(Main_magnifier_width>>1);
  Main_magnifier_offset_Y=Mouse_Y-(Main_magnifier_height>>1);
  // Calcul du coin haut_gauche de la fenêtre devant être zoomée DANS L'ECRAN
  if (Main_magnifier_offset_X+Main_magnifier_width>=Limit_right-Main_offset_X)
    Main_magnifier_offset_X=Limit_right-Main_magnifier_width-Main_offset_X+1;
  if (Main_magnifier_offset_Y+Main_magnifier_height>=Limit_bottom-Main_offset_Y)
    Main_magnifier_offset_Y=Limit_bottom-Main_magnifier_height-Main_offset_Y+1;
  // Calcul des coordonnées absolues de ce coin DANS L'IMAGE
  Main_magnifier_offset_X+=Main_offset_X;
  Main_magnifier_offset_Y+=Main_offset_Y;
  if (Main_magnifier_offset_X<0)
    Main_magnifier_offset_X=0;
  if (Main_magnifier_offset_Y<0)
    Main_magnifier_offset_Y=0;
  // On calcule les bornes visibles dans l'écran
  Position_screen_according_to_zoom();
  Compute_limits();
  Display_all_screen();
  // Repositionner le curseur en fonction des coordonnées visibles
  Compute_paintbrush_coordinates();
  // On fait de notre mieux pour restaurer l'ancienne opération:
  Start_operation_stack(Operation_before_interrupt);
  Display_cursor();
  Wait_end_of_click();
}
/////////////////////////////////////////////////////////// OPERATION_COLORPICK
void Colorpicker_12_0(void)
//
// Opération   : OPERATION_COLORPICK
// Click Souris: 1 ou 2
// Taille_Pile : 0
//
// Souris effacée: Oui
//
{
  Init_start_operation();
  if (Mouse_K==LEFT_SIDE)
  {
    Set_fore_color(Colorpicker_color);
  }
  else
  {
    Set_back_color(Colorpicker_color);
  }
  Operation_push(Mouse_K);
}
void Colorpicker_1_1(void)
//
// Opération   : OPERATION_COLORPICK
// Click Souris: 1
// Taille_Pile : 1
//
// Souris effacée: Non
//
{
  char str[4];
  if ( (Paintbrush_X>=0) && (Paintbrush_Y>=0)
    && (Paintbrush_X=0) && (Paintbrush_Y>=0)
    && (Paintbrush_X1) width--;
          if (height>1) height--;
        }
        Num2str(width,str,4);
        Print_in_menu(str,2);
        Num2str(height,str,4);
        Print_in_menu(str,11);
      }
      else
        Print_coordinates();
    }
    Display_all_screen();
    x=Paintbrush_X;
    y=Paintbrush_Y;
    if (Snap_mode && Config.Adjust_brush_pick)
    {
      dx=Paintbrush_X-start_x;
      dy=Paintbrush_Y-start_y;
      if (dx<0) x++; else {if (dx>0) x--;}
      if (dy<0) y++; else {if (dy>0) y--;}
      Stretch_brush_preview(start_x,start_y,x,y);
    }
    else
      Stretch_brush_preview(start_x,start_y,Paintbrush_X,Paintbrush_Y);
    old_x=Paintbrush_X;
    old_y=Paintbrush_Y;
    Paintbrush_X=start_x;
    Paintbrush_Y=start_y;
    Display_cursor();
    Paintbrush_X=old_x;
    Paintbrush_Y=old_y;
    Display_cursor();
    Operation_stack_size-=2;
    Operation_push(x);
    Operation_push(y);
    Operation_push(start_x);
    Operation_push(start_y);
  }
  Operation_push(Paintbrush_X);
  Operation_push(Paintbrush_Y);
  Operation_push(2);
}
void Stretch_brush_0_7(void)
//
// Opération   : OPERATION_STRETCH_BRUSH
// Click Souris: 0
// Taille_Pile : 7
//
// Souris effacée: Non
//
{
  char  str[5];
  short start_x;
  short start_y;
  short old_x;
  short old_y;
  short width=0;
  short height=0;
  byte  size_change;
  short prev_state;
  Operation_pop(&prev_state);
  Operation_pop(&old_y);
  Operation_pop(&old_x);
  Operation_pop(&start_y);
  Operation_pop(&start_x);
  if ((Paintbrush_X!=old_x) || (Paintbrush_Y!=old_y) || (prev_state!=3))
  {
    if (Menu_is_visible)
    {
      if (Config.Coords_rel)
      {
        width=((start_x1)?start_x+(Brush_width>>1)-1:1;
        height=(Brush_height>1)?start_y+(Brush_height>>1)-1:1;
        break;
      case 'X': // Moitié X
        width=(Brush_width>1)?start_x+(Brush_width>>1)-1:1;
        height=start_y+Brush_height-1;
        break;
      case 'Y': // Moitié Y
        width=start_x+Brush_width-1;
        height=(Brush_height>1)?start_y+(Brush_height>>1)-1:1;
        break;
      case 'n': // Normal
        width=start_x+Brush_width-1;
        height=start_y+Brush_height-1;
        break;
      default :
        size_change=0;
    }
    Key_ANSI=0;
  }
  else
    size_change=0;
  if (size_change)
  {
    // On efface la preview de la brosse (et la croix)
    Display_all_screen();
    old_x=Paintbrush_X;
    old_y=Paintbrush_Y;
    Paintbrush_X=start_x;
    Paintbrush_Y=start_y;
    Display_cursor();
    Paintbrush_X=old_x;
    Paintbrush_Y=old_y;
    Stretch_brush_preview(start_x,start_y,width,height);
    Display_cursor();
    Operation_stack_size-=2;
    Operation_push(width);
    Operation_push(height);
  }
  Operation_push(start_x);
  Operation_push(start_y);
  Operation_push(Paintbrush_X);
  Operation_push(Paintbrush_Y);
  Operation_push(3);
}
void Stretch_brush_2_7(void)
//
// Opération   : OPERATION_STRETCH_BRUSH
// Click Souris: 2
// Taille_Pile : 7
//
// Souris effacée: Oui
//
{
  short computed_x;
  short computed_y;
  short start_x;
  short start_y;
  Operation_stack_size-=3;
  Operation_pop(&start_y);
  Operation_pop(&start_x);
  Operation_pop(&computed_y);
  Operation_pop(&computed_x);
  // On efface la preview de la brosse (et la croix)
  Display_all_screen();
  // Et enfin on stocke pour de bon la nouvelle brosse étirée
  Stretch_brush(start_x,start_y,computed_x,computed_y);
  Return_to_draw_mode();
}
//////////////////////////////////////////////////// OPERATION_ROTATE_BRUSH
void Rotate_brush_12_0(void)
//
// Opération   : OPERATION_ROTATE_BRUSH
// Click Souris: 1 ou 2
// Taille_Pile : 0
//
// Souris effacée: Oui
//
{
  Init_start_operation();
  if (Mouse_K==LEFT_SIDE)
  {
    Brush_rotation_center_X=Paintbrush_X+(Brush_width>>1)-Brush_width;
    Brush_rotation_center_Y=Paintbrush_Y;
    Brush_rotation_center_is_defined=1;
    Operation_push(Paintbrush_X); // Dernière position calculée X
    Operation_push(Paintbrush_Y); // Dernière position calculée Y
    Operation_push(Paintbrush_X); // Dernière position X
    Operation_push(Paintbrush_Y); // Dernière position Y
    Operation_push(1); // State précédent
    if ((Config.Coords_rel) && (Menu_is_visible))
      Print_in_menu("Angle:   0°    ",0);
  }
  else
  {
    Start_operation_stack(Operation_before_interrupt);
    Wait_end_of_click(); // FIXME: celui-la il donne un résultat pas très chouette en visuel
  }
}
void Rotate_brush_1_5(void)
//
// Opération   : OPERATION_ROTATE_BRUSH
// Click Souris: 1
// Taille_Pile : 5
//
// Souris effacée: Non
//
{
  char  str[4];
  short old_x;
  short old_y;
  short prev_state;
  float angle;
  int dx,dy;
  Operation_pop(&prev_state);
  Operation_pop(&old_y);
  Operation_pop(&old_x);
  if ( (Paintbrush_X!=old_x) || (Paintbrush_Y!=old_y) || (prev_state!=2) )
  {
    if ( (Brush_rotation_center_X==Paintbrush_X)
      && (Brush_rotation_center_Y==Paintbrush_Y) )
      angle=0.0;
    else
    {
      dx=Paintbrush_X-Brush_rotation_center_X;
      dy=Paintbrush_Y-Brush_rotation_center_Y;
      angle=acos(((float)dx)/sqrt((dx*dx)+(dy*dy)));
      if (dy>0) angle=M_2PI-angle;
    }
    if (Menu_is_visible)
    {
      if (Config.Coords_rel)
      {
        Num2str((int)(angle*180.0/M_PI),str,3);
        Print_in_menu(str,7);
      }
      else
        Print_coordinates();
    }
    Display_all_screen();
    Rotate_brush_preview(angle);
    Display_cursor();
    Operation_stack_size-=2;
    Operation_push(Paintbrush_X);
    Operation_push(Paintbrush_Y);
  }
  Operation_push(Paintbrush_X);
  Operation_push(Paintbrush_Y);
  Operation_push(2);
}
void Rotate_brush_0_5(void)
//
// Opération   : OPERATION_ROTATE_BRUSH
// Click Souris: 0
// Taille_Pile : 5
//
// Souris effacée: Non
//
{
  char  str[4];
  short old_x;
  short old_y;
  short computed_x=0;
  short computed_y=0;
  byte  angle_change;
  short prev_state;
  float angle=0.0;
  int dx,dy;
  Operation_pop(&prev_state);
  Operation_pop(&old_y);
  Operation_pop(&old_x);
  if ((Paintbrush_X!=old_x) || (Paintbrush_Y!=old_y) || (prev_state!=3))
  {
    if ( (Brush_rotation_center_X==Paintbrush_X)
      && (Brush_rotation_center_Y==Paintbrush_Y) )
      angle=0.0;
    else
    {
      dx=Paintbrush_X-Brush_rotation_center_X;
      dy=Paintbrush_Y-Brush_rotation_center_Y;
      angle=acos(((float)dx)/sqrt((dx*dx)+(dy*dy)));
      if (dy>0) angle=M_2PI-angle;
    }
    if (Menu_is_visible)
    {
      if (Config.Coords_rel)
      {
        Num2str(Round(angle*180.0/M_PI),str,3);
        Print_in_menu(str,7);
      }
      else
        Print_coordinates();
    }
  }
  // Utilise Key_ANSI au lieu de Key, car Get_input() met ce dernier
  // à zero si une operation est en cours (Operation_stack_size!=0)
  if (Key_ANSI)
  {
    angle_change=1;
    computed_x=Brush_rotation_center_X;
    computed_y=Brush_rotation_center_Y;
    switch (Key_ANSI)
    {
      case '6': angle=     0.0 ; computed_x++;             break;
      case '9': angle=M_PI*0.25; computed_x++; computed_y--; break;
      case '8': angle=M_PI*0.5 ;             computed_y--; break;
      case '7': angle=M_PI*0.75; computed_x--; computed_y--; break;
      case '4': angle=M_PI     ; computed_x--;             break;
      case '1': angle=M_PI*1.25; computed_x--; computed_y++; break;
      case '2': angle=M_PI*1.5 ;             computed_y++; break;
      case '3': angle=M_PI*1.75; computed_x++; computed_y++; break;
      default :
        angle_change=0;
    }
    Key_ANSI=0;
  }
  else
    angle_change=0;
  if (angle_change)
  {
    // On efface la preview de la brosse
    Display_all_screen();
    Rotate_brush_preview(angle);
    Display_cursor();
    Operation_stack_size-=2;
    Operation_push(computed_x);
    Operation_push(computed_y);
  }
  Operation_push(Paintbrush_X);
  Operation_push(Paintbrush_Y);
  Operation_push(3);
}
void Rotate_brush_2_5(void)
//
// Opération   : OPERATION_ROTATE_BRUSH
// Click Souris: 2
// Taille_Pile : 5
//
// Souris effacée: Oui
//
{
  short computed_x;
  short computed_y;
  int dx,dy;
  float angle;
  // On efface la preview de la brosse
  Display_all_screen();
  Operation_stack_size-=3;
  Operation_pop(&computed_y);
  Operation_pop(&computed_x);
  // Calcul de l'angle par rapport à la dernière position calculée
  if ( (Brush_rotation_center_X==computed_x)
    && (Brush_rotation_center_Y==computed_y) )
    angle=0.0;
  else
  {
    dx=computed_x-Brush_rotation_center_X;
    dy=computed_y-Brush_rotation_center_Y;
    angle=acos(((float)dx)/sqrt((dx*dx)+(dy*dy)));
    if (dy>0) angle=M_2PI-angle;
  }
  // Et enfin on stocke pour de bon la nouvelle brosse étirée
  Rotate_brush(angle);
  Return_to_draw_mode();
}
///////////////////////////////////////////////////// OPERATION_DISTORT_BRUSH
/// Draws a 2x2 XOR square at the specified picture coordinates, on the screen.
void Draw_stretch_spot(short x_pos, short y_pos)
{
  short x,y;
  for (y=y_pos-1;y=Limit_top && y<=Limit_visible_bottom)
      for (x=x_pos-1;x=Limit_left && x<=Limit_visible_right)
          Pixel_preview(x,y,~Read_pixel(x-Main_offset_X,y-Main_offset_Y));
  Update_part_of_screen(x_pos-1, y_pos-1, 2, 2);
}
void Distort_brush_0_0(void)
//
// Opération   : OPERATION_DISTORT_BRUSH
// Click Souris: 0
// Taille_Pile : 0
//
// Souris effacée: Non
//
{
  if ( Menu_is_visible )
  {
      Print_in_menu("POSITION BRUSH TO START ",0);
  }
}
void Distort_brush_1_0(void)
//
// Opération   : OPERATION_DISTORT_BRUSH
// Click Souris: 1
// Taille_Pile : 0
//
// Souris effacée: Non
//
{
  short x_pos, y_pos;
  Init_start_operation();
  Paintbrush_hidden=1;
  Hide_cursor();
  
  // Top left angle
  x_pos=Paintbrush_X-Brush_offset_X;
  y_pos=Paintbrush_Y-Brush_offset_Y;
  Draw_stretch_spot(x_pos,y_pos);
  Operation_push(x_pos);
  Operation_push(y_pos);
  
  // Top right angle
  x_pos+=Brush_width;
  Draw_stretch_spot(x_pos,y_pos);
  Operation_push(x_pos);
  Operation_push(y_pos);
  
  // Bottom right angle
  y_pos+=Brush_height;
  Draw_stretch_spot(x_pos,y_pos);
  Operation_push(x_pos);
  Operation_push(y_pos);
  
  // Bottom left angle
  x_pos-=Brush_width;
  Draw_stretch_spot(x_pos,y_pos);
  Operation_push(x_pos);
  Operation_push(y_pos);
  
  Distort_brush_preview(
    Operation_stack[1],
    Operation_stack[2],
    Operation_stack[3],
    Operation_stack[4],
    Operation_stack[5],
    Operation_stack[6],
    Operation_stack[7],
    Operation_stack[8]);
  Display_cursor();
  Update_part_of_screen(Paintbrush_X-Brush_offset_X, Paintbrush_Y-Brush_offset_Y, Brush_width, Brush_height);
  Wait_end_of_click();
  // Erase the message in status bar
  if ( (Config.Coords_rel) && (Menu_is_visible) )
  {
      Print_in_menu("X:       Y:             ",0);
  }
}
void Distort_brush_1_8(void)
//
//  Opération   : OPERATION_DISTORT_BRUSH
//  Click Souris: 1
//  Taille_Pile : 8
//
//  Souris effacée: No
//
{
  // How far (in pixels) you can catch a handle
  #define REACH_DISTANCE 100
  short i;
  short x[4];
  short y[4];
  long best_distance=REACH_DISTANCE;
  short best_spot=-1;
  for (i=3;i>=0;i--)
  {
    long distance;
    Operation_pop(&y[i]);
    Operation_pop(&x[i]);
    distance=Distance(Paintbrush_X,Paintbrush_Y,x[i],y[i]);
    if (distance-1)
  {
    Operation_push(best_spot);
  }
  if ( (Config.Coords_rel) && (Menu_is_visible) )
  {
      Print_in_menu("X:       Y:             ",0);
      Print_coordinates();
  }
}
void Distort_brush_1_9(void)
//
//  Opération   : OPERATION_DISTORT_BRUSH
//  Click Souris: 1
//  Taille_Pile : 9
//
//  Souris effacée: No
//
{
  short i;
  short x[4];
  short y[4];
  short selected_corner;
  // Pop all arguments
  Operation_pop(&selected_corner);
  for (i=3;i>=0;i--)
  {
    Operation_pop(&y[i]);
    Operation_pop(&x[i]);
  }
  
  if (Paintbrush_X!=x[selected_corner] || Paintbrush_Y!=y[selected_corner])
  {  
    Hide_cursor();
    
    // Easiest refresh mode: make no assumptions on how the brush was
    // displayed before.
    Display_all_screen();
    x[selected_corner]=Paintbrush_X;
    y[selected_corner]=Paintbrush_Y;
    for (i=0;i<4;i++)
      Draw_stretch_spot(x[i],y[i]);
    Distort_brush_preview(x[0],y[0],x[1],y[1],x[2],y[2],x[3],y[3]);
    Display_cursor();
    
    if ( (Config.Coords_rel) && (Menu_is_visible) )
    {
        Print_in_menu("X:       Y:             ",0);
        Print_coordinates();
    }
    Update_rect(0,0,Screen_width,Menu_Y);
  }
  
  // Push back all arguments
  for (i=0;i<4;i++)
  {
    Operation_push(x[i]);
    Operation_push(y[i]);
  }
  Operation_push(selected_corner);
}
void Distort_brush_0_9(void)
//
//  Opération   : OPERATION_DISTORT_BRUSH
//  Click Souris: 0
//  Taille_Pile : 9
//
//  Souris effacée: No
//
{
  short selected_corner;
  Operation_pop(&selected_corner);
  
}
void Distort_brush_2_0(void)
//
// Opération   : OPERATION_DISTORT_BRUSH
// Click Souris: 2
// Taille_Pile : 0
//
// Souris effacée: Oui
//
{
  Paintbrush_hidden=0;
  Display_all_screen();
  // Erase the message in status bar
  if ( (Config.Coords_rel) && (Menu_is_visible) )
  {
      Print_in_menu("X:       Y:             ",0);
  }
  Return_to_draw_mode();
}
void Distort_brush_2_8(void)
//
// Opération   : OPERATION_DISTORT_BRUSH
// Click Souris: 2
// Taille_Pile : 8
//
// Souris effacée: Oui
//
{
  short i;
  short x[4];
  short y[4];
  // Pop all arguments
  for (i=3;i>=0;i--)
  {
    Operation_pop(&y[i]);
    Operation_pop(&x[i]);
  }
  Distort_brush(x[0],y[0],x[1],y[1],x[2],y[2],x[3],y[3]);
  
  Paintbrush_hidden=0;
  Display_all_screen();
  
  Return_to_draw_mode();
}