/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
 * Version: NPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.1 (the "License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the License at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is sila.mozdev.org code.
 *
 * The Initial Developer of the Original Code is 
 * Keith Stribley.
 * Portions created by the Initial Developer are Copyright (C) 2005
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the NPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the NPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */

#include <X11/Xft/Xft.h>
#include <math.h>
#include "prtypes.h"
#include "nsAutoDrawSpecBuffer.h"
#include "sila.h"
#include "nsXftPainterFactory.h"
#include <graphite/SegmentAux.h>

using namespace gr;

class nsXftSegmentPainter : public gr::SegmentPainter
{
public:
  nsXftSegmentPainter(gr::Segment * pseg, XftFont * pFont, XftDraw * pDraw,
                       nsAutoDrawSpecBuffer & adsBuffer, 
                       float xsOrigin = 0, float ysOrigin = 0)
    : SegmentPainter(pseg, xsOrigin, ysOrigin), 
      mpFont(pFont), mpDraw(pDraw), 
      mDrawColor(0), mAdsBuffer(adsBuffer)
  {
    nsColor2XftColor(mDrawColor, mXftColor);
    mAdsBuffer.SetColor(&mXftColor);
  }
  virtual ~nsXftSegmentPainter() {mAdsBuffer.Flush();};
  virtual void paint();
  
protected:
  typedef std::pair< gr::GlyphIterator, gr::GlyphIterator > GlyphRange;
  
  virtual void drawRect(gr::Rect r, nscolor color);
  virtual void drawGlyph(FT_UInt r, float x, float y, nscolor color);
  void nsColor2XftColor(const nscolor aColor, XftColor & xftColor);
  int roundCoord(float fCoord) 
  { 
    return static_cast<int>(roundf(fCoord)); 
  };
  int roundCoordUp(float fCoord) 
  { 
    return static_cast<int>(ceilf(fCoord)); 
  };
  int roundCoordDown(float fCoord) 
  { 
    return static_cast<int>(floorf(fCoord)); 
  };
  XftFont * mpFont;
  XftDraw * mpDraw;
  XftColor mXftColor;
  nscolor mDrawColor;
  nsAutoDrawSpecBuffer & mAdsBuffer;
};

void
nsXftSegmentPainter::paint()
{
  GlyphRange gRange =  m_pseg->glyphs();
  GlyphIterator gi = gRange.first;
  int foreground = 0;
  int background = 0;
  int prevBackground = 0;
  IColorTextSource & textSrc = reinterpret_cast<IColorTextSource&>(m_pseg->getText());
  // First draw the backgrounds for each character
  // In mozilla there should only be one background colour which is only set
  // for selections. Real backgrounds are rendered separately. However, this
  // code should work even if the background does change for some reason.
  Rect selRect;
  PRBool selRectDrawn = PR_TRUE;
  // This implementation always renders the background full height, even if 
  // one glyph in a vertical stack has the background transparent and another
  // has it set.
  // Heights are measured downwards, hence the negative sign here
  selRect.top = - m_pseg->getFont().ascent();
  selRect.bottom = m_pseg->getFont().descent(); 
  while (gi != gRange.second)
  {
    GlyphInfo gInfo = *gi;
    if (!gInfo.isSpace())
    {
      textSrc.getColors(gInfo.firstChar(), &foreground, &background);
      if (unsigned(background) != kclrTransparent)
      {
        if (!selRectDrawn)
        {
          // 1 pixel gaps look bad, so check a bit more loosely
          if (prevBackground != background && 
              gInfo.bb().left > selRect.right + 1.0f)
          {
            // discontinuous
            // draw current rect
            drawRect(selRect, prevBackground);
            selRect.left = gInfo.bb().left;
            selRect.right = gInfo.bb().right;
            selRectDrawn = PR_FALSE;
            //prevBackground = background;
          }
          else // append to existing selection rectangle
          {
            NS_ASSERTION(prevBackground == background, "background changed color unexpectedly");
            selRect.left = fminf(gInfo.bb().left, selRect.left);
            selRect.right = fmaxf(gInfo.bb().right, selRect.right);
            selRectDrawn = PR_FALSE;
          }    
        }  
        else 
        {
          selRect.left = gInfo.bb().left;
          selRect.right = gInfo.bb().right;
          selRectDrawn = PR_FALSE;
          //prevBackground = background;
        }    
      }
      prevBackground = background;
    }
    printf("x%f %f-%f y%f %f-%f a%f d%f\n", gInfo.origin(), gInfo.bb().left, gInfo.bb().right, 
           gInfo.yOffset(), gInfo.bb().top, gInfo.bb().bottom, 
           m_pseg->getFont().ascent(), m_pseg->getFont().descent());
    ++gi;
  }
  if (selRectDrawn == PR_FALSE)
    drawRect(selRect, prevBackground);
  // now draw the characters on top
  gi = gRange.first;
  //FT_Face face = XftLockFace(mpFont);
  while (gi != gRange.second)
  {
    GlyphInfo gInfo = *gi;
    if (!gInfo.isSpace())
    {
      textSrc.getColors(gInfo.firstChar(), &foreground, &background);
      drawGlyph(gInfo.glyphID(), gInfo.origin(), 
                gInfo.yOffset(), foreground);
    }
    ++gi;
  } 
  //XftUnlockFace(mpFont);
}

void nsXftSegmentPainter::nsColor2XftColor(const nscolor aColor, XftColor & xftColor)
{
  xftColor.pixel = 0;
  xftColor.color.red = (NS_GET_R(aColor) << 8) | NS_GET_R(aColor);
  xftColor.color.green = (NS_GET_G(aColor) << 8) | NS_GET_G(aColor);
  xftColor.color.blue = (NS_GET_B(aColor) << 8) | NS_GET_B(aColor);
  xftColor.color.alpha = 0xff00;//0xffff;
  printf("%x %x %x %x\n",xftColor.color.red, xftColor.color.green, 
    xftColor.color.blue, xftColor.color.alpha);
}


void
nsXftSegmentPainter::drawRect(Rect r, nscolor color)
{
  XftColor xftColor;
  nsColor2XftColor(color, xftColor);
  int x = roundCoordDown(ScaleXToDest(r.left));
  int xRight = roundCoordUp(ScaleXToDest(r.right));
  int y = roundCoord(ScaleYToDest(r.top));
  int width = xRight - x;
  //int width = roundCoordUp(ScaleXToDest(r.right) - ScaleXToDest(r.left));
  int height = roundCoord(- ScaleYToDest(r.top) + ScaleYToDest(r.bottom));
  printf("rect x%d+%d y%d+%d\n", x, width, y, height);
  XftDrawRect(mpDraw, &xftColor, x, y, width, height);
}

void
nsXftSegmentPainter::drawGlyph(FT_UInt gid, float x, float y, nscolor aColor)
{
  int xPos = roundCoord(ScaleXToDest(x));
  int yPos = roundCoord(ScaleYToDest(y));
  //xPos = (int)x; yPos = (int)y;
  printf("%d x%f %d y%f %d\n", gid, x, xPos, y, yPos);
  if (mDrawColor != aColor)
  {
    mAdsBuffer.Flush(); // flush buffer before changing colour 
    // since only a pointer to the color is stored
    nsColor2XftColor(aColor, mXftColor);
    mAdsBuffer.SetColor(&mXftColor);
    mDrawColor = aColor;
  }
  mAdsBuffer.Draw(xPos, yPos, mpFont, gid);
}

SegmentPainter * 
nsXftPainterFactory::getPainter(gr::Segment * aSeg)
{
  NS_ASSERTION(mpDraw, "no draw");
  NS_ASSERTION(mpAdsBuffer, "no Draw spec buffer");
  nsXftSegmentPainter * painter = new nsXftSegmentPainter(aSeg, mpFont, mpDraw, *mpAdsBuffer);
  if (!painter) return nsnull;
  return painter;
}

