/*
 * This file is part of the apvlv package
 * Copyright (C) <2010>  <Alf>
 *
 * Contact: Alf <naihe2010@126.com>
 *
 * 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.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 */
/* @CPPFILE ApvlvTxt.cc
 *
 *  Author: Alf <naihe2010@126.com>
 */
/* @date Created: 2012/01/16 11:06:35 Alf*/

#include "ApvlvUtil.h"
#include "ApvlvTxt.h"

#include <cmath>

namespace apvlv
{
#define DOUBLE_EQ(a,b)                  (fabs ((a) - (b)) < 0.0001)
#define DOUBLE_NE(a,b)                  (!DOUBLE_EQ(a,b))

  const gint PAGE_CHARACTER_COUNT = 10000;
  const gdouble PAGE_RENDER_ZOOM = 1.0;
  const gint PAGE_RENDER_WIDTH = 200;
  const gint PAGE_RENDER_HEIGHT = 1200;

  ApvlvTxtPage::ApvlvTxtPage (int id, const gchar *str, gsize size)
  {
    mId = id;
    mContent = g_string_new_len (str, size);

    mZoomrate = PAGE_RENDER_ZOOM;
    mRenderBuf = NULL;
    mWidth = mHeight = 0;
  }

  ApvlvTxtPage::~ApvlvTxtPage ()
  {
    if (mContent)
      {
        g_string_free (mContent, TRUE);
      }
    if (mRenderBuf)
      {
        g_free (mRenderBuf);
      }
  }

  static void
  txt_page_free (gpointer v)
  {
    ApvlvTxtPage *p;

    p = (ApvlvTxtPage *) v;
    delete p;
  }

  ApvlvTXT::ApvlvTXT (const char *filename, bool check):ApvlvFile (filename,
								   check)
  {
    gboolean ok;
    gchar *content; 
    const gchar *begin;
    gsize length;

    gchar * lname = g_locale_from_utf8 (filename, -1, NULL, NULL, NULL);
    if (lname)
      {
	ok = g_file_get_contents (lname, &content, &length, NULL);
	g_free (lname);
      }
    else
      {
	ok = g_file_get_contents (filename, &content, &length, NULL);
      }

    if (ok == FALSE)
      {
	throw std::bad_alloc ();
      }

    debug ("total chars: %d", length);
    gchar *content2 = NULL;
    gsize len2;
    if (g_utf8_validate (content, length, &begin) == FALSE
	&& begin == content)
      {
	content2 = g_locale_to_utf8 (content, length, NULL, &len2, NULL);
      }

    if (content2)
      {
	mContent = g_string_new_len (content2, len2);
	g_free (content2);
      }
    else
      {
	mContent = g_string_new_len (content, length);
      }
    g_free (content);

    mLength = g_utf8_strlen (mContent->str, mContent->len);
    debug ("total Character: %d", mLength);

    load_pages ();
  }

  ApvlvTXT::~ApvlvTXT ()
  {
    if (mContent)
      {
	g_string_free (mContent, TRUE);
      }

    if (mPages)
      {
	g_ptr_array_free (mPages, TRUE);
      }
  }

  gboolean
  ApvlvTXT::load_pages ()
  {
    ApvlvTxtPage *page;
    gchar *b, *e;
    gunichar unic;
    gsize i, len;

    mPages = g_ptr_array_new_with_free_func (txt_page_free);
    g_return_val_if_fail (mPages, FALSE);

    for (i = 0, mPageCount = 0;
	 i < mLength;
	 i += len, ++ mPageCount)
      {
	len = (i + PAGE_CHARACTER_COUNT) > mLength ? mLength - i : PAGE_CHARACTER_COUNT;

	b = g_utf8_offset_to_pointer (mContent->str, i);
	e = g_utf8_offset_to_pointer (b, len);
	unic = g_utf8_get_char (e);
	while (g_unichar_isspace (unic) == FALSE && *e != '\0')
	  {
	    ++ len;
	    e = g_utf8_next_char (e);
	    unic = g_utf8_get_char (e);
	  } 
	page = new ApvlvTxtPage (mPageCount, 
				 b,
				 e - b);
	g_ptr_array_add (mPages, page);
      }
    debug ("total page: %d", mPageCount);

    return TRUE;
  }

  bool ApvlvTXT::writefile (const char *filename)
  {
    if (g_file_set_contents (filename, mContent->str, mContent->len, NULL) == TRUE)
      {
	return true;
      }
    return false;
  }

  bool ApvlvTXT::pagesize (int pn, int rot, double *px, double *py)
  {
    ApvlvTxtPage *page;

    page = (ApvlvTxtPage *) g_ptr_array_index (mPages, pn);
    if (page != NULL)
      {
	return page->pagesize (rot, px, py);
      }
    else
      {
	return false;
      }
  }

  bool ApvlvTxtPage::pagesize (int rot, double *px, double *py)
  {
    if (DOUBLE_NE (mZoomrate, PAGE_RENDER_ZOOM))
      {
	if (mRenderBuf)
	  {
	    g_free (mRenderBuf);
	    mRenderBuf = NULL;
	  }
	mZoomrate = PAGE_RENDER_ZOOM;
      }

    if (!mRenderBuf)
      {
	self_render (rot);
      }

    if (!mRenderBuf)
      {
	return false;
      }

    if (px)
      {
	*px = mWidth + 2;
      }
    if (py)
      {
	*py = mHeight + 2;
      }

    return true;
  }

  int ApvlvTXT::pagesum ()
  {
    return mPageCount;
  }

  bool ApvlvTxtPage::render (int ix, int iy, double zm, int rot,
			     GdkPixbuf * pix, char *buffer)
  {
    guint i, j, passed, fromx, fromy, tox, toy;

    if (DOUBLE_NE (zm, mZoomrate))
      {
	if (mRenderBuf)
	  {
	    g_free (mRenderBuf);
	    mRenderBuf = NULL;
	  }
	mZoomrate = zm;
      }

    if (!mRenderBuf)
      {
	self_render (rot);
      }

    if (!mRenderBuf)
      {
	return false;
      }

    fromx = 0;
    fromy = 0;
    tox = (guint) ix > mLayoutWidth ? mLayoutWidth: ix;
    toy = (guint) iy > mLayoutHeight ? mLayoutHeight: iy;

    for (j = fromy; j <= toy; ++j)
      {
	passed = j * mStride;

	for (i = fromx; i <= tox; ++i)
	  {
	    memcpy (buffer + i * 3, mRenderBuf + passed + i * 4 + 1, 3);
	  }
	buffer += 3 * ix;
      }

    return true;
  }

  bool ApvlvTXT::render (int pn, int ix, int iy, double zm, int rot,
			 GdkPixbuf * pix, char *buffer)
  {
    ApvlvTxtPage *page;

    page = (ApvlvTxtPage *) g_ptr_array_index (mPages, pn);
    if (page)
      {
	return page->render (ix, iy, zm, rot, pix, buffer);
      }
    else
      {
	return false;
      }
  }

  gboolean ApvlvTxtPage::self_render (int rot)
  {
    cairo_surface_t *surface;
    cairo_t *cr;
    int wunits, hunits;
    PangoLayout *layout;

    mLayoutWidth = PAGE_RENDER_WIDTH * mZoomrate;
    mLayoutHeight = PAGE_RENDER_HEIGHT * mZoomrate;
    mStride = cairo_format_stride_for_width (CAIRO_FORMAT_RGB24, mLayoutWidth);

    mRenderBuf = (guchar *) g_malloc (mStride * mLayoutHeight);
    g_return_val_if_fail (mRenderBuf, false);

    surface =
      cairo_image_surface_create_for_data (mRenderBuf, CAIRO_FORMAT_RGB24, 
					   mLayoutWidth, 
					   mLayoutHeight, mStride);
    if (surface == NULL)
      {
	g_free (mRenderBuf);
	return false;
      }

    cr = cairo_create (surface);
    if (cr == NULL)
      {
	g_free (mRenderBuf);
	cairo_surface_destroy (surface);
	return false;
      }

    /* init the background */
    cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
    cairo_rectangle (cr, 0, 0, mLayoutWidth, mLayoutHeight);
    cairo_fill (cr);

    cairo_select_font_face (cr,
			    "Sans",
			    CAIRO_FONT_SLANT_NORMAL,
			    CAIRO_FONT_WEIGHT_NORMAL);
    cairo_set_font_size (cr, 20 * mZoomrate);
    cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);

    layout = pango_cairo_create_layout (cr);

    wunits = pango_units_from_double (mLayoutWidth);
    hunits = pango_units_from_double (mLayoutHeight);
    pango_layout_set_width (layout, wunits);
    pango_layout_set_height (layout, hunits);
    pango_layout_set_wrap (layout, PANGO_WRAP_WORD_CHAR);
    pango_layout_set_spacing (layout, 80);
    pango_layout_set_indent (layout, 160);

    pango_layout_set_text (layout, mContent->str, mContent->len);

    pango_cairo_update_layout (cr, layout);
    pango_cairo_show_layout (cr, layout);

    pango_layout_get_pixel_size (layout, &mWidth, &mHeight);
    debug ("txt page size: %d:%d", mWidth, mHeight);

    cairo_surface_destroy (surface);
    cairo_destroy (cr);

    return true;
  }

  bool ApvlvTXT::pageselectsearch (int pn, int ix, int iy, double zm,
				   int rot, GdkPixbuf * pix, char *buffer,
				   int sel, ApvlvPoses * poses)
  {
    return false;
  }

  ApvlvPoses *ApvlvTXT::pagesearch (int pn, const char *str, bool reverse)
  {
    return NULL;
  }

  ApvlvLinks *ApvlvTXT::getlinks (int pn)
  {
    return NULL;
  }

  bool ApvlvTXT::pagetext (int pn, int x1, int y1, int x2, int y2,
			   char **out)
  {
    return false;
  }

  ApvlvFileIndex *ApvlvTXT::new_index ()
  {
    return NULL;
  }

  void ApvlvTXT::free_index (ApvlvFileIndex * index)
  {
  }

  bool ApvlvTXT::pageprint (int pn, cairo_t * cr)
  {
    return false;
  }
}

// Local Variables:
// mode: c++
// End:
