/** @file
 * libLASi provides a C++ output stream interface for writing multi-language Postscript documents.
 * Copyright (C) 2003  Larry Siden.
 * See README file in project root directory for copyright and contact info.
 * See COPYING file in project root for terms of re-distribution.
 */

#include <ostream>
#include <ft2build.h>
#include FT_OUTLINE_H
#include <algorithm>
#include <LASi.h>
#include <config.h>
#include "glyphMgr.h"
#include "util.h"
#include "memory.h"

using namespace std;
using namespace LASi;

static FT_Vector operator + (const FT_Vector& vec1, const FT_Vector& vec2) {
  FT_Vector vec;
  vec.x = vec1.x + vec2.x;
  vec.y = vec1.y + vec2.y;
  return vec;
}

static FT_Vector operator * (const FT_Vector& vec1, const int n) {
  FT_Vector vec2;
  vec2.x = n * vec1.x;
  vec2.y = n * vec1.y;
  return vec2;
}

static FT_Vector operator * (const int n, const FT_Vector& vec1) {
  return operator*(vec1, n);
}

static FT_Vector operator / (const FT_Vector& vec1, const int n) {
  FT_Vector vec2;
  vec2.x = vec1.x / n;
  vec2.y = vec1.y / n;
  return vec2;
}

//static FT_Vector operator / (const int n, const FT_Vector& vec1) {
//  return operator/(vec1, n);
//}

static ostream& operator<<(ostream& os, const FT_Vector& ftVec) {
  os << ftVec.x / 64.0 << " " << ftVec.y / 64.0;
  return os;
}

struct StateOfDrawGlyph {
  private:
    std::ostream& _os;
    FT_Vector _startPt;
    bool _isNewPath;

  public:
    StateOfDrawGlyph(std::ostream& os) 
      : _os(os), _isNewPath(true)
      {
        _startPt.x = 0;
        _startPt.y = 0;
      }

    std::ostream& os() {return _os;}
    
    FT_Vector startPt() {return _startPt;}
    void setStartPt(const FT_Vector pt) {_startPt = pt;}
    
    bool isNewPath() {return _isNewPath;}
    void setNewPathFalse() {_isNewPath = false;}
};

static int xTo(const FT_Vector* pftVec, void* const data, const char* const cmd) {
  StateOfDrawGlyph* const state = reinterpret_cast<StateOfDrawGlyph* const>(data);
  FT_Vector to = *pftVec;
  state->os() << to << " " << cmd << endl;
  state->setStartPt(to);
  return 0;
}

#ifdef HAVE_FT2_2PLUS
static int moveTo(const FT_Vector* pftVec, void* data) {
#else
static int moveTo(FT_Vector* pftVec, void* data) {
#endif
  StateOfDrawGlyph* const state = reinterpret_cast<StateOfDrawGlyph* const>(data);
  state->os() << (state->isNewPath() ? "newpath" : "closepath") << endl;
  state->setNewPathFalse();
  xTo(pftVec, data, "moveto");
  return 0;
}

#ifdef HAVE_FT2_2PLUS
static int lineTo(const FT_Vector* pftVec, void* data) {
#else
static int lineTo(FT_Vector* pftVec, void* data) {
#endif
  xTo(pftVec, data, "lineto");
  return 0;
}

#ifdef HAVE_FT2_2PLUS
static int cubicTo(const FT_Vector* ctrlPt1, const FT_Vector* ctrlPt2, const FT_Vector* pEndPt, void* data) {
#else
static int cubicTo(FT_Vector* ctrlPt1, FT_Vector* ctrlPt2, FT_Vector* pEndPt, void* data) {
#endif
  StateOfDrawGlyph* const state = reinterpret_cast<StateOfDrawGlyph* const>(data);
  state->os() << *ctrlPt1 << " " << *ctrlPt2 << " " << *pEndPt << " curveto" << endl;
  state->setStartPt(*pEndPt);
  return 0;
}

#ifdef HAVE_FT2_2PLUS
static int conicTo(const FT_Vector* pCtrlPt, const FT_Vector* pEndPt, void* data) {
#else
static int conicTo(FT_Vector* pCtrlPt, FT_Vector* pEndPt, void* data) {
#endif
  StateOfDrawGlyph* const state = reinterpret_cast<StateOfDrawGlyph* const>(data);
  FT_Vector ctrlPt1 = (state->startPt() + 2 * *pCtrlPt) / 3;
  FT_Vector ctrlPt2 = (*pEndPt + 2 * *pCtrlPt) / 3;
  return cubicTo(&ctrlPt1, &ctrlPt2, pEndPt, data);
}

static const FT_Outline_Funcs outlineFuncs = {moveTo, lineTo, conicTo, cubicTo};

void PostscriptDocument::write_glyph_routine_to_stream::operator()(PostscriptDocument::GlyphMap::value_type v) 
{
  const GlyphId& glyphId = v.first;
  const FreetypeGlyphMgr& glyphMgr = v.second;
  const FT_Glyph glyph = static_cast<FT_Glyph>(glyphMgr);
  const ios::fmtflags oldflags = os.setf(ios::showpos);
  const int oldPrecision = os.precision();
  os.precision(4);

  os << endl << '/' << glyphId.str() << " {" << endl;
  os << "/myFontsize exch def" << endl;  // bind argument to name

  // calculate drawing scale factor
  os << "/scalefactor myFontsize " << PostscriptDocument::DRAWING_SCALE << " div def" << endl;  // divide myFontsize by DRAWING_SCALE
  os << "gsave" << endl;
  os << "currentpoint translate" << endl; // put x, y coordinates of current point on top of stack
  os << "scalefactor dup scale" << endl; // set the x and y scales in the PS transformation matrix

  StateOfDrawGlyph state(os);
  if (glyph->format == FT_GLYPH_FORMAT_OUTLINE) {
  evalReturnCode(
      FT_Outline_Decompose(
        &reinterpret_cast<FT_OutlineGlyph>(glyph)->outline, &outlineFuncs, &state),
      "FT_Outline_Decompose");
  
  //
  // 2006.05.01.ET BUG FIX: Various "space" characters
  // don't actually draw anything, so "fill" can produce 
  // undesired artifacts on pre-existing paths ...
  //
  if (!state.isNewPath()) {
    os << "fill" << endl;
  }
  }
  os << "grestore" << endl;

  // advance position by width of character (in 16.16 format)
  os << "scalefactor " << glyph->advance.x / (double)0x10000 << " mul 0 rmoveto" << endl;
  os << "} def" << endl;

  // restore os flags and precision
  os.flags(oldflags);
  os.precision(oldPrecision);
}
