/**
 *  GraphTFT plugin for the Video Disk Recorder 
 * 
 *  imlibrenderer.c - A plugin for the Video Disk Recorder
 *
 *  (c) 2004 Lars Tegeler, Sascha Volkenandt  
 *
 * This code is distributed under the terms and conditions of the
 * GNU GENERAL PUBLIC LICENSE. See the file COPYING for details.
 *
 * $Id: imlibrenderer.c,v 1.9 2007/12/03 19:58:05 root Exp $
 *
 **/


#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <locale.h>

#include <string>
#include <vector>
#include <sstream>

#include <string>
#include <utility>
#include <iostream>

#include "imlibrenderer.h"
#include "theme.h"
#include "common.h"

using std::string;
using std::cout;
using std::endl;

//***************************************************************************
// Class ImlibRenderer
//***************************************************************************

ImlibRenderer::ImlibRenderer(int x, int y, int width, int height, 
                             string cfgPath, int utf, string thmPath)
   : Renderer(x, y, width, height, cfgPath, utf, thmPath)
{
   Imlib_Context ctx = imlib_context_new();
   imlib_context_push(ctx);
	imlib_set_color_usage(256);

   // cache

   imlib_set_cache_size(4096 * 1024);
   imlib_set_font_cache_size(2048 * 1024);
 
   // font

   std::ostringstream path; path <<  confPath.c_str() << "/graphTFT/fonts/";
   imlib_add_path_to_font_path(path.str().c_str());
  
   // new image

   _cur_image = imlib_create_image(themeWidth, themeHeight);
   imlib_context_set_image(_cur_image);
}

ImlibRenderer::~ImlibRenderer()
{
	;
}

int ImlibRenderer::init(int devnum)
{
   _render_image = imlib_create_image(dspWidth, dspHeight);

   return success;
}

//***************************************************************************
// Refresh
//***************************************************************************

void ImlibRenderer::refresh()
{
   LogDuration ld("ImlibRenderer::refresh()");

   // resize only if needed !

   if (xOffset || yOffset || xBorder || yBorder 
      || themeWidth != dspWidth || themeHeight != dspHeight)
   {
      tell(2, "scale image from (%d/%d) to (%d/%d)", 
        themeWidth, themeHeight, 
        dspWidth - xOffset - (2 * xBorder), 
        dspHeight - yOffset - (2 * yBorder));
      
      imlib_context_set_image(_render_image);

      imlib_blend_image_onto_image(_cur_image, 0, 0, 0, themeWidth, themeHeight,
                                   xOffset + xBorder, yOffset + yBorder,
                                   dspWidth - xOffset - (2 * xBorder), 
                                   dspHeight - yOffset - (2 * yBorder));

      pImageToDisplay = &_render_image;
   }
   else
   {
      pImageToDisplay = &_cur_image;
   }
}

void ImlibRenderer::clear()
{
	// clear the current image

	imlib_context_set_image(_cur_image);
	imlib_free_image();
	_cur_image = imlib_create_image(themeWidth, themeHeight);
	imlib_context_set_image(_cur_image);
}


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

void ImlibRenderer::image(const char* fname, int x, int y, 
                          int width, int height, 
                          bool fit, bool aspectRatio)
{
   Imlib_Image new_image;
   int imgWidth = 0;
   int imgHeight = 0;
   std::ostringstream path;
   int areaWidth = width;
   int areaHeight = height;

   if (fname[0] == '/')
      path << fname;
   else
      path << confPath << "/graphTFT/themes/" << themePath << "/" << fname; 

   if (!fileExists(path.str().c_str()))
   { 
      tell(0, "Image '%s' not found", path.str().c_str());
      return ;
   }

   new_image = imlib_load_image(path.str().c_str());

   if (!new_image)
   { 
      tell(0, "The image '%s' could not be loaded", path.str().c_str());
      return ;
   }

   imlib_context_set_image(new_image);
   imgWidth = imlib_image_get_width();
   imgHeight = imlib_image_get_height();

   imlib_context_set_image(_cur_image);

   if (fit)
   {
      if (aspectRatio)
      {
         double ratio = (double)imgWidth / (double)imgHeight;

         if ((double)width/(double)imgWidth < (double)height/(double)imgHeight)
         {
            height = (int)((double)width / ratio);
            y += (areaHeight-height) / 2;
         }
         else
         {
            width = (int)((double)height * ratio);
            x += (areaWidth-width) / 2;
         }
      }

      imlib_blend_image_onto_image(new_image, 0, 0, 0, 
                                   imgWidth, imgHeight, x, y, 
                                   width, height);
   }
   else
   {
      imlib_blend_image_onto_image(new_image, 0, 0, 0, 
                                   imgWidth, imgHeight, x, y, 
                                   imgWidth, imgHeight);
   }
  
   imlib_context_set_image(new_image);
   imlib_free_image();
   imlib_context_set_image(_cur_image);
}

int ImlibRenderer::textWidthOf(const char* text, const char* fontName, 
                               int fontSize)
{
   Imlib_Font font;
   int width = 20;
   int height = 20;

   if (!fontName || !text || !strlen(text))
      return 1;

   std::ostringstream font_name_size;
	font_name_size <<  fontName << "/" << fontSize;

   font = imlib_load_font((font_name_size.str()).c_str());

   if (font)
   {
      imlib_context_set_font(font);
		imlib_context_set_image(_cur_image);
      imlib_get_text_size(text, &width, &height);
      imlib_free_font();

      width /= strlen(text);
   }  
	else
		tell(1, "The font '%s' could not be loaded.", fontName);

   return width;
}

int ImlibRenderer::charWidthOf(const char* fontName, int fontSize)
{
   Imlib_Font font;
   int width = 20;
   int height = 20;

   const char* text = "We need here something like a "
      "representive sentence WITH SOME UPPER CASE LETTERS";

   if (!fontName)
   {
      imlib_get_text_size(text, &width, &height);
      return width / strlen(text);
   }

   std::ostringstream font_name_size;
	font_name_size <<  fontName << "/" << fontSize;

   font = imlib_load_font((font_name_size.str()).c_str());

   if (font)
   {
      imlib_context_set_font(font);
		imlib_context_set_image(_cur_image);
      imlib_get_text_size(text, &width, &height);
      imlib_free_font();

      width /= strlen(text);
   }  
	else
		tell(1, "The font '%s' could not be loaded.", fontName);

   return width;
}

/*
// ***************************************************************************
// Render Text
// ***************************************************************************

int ImlibRenderer::text(const char* _text, 
                        const char* font_name, int size, 
                        int align, 
                        int x, int y, 
                        int r, int g, int b, 
                        int width, int height, 
                        int lines, int dots)
{
   int lineHeight, textWidth, dummy;
   int blankWidth;
   int dotsWidth;
   int currentWidth;
   Imlib_Font font;
   const char* token;
   char* text;
   char* line;

   if (!strlen(_text))
      return 0;

   // load font

   std::ostringstream font_name_size;
   font_name_size <<  font_name << "/" << size;

   if (!(font = imlib_load_font((font_name_size.str()).c_str())))
   {
      tell(1, "The font '%s' could not be loaded.", font_name);
      return 0;
   }

   text = new char[2 * strlen(_text) + 1];
   line = new char[2 * strlen(_text) + 1];
   strcpy(text, _text);

   // convert to UTF8

   if (utf8)
   {
      int inLen = strlen(_text);
      int outLen = 2 * inLen;

      isolat1ToUTF8((unsigned char*)text , &outLen, (unsigned char*)_text, &inLen);
     	// toUTF8((unsigned char*)text , &outLen, (unsigned char*)_text, &inLen);
   }

   if (width <= 0)
      width = themeWidth;

   imlib_context_set_font(font);
   imlib_context_set_image(_cur_image);
   imlib_context_set_color(r, g, b, 255);

   imlib_get_text_size(text, &textWidth, &lineHeight);
   imlib_get_text_size(" ", &blankWidth, &dummy);
   imlib_get_text_size("...", &dotsWidth, &dummy);

   if (!lines)
      lines = height / lineHeight;

   // draw at least one line

   if (lines <= 0)
      lines = 1;

   token = strtok(text, " ");

   if (token && *token && *text == ' ' && *token != ' ')
      token--;
   
   //
   // TODO, mabe faster to use imlib_text_get_index_and_location(...) instead of 
   // imlib_get_text_size(...)
   // int imlib_text_get_index_and_location(const char *text, int x, int y, int *char_x_return, int *char_y_return, int *char_width_return, int *char_height_return);
   //
   
   for (int l = 1; token && l <= lines; l++)
   {
      int yPos = y + lineHeight * (l-1);

      currentWidth = 0;
      *line = 0;

      for (int w = 0; token; w++) 
      {
         int tokenWidth;

         imlib_get_text_size(token, &tokenWidth, &dummy);
         
         if (currentWidth + (currentWidth ? blankWidth : 0) + tokenWidth > width)
         {
            // passt nicht mehr

            if (!w || l == lines)
            {
               // alleinstehendes Wort oder letzte Zeile,
               // daher abgeschnitten anzeigen
               
               int i = 0;
               char* p = line + strlen(line);

               if (strlen(line)) *p++ = ' ';
               *p = 0;

               while (currentWidth + (dots ? dotsWidth : 0) < width && token[i])
               {
                  *p++ = token[i++];
                  *p = 0;
                  imlib_get_text_size(line, &currentWidth, &dummy);
               }

               // einer zuviel !

               *(--p) = 0;

               if (dots)
               {
                  if (currentWidth + dotsWidth > width)
                     *(p-1) = 0;
                  
                  strcat(line, "...");
               }

               token = strtok(0, " ");
            }

            break;
         }
         else
         {
            // passt noch rein

            currentWidth += blankWidth + tokenWidth;
            sprintf(line, "%s%s%s", line, w ? " " : "", token);

            token = strtok(0, " ");
         }
      }

      if (align != 0)
         imlib_get_text_size(line, &textWidth, &dummy);

      if (align == 0)                // left
         imlib_text_draw(x, yPos, _line);
      else if (align == 1)           // center
         imlib_text_draw(x + (width - textWidth) / 2, yPos, line);
      else                           // right
         imlib_text_draw(x + width - textWidth, yPos, line);
   }

   imlib_free_font();
   delete[] text;
   delete[] line;

   return 0;
}
*/

//***************************************************************************
// Draw Text
//***************************************************************************

int ImlibRenderer::text(const char* text, 
                        const char* font_name, int size, 
                        int align, int x, int y, 
                        int r, int g, int b, 
                        int width, int height, 
                        int lines, int dots, int skipLines)
{
   string line;
   string tmp;
   int lineHeight, textWidth, dummy;
   int blankWidth;
   int dotsWidth;
   int currentWidth;
   Imlib_Font font;
   unsigned int pos;

   if (Str::isEmpty(text))
      return 0;
 
   if (utf8)
   {
      const int maxBuf = 10000;
      char out[maxBuf+TB];

      if (toUTF8(out, maxBuf, text) == success)
         tmp = out;
      else
         tmp = text;
   }
   else
      tmp = text;

   // load font

   std::ostringstream font_name_size;
   font_name_size <<  font_name << "/" << size;

   if (!(font = imlib_load_font((font_name_size.str()).c_str())))
   {
      tell(1, "The font '%s' could not be loaded.", font_name);
      return 0;
   }

   if (width <= 0)
      width = themeWidth;          // ;)

   imlib_context_set_font(font);
   imlib_context_set_image(_cur_image);
   imlib_context_set_color(r, g, b, 255);

   imlib_get_text_size(tmp.c_str(), &textWidth, &lineHeight);
   imlib_get_text_size(" ", &blankWidth, &dummy);
   imlib_get_text_size("...", &dotsWidth, &dummy);


   // unsigned int charPerLine = width / charWidthOf();
   
   if (!lines)
      lines = height / lineHeight;

   if (lines <= 0)
      lines = 1;

   pos = 0;
   int tl = 1;

   for (int l = 1; l <= lines && pos < tmp.length(); l++, tl++)
   {
      int yPos = y + lineHeight * (l-1);
      string token;
      line = "";

      currentWidth = 0;

      while (pos < tmp.length())
      {
         int tokenWidth;
         int lf = no;

         int pA = tmp.find_first_of(" \n", pos);

         if (tmp[pA] == '\n')
         {
            lf = yes;
            tmp[pA] = ' ';
         }

         token = tmp.substr(pos, pA - pos);

         if (token == "")
         {
            line += " ";
            pos++;

            if (lf)
               break;
            else
               continue;
         }

         imlib_get_text_size(token.c_str(), &tokenWidth, &dummy);
         
         if (currentWidth + (currentWidth ? blankWidth : 0) + tokenWidth > width)
         {
            // passt nicht mehr ganz rein

            if (!line.length() || l == lines)
            {
               // alleinstehendes Wort oder letzte Zeile,
               // daher Abgeschnitten anzeigen

               unsigned int i = 0;

               while (currentWidth + (dots ? dotsWidth : 0) < width && i <= token.length())
               {
                  line += token.substr(i++, 1);
                  imlib_get_text_size(line.c_str(), &currentWidth, &dummy);
               }

               // einer zuviel !

               line = line.substr(0, line.length()-1);

               if (dots)
               {
                  if (currentWidth + dotsWidth > width)
                     line = line.substr(0, line.length()-1);
                  
                  line += "...";
               }

               pos += token.length();
            }

            break;

         }
         else
         {
            // passt noch rein

            currentWidth += blankWidth + tokenWidth;
            line = line + token;
            pos += token.length();

            if (lf)
               break;
         }
      } 

      if (skipLines && tl <= skipLines)
      {
         l--;
         continue;
      }

      if (align != 0)
         imlib_get_text_size(line.c_str(), &textWidth, &lineHeight);

      if (align == 0)                // left
         imlib_text_draw(x, yPos, line.c_str());
      else if (align == 1)           // center
         imlib_text_draw(x + (width - textWidth) / 2, yPos, line.c_str());
      else                           // right
         imlib_text_draw(x + width - textWidth, yPos, line.c_str());
   }

   imlib_free_font();

   return 0;
}

//***************************************************************************
// Draw Rectangle
//***************************************************************************

void ImlibRenderer::rectangle(int x, int y, 
                              int width, int height, 
                              int r, int g, int b, 
                              int tr) 
{
	imlib_context_set_image(_cur_image);
	imlib_context_set_color(r, g, b, tr);
	imlib_image_fill_rectangle(x, y, width, height);
}

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

void ImlibRenderer::dumpImage2File(const char* fname, int dumpWidth, 
                                   int dumpHeight, const char* aPath)
{
	std::ostringstream path; 
   const char* format;

   if (aPath && *aPath)
      path << aPath;
   else
      path << "/tmp";
   
   format = strchr(fname, '.') + 1;

	// check if local directory structure exist, else create it.

  	if (!fileExists(path.str().c_str())) 
      mkdir(path.str().c_str(), 0777);

	// an image handle

  	Imlib_Image new_image = imlib_create_image(dumpWidth, dumpHeight);

	// get width and heigt

   imlib_context_set_image(*pImageToDisplay);
	int width = imlib_image_get_width();
	int height = imlib_image_get_height();

	imlib_context_set_image(new_image);
	imlib_blend_image_onto_image(*pImageToDisplay, 0, 0, 0, width, height, 
                                0, 0, dumpWidth, dumpHeight);

	// save the image

	imlib_image_set_format(format && *format ? format : "png");
	path << "/" << fname;
	imlib_save_image(path.str().c_str());

	imlib_free_image();
	imlib_context_set_image(*pImageToDisplay);
}

//***************************************************************************
// Get Image Buffer
//***************************************************************************

int ImlibRenderer::getImageBuffer(const void*& buffer, int quality)
{
   int size = 0;

#ifdef HAVE_IMAGE_MAGICK

   int w, h;
   DATA32* p;

   imlib_context_set_image(_cur_image);
   
   p = imlib_image_get_data_for_reading_only();
   w = imlib_image_get_width();
   h = imlib_image_get_height();
   
   if (p)
   {
      Image image(w, h, "BGRA", CharPixel, p);
      
      image.magick("JPEG");       // set format
      image.quality(quality);     // set quality
      image.write(&blob);
      
      buffer = blob.data();
      size = blob.length();
   }

#endif

   return size;
}
