/** 
 *  Yudit Unicode Editor Source File
 *
 *  GNU Copyright (C) 1997-2023  Gaspar Sinai <gaspar@yudit.org>  
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License, version 2,
 *  dated June 1991. See file COPYYING for details.
 *
 *  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., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include "swidget/SDrawing.h"

#define SS_LEFT_MARGIN 2

#define SS_MAX_SEGMENTS 100
#define SS_MAX_LINES 100

SDrawingListener::SDrawingListener(void)
{
}
SDrawingListener::~SDrawingListener()
{
}

/**
 * A drawing component that lets you make a small 
 * drawing with the mouse.
 */
SDrawing::SDrawing (void) : fg (SColor (0.0, 0.0, 0.0, 1.0)),
  lfg (SColor (0.0, 0.0, 1.0, 1.0))
{
  listener = 0;
  preferredSize = SDimension (20,20);
  SDimension d = border.getBorderSize();
  preferredSize.width += d.width * 2;
  preferredSize.height += d.height * 2;
  clip (true);
}

/**
 * Deletes this drawing. Nothing to do.
 */
SDrawing::~SDrawing ()
{
}

/**
 * There can be only one drawing listener.
 */
void
SDrawing::setDrawingListener (SDrawingListener* _listener)
{
  listener = _listener;
}

/**
 * @return the drawing drawn.
 */
const SLineCurves&
SDrawing::getDrawing()
{
  return allLines;
}

/**
 * Set the overall background. This will effectively be the border's 
 * background.
 * @param bg is the background.
 */
void
SDrawing::setBackground (const SColor& bg)
{
  border.setBackground (bg);
}

/**
 * Set the background of the drawing itself.
 * @param bg is the background.
 */
void
SDrawing::setTextBackground (const SColor& bg)
{
  SPanel::setBackground (bg);
}

/**
 * Set the foreground of the drawing itself.
 * @param fg is the foreground
 * @param fgrecent is the foreground of the last line, being drawn
 */
void
SDrawing::setForeground (const SColor& _fg, const SColor& fgrecent)
{
  fg = _fg;
  lfg = fgrecent;
}

/**
 * Redraw the Component on a canvas 
 * @param canvas is where we redraw this.
 */
void
SDrawing::redraw (SCanvas *canvas, int x, int y, unsigned int width, unsigned int height)
{
  clip (false);
  border.redraw (canvas, x, y, width, height);
  clip (true);
  for (unsigned int i=0; i<allLines.size(); i++)
  {
    redrawInternal (canvas, fg, allLines[i]);
  }
  redrawInternal (canvas, lfg, lastLines);

}

/**
 * redraw the whole drawing. 
 * @param fore is the foreground color.
 * @param lines are the lines.
 */
void
SDrawing::redrawInternal (SCanvas* canvas, const SColor& fore, 
    const SLineCurve& lines)
{
  for (unsigned int i=1; i<lines.size(); i++)
  {
    SLocation p0 = lines[i-1];
    SLocation p1 = lines[i];
    canvas->bitline (fore, p0.x, p0.y, p1.x, p1.y);
  }
}


/**
 * redraw the whole drawing. 
 * @param fore is the foreground color.
 * @param lines are the lines.
 */
void
SDrawing::redrawContours (SWindow* canvas, const SColor& fore,
    const SLineCurve& lines)
{
  if (window->isDoubleBufferEnabled ())
  {
    window->redraw (true, 0 , 0, canvas->getWidth(), canvas->getHeight());
  }
  else
  {
    redrawInternal (canvas, fore, lines);
  }
}


/**
 * Turn clipping at the border on and off.
 * @param on is true if we turn on clipping.
 */
void
SDrawing::clip (bool on)
{
  if (!on)
  {
    window->removeClippingArea ();
    return;
  }
  window->setClippingArea (
       (int) border.getBorderSize().width ,
       (int) border.getBorderSize().height,
       size.width - 2 * border.getBorderSize().width,
       size.height - 2 * border.getBorderSize().height);
}

/**
 * Pass this to the editor. Start a timer to measure
 * the double click. Get the focus.
 */
void
SDrawing::buttonPressed (SWindow * w, int button, int x, int y)
{
 if (lastLines.size()==0 && (button == 2 ||  button == 1))
 {
   if (listener != 0) listener->clicked (this, button);
   return;
 }
 if (button != 0) return;
 lastLines.clear();
 lastLines.append (SLocation (x,y));
}

/**
 * A button was released.
 */
void
SDrawing::buttonReleased (SWindow * w, int button, int x, int y)
{
  if (button != 0) return;
  /* redraw with different color */
  if (lastLines.size() > 1)
  {
    redrawContours (w, fg, lastLines);
    allLines.append (lastLines);
    if (allLines.size() > SS_MAX_LINES) clear();
    if (listener) listener->strokeChanged(this, allLines.size());
  }
  lastLines.clear();
}

void
SDrawing::buttonDragged (SWindow * w, int button, int x, int y)
{
  if (button != 0) return;
  lastLines.append (SLocation (x,y));
  redrawContours (w, lfg, lastLines);
}

/**
 * Resize the component
 * @param d is the new size
 */
void 
SDrawing::resize(const SDimension& d)
{
  if (getSize() == d) return;
  SPanel::resize (d);
  border.resize (d);
  SDimension td;
  if (size.width + 2 * SS_LEFT_MARGIN> border.getBorderSize().width * 2)
  {
    td.width  = size.width - border.getBorderSize().width * 2 - 2 * SS_LEFT_MARGIN;
  }
  if (size.height > border.getBorderSize().height * 2)
  {
    td.height  = size.height - border.getBorderSize().height * 2;
  }
  clip (true);
  SComponent::resize (d);
}

/**
 * Move the component
 * @param l is the new location
 */
void 
SDrawing::move(const SLocation& l)
{
  SPanel::move (l);
}

/**
 * getPreferredSize.
 * This is calculated form the preferred size of the textView and
 * adding the border size to it.
 */
const SDimension&
SDrawing::getPreferredSize()
{
  return preferredSize;
}

/**
 * Schedule a redraw of the whole drawing. 
 * @param isclear is true if window should be cleared before redraw.
 */
void
SDrawing::redrawClear (bool isclear)
{
  int eheight = (int)size.height - (int)2 * border.getBorderSize().height;
  if (eheight <= 0) return;
  int ewidth = (int)size.width - (int)2 * border.getBorderSize().width 
       + 2 * SS_LEFT_MARGIN;

  window->redraw (isclear, (int) border.getBorderSize().width ,
    (int) border.getBorderSize().height, ewidth + 2 * SS_LEFT_MARGIN,
     eheight);
}

/**
 * Clear the drawing
 */
void
SDrawing::clear()
{
  allLines.clear();
  lastLines.clear();
  if (listener) listener->strokeChanged(this, allLines.size());
  redrawClear (true);
}

void
SDrawing::undo ()
{
  if (allLines.size()) allLines.truncate (allLines.size()-1);
  if (listener) listener->strokeChanged(this, allLines.size());
  redrawClear (true);
}
