/*
 * Dillo Widget
 *
 * Copyright 2005-2007 Sebastian Geerken <sgeerken@dillo.org>
 *
 * 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 3 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, see <http://www.gnu.org/licenses/>.
 */

#include "../lout/msg.h"

#include "fltkpreview.hh"
#include "fltkmisc.hh"

#include <FL/Fl.H>
#include <FL/Fl_Bitmap.H>
#include <FL/fl_draw.H>
#include <stdio.h>

#include "preview.xbm"

namespace dw {
namespace fltk {

FltkPreview::FltkPreview (int x, int y, int w, int h,
                          dw::core::Layout *layout, const char *label):
   FltkViewBase (x, y, w, h, label)
{
   layout->attachView (this);

   scrollX = 0;
   scrollY = 0;
   scrollWidth = 1;
   scrollHeight = 1;
}

FltkPreview::~FltkPreview ()
{
}

int FltkPreview::handle (int event)
{
   return FltkViewBase::handle (event);
}

int FltkPreview::translateViewXToCanvasX (int x)
{
   return x * canvasWidth / w ();
}

int FltkPreview::translateViewYToCanvasY (int y)
{
   return y * canvasHeight / h ();
}

int FltkPreview::translateCanvasXToViewX (int x)
{
   return x * w () / canvasWidth;
}

int FltkPreview::translateCanvasYToViewY (int y)
{
   return y * h () / canvasHeight;
}

void FltkPreview::setCanvasSize (int width, int ascent, int descent)
{
   FltkViewBase::setCanvasSize (width, ascent, descent);
   if (parent() && parent()->visible ())
      ((FltkPreviewWindow*)parent())->reallocate ();
}

bool FltkPreview::usesViewport ()
{
   return true;
}

int FltkPreview::getHScrollbarThickness ()
{
   return 0;
}

int FltkPreview::getVScrollbarThickness ()
{
   return 0;
}

int FltkPreview::getScrollbarOnLeft ()
{
   return 0;
}

void FltkPreview::scrollTo (int x, int y)
{
   scrollX = x;
   scrollY = y;
}

void FltkPreview::scroll (dw::core::ScrollCommand cmd)
{
   MSG_ERR("FltkPreview::scroll not implemented\n");
}

void FltkPreview::setViewportSize (int width, int height,
                                   int hScrollbarThickness,
                                   int vScrollbarThickness)
{
   scrollWidth = width - vScrollbarThickness;
   scrollHeight = height - hScrollbarThickness;
}

void FltkPreview::drawText (core::style::Font *font,
                            core::style::Color *color,
                            core::style::Color::Shading shading,
                            int x, int y, const char *text, int len)
{
   /*
    * We must call setfont() before calling getwidth() (or anything
    * else that measures text).
    */
   FltkFont *ff = (FltkFont*)font;
   Fl::set_font(ff->font, translateCanvasXToViewX (ff->size));
#if 0
   /**
    * \todo Normally, this should already be known, maybe it
    * should be passed?
    */
   int width = (int)getwidth (text, len);
   int height = font->ascent; // No descent, this would look to "bold".

   int x1 = translateCanvasXToViewX (x);
   int y1 = translateCanvasYToViewY (y);
   int x2 = translateCanvasXToViewX (x + width);
   int y2 = translateCanvasYToViewY (y + height);
   Rectangle rect (x1, y1, x2 - x1, y2 - y1);

   setcolor(((FltkColor*)color)->colors[shading]);
   fillrect (rect);
#endif
   fl_color(((FltkColor*)color)->colors[shading]);
   fl_draw(text, len, translateCanvasXToViewX (x), translateCanvasYToViewY(y));
}

void FltkPreview::drawSimpleWrappedText (core::style::Font *font,
                                         core::style::Color *color,
                                         core::style::Color::Shading shading,
                                         int x, int y, int w, int h,
                                         const char *text)
{
}

void FltkPreview::drawImage (core::Imgbuf *imgbuf, int xRoot, int yRoot,
                int x, int y, int width, int height)
{
}

bool FltkPreview::usesFltkWidgets ()
{
   return false;
}

void FltkPreview::drawFltkWidget (Fl_Widget *widget,
                                  core::Rectangle *area)
{
}

// ----------------------------------------------------------------------

FltkPreviewWindow::FltkPreviewWindow (dw::core::Layout *layout):
   Fl_Menu_Window (1, 1)
{
   box (FL_EMBOSSED_BOX);

   begin ();
   preview = new FltkPreview (BORDER_WIDTH, BORDER_WIDTH, 1, 1, layout);
   end ();

   hide ();
}

FltkPreviewWindow::~FltkPreviewWindow ()
{
}

void FltkPreviewWindow::showWindow ()
{
   reallocate ();
   show ();
}

void FltkPreviewWindow::reallocate ()
{
   int maxWidth = misc::screenWidth () / 2;
   int maxHeight = misc::screenHeight () * 4 / 5;
   int mx, my, width, height;
   bool warp = false;

   if (preview->canvasHeight * maxWidth > maxHeight * preview->canvasWidth) {
      // Expand to maximal height (most likely case).
      width = preview->canvasWidth * maxHeight / preview->canvasHeight;
      height = maxHeight;
   } else {
      // Expand to maximal width.
      width = maxWidth;
      height = preview->canvasHeight * maxWidth / preview->canvasWidth;
   }

   Fl::get_mouse(mx, my);

   posX = mx - preview->translateCanvasXToViewX (preview->scrollX
                                                 + preview->scrollWidth / 2);
   posY = my - preview->translateCanvasYToViewY (preview->scrollY
                                                 + preview->scrollHeight / 2);

   if (posX < 0) {
      mx -= posX;
      posX = 0;
      warp = true;
   } else if (posX + width > misc::screenWidth ()) {
      mx -= (posX - (misc::screenWidth () - width));
      posX = misc::screenWidth () - width;
      warp = true;
   }

   if (posY < 0) {
      my -= posY;
      posY = 0;
      warp = true;
   } else if (posY + height > misc::screenHeight ()) {
      my -= (posY - (misc::screenHeight () - height));
      posY = misc::screenHeight () - height;
      warp = true;
   }

   if (warp)
      misc::warpPointer (mx, my);

   resize (posX, posY, width, height);

   preview->size(w () - 2 * BORDER_WIDTH, h () - 2 * BORDER_WIDTH);
}

void FltkPreviewWindow::hideWindow ()
{
   Fl_Window::hide ();
}

void FltkPreviewWindow::scrollTo (int mouseX, int mouseY)
{
   preview->scrollX =
      preview->translateViewXToCanvasX (mouseX - posX - BORDER_WIDTH)
      - preview->scrollWidth / 2;
   preview->scrollY =
      preview->translateViewYToCanvasY (mouseY - posY - BORDER_WIDTH)
      - preview->scrollHeight / 2;
   preview->theLayout->scrollPosChanged (preview,
                                         preview->scrollX, preview->scrollY);
}

// ----------------------------------------------------------------------

FltkPreviewButton::FltkPreviewButton (int x, int y, int w, int h,
                                      dw::core::Layout *layout,
                                      const char *label):
   Fl_Button (x, y, w, h, label)
{
   image (new Fl_Bitmap (preview_bits, preview_width, preview_height));
   window = new FltkPreviewWindow (layout);
}

FltkPreviewButton::~FltkPreviewButton ()
{
}

int FltkPreviewButton::handle (int event)
{
   /** \bug Some parts are missing. */

   switch (event) {
   case FL_PUSH:
      window->showWindow ();
      return Fl_Button::handle (event);

   case FL_DRAG:
      if (window->visible ()) {
         window->scrollTo (Fl::event_x_root (), Fl::event_y_root ());
         return 1;
      }
      return Fl_Button::handle (event);

   case FL_RELEASE:
      window->hideWindow ();
      return Fl_Button::handle (event);

   default:
      return Fl_Button::handle (event);
   }
}

} // namespace fltk
} // namespace dw
