
#include <cstring>

// TnzCore includes
#include "tsystem.h"
#include "tthread.h"
#include "tstopwatch.h"

#include "tvectorgl.h"
#include "tvectorrenderdata.h"
#include "tvectorimage.h"
#include "trasterimage.h"
#include "tlevel_io.h"
#include "tstrokeprop.h"
#include "ttessellator.h"
#include "tcolorfunctions.h"
#include "tofflinegl.h"
#include "drawutil.h"
#include "trop.h"
#include "tstencilcontrol.h"
#include "tpalette.h"
#include "tpixelutils.h"
#include "tregionprop.h"
#include "tcurves.h"
#include "tcurveutil.h"
#include "tstrokeoutline.h"
#include "tregion.h"

#include "tsimplecolorstyles.h"

#ifndef _WIN32
#define CALLBACK
#endif

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

TFilePath TRasterImagePatternStrokeStyle::m_rootDir = TFilePath();
TFilePath TVectorImagePatternStrokeStyle::m_rootDir = TFilePath();

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

#ifndef NDEBUG
static void checkGlError() {
  GLenum err = glGetError();
  assert(err != GL_INVALID_ENUM);
  assert(err != GL_INVALID_VALUE);
  assert(err != GL_INVALID_OPERATION);
  assert(err != GL_STACK_OVERFLOW);
  assert(err != GL_STACK_UNDERFLOW);
  assert(err != GL_OUT_OF_MEMORY);
  assert(err == GL_NO_ERROR);
}

#define CHECK_GL_ERROR checkGlError();
#else
#define CHECK_GL_ERROR
#endif

// Use GL_LINE_STRIP to debug the centerline-to-outline algorithm
#define QUAD_PRIMITIVE GL_QUAD_STRIP

//*************************************************************************************
//    Local namespace  stuff
//*************************************************************************************

namespace {

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

// se ras ha le dimensioni giuste (potenze di due) ritorna ras. Altrimenti
// crea il piu' piccolo raster con le dimensioni giuste che contenga ras e
// copia il contenuto (scalandolo)
// se il raster di partenza e' vuoto, non e' ras32 o e' piu' piccolo di 2x2
// ritorna un raster vuoto
TRaster32P makeTexture(const TRaster32P &ras) {
  if (!ras || ras->getLx() < 2 || ras->getLy() < 2) return TRaster32P();
  TRaster32P ras32 = ras;
  if (!ras32) return TRaster32P();
  TDimension d(2, 2);
  while (d.lx < 256 && d.lx * 2 <= ras32->getLx()) d.lx *= 2;
  while (d.ly < 256 && d.ly * 2 <= ras32->getLy()) d.ly *= 2;
  if (d == ras32->getSize())
    return ras32;
  else {
    TRaster32P texture(d);
    TScale sc((double)d.lx / ras32->getLx(), (double)d.ly / ras32->getLy());
    TRop::resample(texture, ras32, sc);
    return texture;
  }
}

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

class OutlineBuilder {
private:
  double width;
  int stage;
  double stages[3][3];
  double px, py, pz;
  double ax, ay;

  inline void next_stage(const double &x, const double &y, const double &z) {
    stages[stage][0] = x;
    stages[stage][1] = y;
    stages[stage][2] = z;
    ++stage;
  }

public:
  std::vector<double> vertices;

  explicit OutlineBuilder(double width, int count = 0)
      : width(width), stage(0), px(), py(), pz(), ax(), ay() {
    memset(stages, 0, sizeof(stages));
    vertices.reserve(12 * (count + 2));
  }

  void add(double x, double y, double z = 0.0) {
    const double maxl = 4.0;

    if (stage == 0)
      next_stage(x, y, z);
    else {
      double bx = x - px;
      double by = y - py;
      double lb = sqrt(bx * bx + by * by);
      if (lb < 1e-9) return;
      bx = width * bx / lb;
      by = width * by / lb;

      if (stage == 1)
        next_stage(x, y, z);
      else {
        if (stage == 2) next_stage(x, y, z);
        double l = fabs(ax + bx) > 1e-9   ? -(ay - by) / (ax + bx)
                   : fabs(ay + by) > 1e-9 ? (ax - bx) / (ay + by)
                                          : 0.0;
        if (fabs(l) > maxl || fabs(l) < 1.0 || l > 0.0) {
          vertices.resize(vertices.size() + 12);
          double *p = &vertices.back() - 11;
          p[0]      = px;
          p[1]      = py;
          p[2]      = pz;
          p[3]      = px + ay;
          p[4]      = py - ax;
          p[5]      = pz;
          p[6]      = px;
          p[7]      = py;
          p[8]      = pz;
          p[9]      = px + by;
          p[10]     = py - bx;
          p[11]     = pz;
        } else {
          vertices.resize(vertices.size() + 6);
          double *p = &vertices.back() - 5;
          p[0]      = px;
          p[1]      = py;
          p[2]      = pz;
          p[3]      = px - l * bx + by;
          p[4]      = py - l * by - bx;
          p[5]      = pz;
        }
      }

      ax = bx;
      ay = by;
    }

    px = x;
    py = y;
    pz = z;
  }

  void finish() {
    for (int i = 0; i < stage; ++i)
      add(stages[i][0], stages[i][1], stages[i][2]);
    stage = 0;
  }

  double get_width() const { return width; }

  void restart(double width) {
    this->width = width;
    stage       = 0;
    vertices.clear();
  }

  void invert() { restart(-width); }
};

void vector4_div(double *v) {
  double k = fabs(v[3]) > 1e-9 ? 1.0 / v[3] : 0.0;
  v[0] *= k, v[1] *= k, v[2] *= k;
  v[3] = 1.0;
}

void matrix4_x_vector4(double *dv, const double *m, const double *v) {
  dv[0] = m[0] * v[0] + m[4] * v[1] + m[8] * v[2] + m[12] * v[3];
  dv[1] = m[1] * v[0] + m[5] * v[1] + m[9] * v[2] + m[13] * v[3];
  dv[2] = m[2] * v[0] + m[6] * v[1] + m[10] * v[2] + m[14] * v[3];
  dv[3] = m[3] * v[0] + m[7] * v[1] + m[11] * v[2] + m[15] * v[3];
}

void matrix4_x_matrix4(double *dm, const double *ma, const double *mb) {
  matrix4_x_vector4(dm + 0, ma, mb + 0);
  matrix4_x_vector4(dm + 4, ma, mb + 4);
  matrix4_x_vector4(dm + 8, ma, mb + 8);
  matrix4_x_vector4(dm + 12, ma, mb + 12);
}

class AntialiasingOutlinePainter : public OutlineBuilder {
private:
  double matrix[16];
  double projection_matrix[16];
  double modelview_matrix[16];
  double anti_viewport_matrix[16];

public:
  explicit AntialiasingOutlinePainter(int count = 0) : OutlineBuilder(1.0, 0) {
    memset(matrix, 0, sizeof(matrix));
    memset(projection_matrix, 0, sizeof(projection_matrix));
    memset(modelview_matrix, 0, sizeof(modelview_matrix));
    memset(anti_viewport_matrix, 0, sizeof(anti_viewport_matrix));

    // read transformations
    double viewport[4] = {};
    glGetDoublev(GL_VIEWPORT, viewport);
    glGetDoublev(GL_PROJECTION_MATRIX, projection_matrix);
    glGetDoublev(GL_MODELVIEW_MATRIX, modelview_matrix);

    double viewport_matrix[16] = {};
    viewport_matrix[0]         = 0.5 * viewport[2];
    viewport_matrix[5]         = 0.5 * viewport[3];
    viewport_matrix[10]        = 1.0;
    viewport_matrix[12]        = 0.5 * viewport[2];
    viewport_matrix[13]        = 0.5 * viewport[3];
    viewport_matrix[15]        = 1.0;

    anti_viewport_matrix[0]  = 2.0 / viewport[2];
    anti_viewport_matrix[5]  = 2.0 / viewport[3];
    anti_viewport_matrix[10] = 1.0;
    anti_viewport_matrix[12] = -1.0;
    anti_viewport_matrix[13] = -1.0;
    anti_viewport_matrix[15] = 1.0;

    {
      double tmp[16] = {};
      matrix4_x_matrix4(tmp, projection_matrix, modelview_matrix);
      matrix4_x_matrix4(matrix, viewport_matrix, tmp);
    }
  }

  void add(double x, double y, double z = 0.0) {
    double dest[4], src[4] = {x, y, z, 1.0};
    matrix4_x_vector4(dest, matrix, src);
    vector4_div(dest);
    OutlineBuilder::add(dest[0], dest[1], dest[2]);
  }

  void finish() {
    OutlineBuilder::finish();
    if (vertices.empty()) return;
    int count = (int)vertices.size() / 6;

    // prepare colors
    float color[8] = {};
    glGetFloatv(GL_CURRENT_COLOR, color);
    memcpy(color + 4, color, 3 * sizeof(float));
    std::vector<float> colors(8 * count);
    for (float *c = &colors[0], *ce = &colors.back(); c < ce; c += 8)
      memcpy(c, color, sizeof(color));

    // draw
    glMatrixMode(GL_MODELVIEW);
    glLoadMatrixd(anti_viewport_matrix);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glEnableClientState(GL_VERTEX_ARRAY);
    glEnableClientState(GL_COLOR_ARRAY);

    glVertexPointer(3, GL_DOUBLE, 0, &vertices.front());
    glColorPointer(4, GL_FLOAT, 0, &colors.front());
    glDrawArrays(GL_TRIANGLE_STRIP, 0, 2 * count);

    glDisableClientState(GL_VERTEX_ARRAY);
    glDisableClientState(GL_COLOR_ARRAY);
    glLoadMatrixd(projection_matrix);
    glMatrixMode(GL_MODELVIEW);
    glLoadMatrixd(modelview_matrix);

    glColor4fv(color);
    restart(get_width());
  }
};

void drawAntialiasedOutline(const std::vector<TOutlinePoint> &_v,
                            const TStroke *stroke) {
  if (_v.empty()) return;

  const TOutlinePoint *begin = &_v.front(), *end = &_v.back();
  assert(_v.size() % 2 == 0);

  AntialiasingOutlinePainter outline(_v.size());
  for (const TOutlinePoint *i = begin; i < end; i += 2) outline.add(i->x, i->y);
  for (const TOutlinePoint *i = end; i > begin; i -= 2) outline.add(i->x, i->y);
  outline.finish();

  outline.invert();
  for (const TOutlinePoint *i = begin; i < end; i += 2) outline.add(i->x, i->y);
  for (const TOutlinePoint *i = end; i > begin; i -= 2) outline.add(i->x, i->y);
  outline.finish();
}

void drawAliasedOutline(const std::vector<TOutlinePoint> &_v,
                        const TStroke *stroke) {
  static const int stride = 2 * sizeof(TOutlinePoint);

  glEnableClientState(GL_VERTEX_ARRAY);

  glVertexPointer(2, GL_DOUBLE, stride, &_v[0]);
  glDrawArrays(GL_LINE_STRIP, 0, _v.size() / 2);

  glVertexPointer(2, GL_DOUBLE, stride, &_v[1]);
  glDrawArrays(GL_LINE_STRIP, 0, _v.size() / 2);

  glDisableClientState(GL_VERTEX_ARRAY);
}

}  // namespace

//*************************************************************************************
//    DisplayListManager  definition
//*************************************************************************************

#ifdef _WIN32

namespace {

class DisplayListManager  // singleton
{
public:
  TThread::Mutex m_mutex;
  typedef HGLRC T_hGlContext;

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

  struct Key {
    std::string m_name;
    TFrameId m_fid;
    T_hGlContext m_glContext;
    Key(std::string name, TFrameId fid, T_hGlContext glContext)
        : m_name(name), m_fid(fid), m_glContext(glContext) {}
  };

  //----------------------------------------------
  struct Info {
    GLuint m_displayListId;
    TColorFunction::Parameters m_parameters;
    HDC m_hdc;
    Info()
        : m_displayListId(0)
        , m_hdc(0)
        , m_parameters(TColorFunction::Parameters()) {}

    Info(GLuint displayListId, HDC hdc, TColorFunction::Parameters parameters)
        : m_displayListId(displayListId)
        , m_hdc(hdc)
        , m_parameters(parameters) {}

    // se ad una stessa chiave corrispondono due displayInfo diversi (second
    // questo metodo),
    // si aggiorna la displayList secondo l'ultima chiamata.
    // Serve per non duplicare troppe liste, per parametri che non cambiano
    // spesso, come le cf.
    bool isDrawnAs(const Info &linfo) {
      return (this->m_parameters.m_mR == linfo.m_parameters.m_mR &&
              this->m_parameters.m_mG == linfo.m_parameters.m_mG &&
              this->m_parameters.m_mB == linfo.m_parameters.m_mB &&
              this->m_parameters.m_mM == linfo.m_parameters.m_mM &&
              this->m_parameters.m_cR == linfo.m_parameters.m_cR &&
              this->m_parameters.m_cG == linfo.m_parameters.m_cG &&
              this->m_parameters.m_cB == linfo.m_parameters.m_cB &&
              this->m_parameters.m_cM == linfo.m_parameters.m_cM);
    }
  };
  //----------------------------------------------

  struct KeyLess final {
    bool operator()(const Key &d1, const Key &d2) const {
        return d1.m_glContext < d2.m_glContext ||
               (d1.m_glContext == d2.m_glContext &&
                (d1.m_fid < d2.m_fid ||
                 d1.m_fid == d2.m_fid && d1.m_name < d2.m_name));
    }
};

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

  typedef std::map<Key, Info, KeyLess> DisplayListMap;

private:
  DisplayListMap m_displayListMap;

  DisplayListManager() {}

  void recordList(GLuint listId, TVectorImage *image,
                  const TVectorRenderData &rd) {
    QMutexLocker sl(&m_mutex);
    TRectD bbox = image->getBBox();
    double ry   = bbox.getLy() * 0.5;
    if (ry < 1e-5) ry = 1e-5;
    TAffine identity;
    TRect rect;
    glPushMatrix();

    glLoadIdentity();
    // forzo pixelSize come se si disegnasse su una stroke con thickness=10
    // tglMultMatrix(TScale(10.0/ry));

    // forzo pixelSize a 20.
    // In questo modo, un'immagine piu' grande (nel pli
    // che definisce il custom style) viene piu' definita di una piu' piccola,
    // anche se appaiono della stessa dimensione (che dipende esclusivamente
    // dalla thickness)
    // tglMultMatrix(TScale(0.50));

    TPalette *vPalette = image->getPalette();
    assert(vPalette);

    TVectorRenderData rd2(identity, rect, vPalette, rd.m_cf, true, true);
    rd2.m_isOfflineRender = rd.m_isOfflineRender;
    rd2.m_isImagePattern  = true;

    CHECK_GL_ERROR
    glNewList(listId, GL_COMPILE);
    CHECK_GL_ERROR
    tglDraw(rd2, image);
    CHECK_GL_ERROR
    glEndList();
    CHECK_GL_ERROR
    glPopMatrix();
    CHECK_GL_ERROR
  }

public:
  static DisplayListManager *instance() {
    static DisplayListManager singleton;
    return &singleton;
  }
  //--------------------------------------------

  GLuint getDisplayListId(TVectorImage *image, std::string name, TFrameId fid,
                          const TVectorRenderData &rd) {
    QMutexLocker sl(&m_mutex);
    TColorFunction::Parameters parameters;
    if (rd.m_cf) rd.m_cf->getParameters(parameters);
    CHECK_GL_ERROR

    HDC hDC               = wglGetCurrentDC();
    T_hGlContext hContext = wglGetCurrentContext();
    Info listInfo(0, hDC, parameters);
    Key key(name, fid, hContext);
    DisplayListMap::iterator it = m_displayListMap.find(key);
    if (it != m_displayListMap.end()) {
      if (listInfo.isDrawnAs(it->second)) {
        // lascia tutto com'e' ed utilizza la displaliList
        listInfo.m_displayListId = it->second.m_displayListId;
      } else {  // aggiorna la displayList con le nuove info (attualmente solo
                // la cf)
        listInfo.m_displayListId = it->second.m_displayListId;
        recordList(listInfo.m_displayListId, image, rd);
        m_displayListMap[key] = listInfo;
      }
    } else  // se non ho mai disegnato l'immagine creo la displayList
    {
      CHECK_GL_ERROR
      listInfo.m_displayListId = glGenLists(1);

      if (listInfo.m_displayListId != 0)
        recordList(listInfo.m_displayListId, image, rd);
      else
        std::cout << "list of " << name << fid << " is 0." << std::endl;

      CHECK_GL_ERROR
      m_displayListMap[key] = listInfo;
    }
    return listInfo.m_displayListId;
  }

  void clearLists() {
    // save current context and others
    HDC hCurrentDC               = wglGetCurrentDC();
    T_hGlContext hCurrentContext = wglGetCurrentContext();
    // free all display lists
    DisplayListMap::iterator it = m_displayListMap.begin();
    for (; it != m_displayListMap.end(); ++it) {
      wglMakeCurrent(it->second.m_hdc, it->first.m_glContext);
      glDeleteLists(it->second.m_displayListId, 1);
    }
    m_displayListMap.clear();
    // restore current context

    wglMakeCurrent(hCurrentDC, hCurrentContext);
  }
};

}  // namespace

#endif  // _WIN32

//*************************************************************************************
//    TSimpleStrokeStyle  implementation
//*************************************************************************************

TStrokeProp *TSimpleStrokeStyle::makeStrokeProp(const TStroke *stroke) {
  return new TSimpleStrokeProp(stroke, this);
}

//*************************************************************************************
//    TOutlineStyle  implementation
//*************************************************************************************

TOutlineStyle::TOutlineStyle() : m_regionOutlineModifier(0) {}

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

TOutlineStyle::TOutlineStyle(const TOutlineStyle &other)
    : TColorStyle(other)
    , m_regionOutlineModifier(other.m_regionOutlineModifier
                                  ? other.m_regionOutlineModifier->clone()
                                  : 0) {}

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

TOutlineStyle::~TOutlineStyle() { delete m_regionOutlineModifier; }

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

void TOutlineStyle::setRegionOutlineModifier(RegionOutlineModifier *modifier) {
  delete m_regionOutlineModifier;
  m_regionOutlineModifier = modifier;
}

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

TStrokeProp *TOutlineStyle::makeStrokeProp(const TStroke *stroke) {
  return new OutlineStrokeProp(stroke, this);
}

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

TRegionProp *TOutlineStyle::makeRegionProp(const TRegion *region) {
  return new OutlineRegionProp(region, this);
}

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

void TOutlineStyle::computeOutline(const TStroke *stroke,
                                   TStrokeOutline &outline,
                                   TOutlineUtil::OutlineParameter param) const {
  TOutlineUtil::makeOutline(*stroke, outline, param);
}

//*************************************************************************************
//    TSolidColorStyle  implementation
//*************************************************************************************

TSolidColorStyle::TSolidColorStyle(const TPixel32 &color)
    : m_color(color), m_tessellator(new TglTessellator) {}

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

TSolidColorStyle::TSolidColorStyle(const TSolidColorStyle &other)
    : TOutlineStyle(other)
    , m_color(other.m_color)
    , m_tessellator(new TglTessellator) {}

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

TSolidColorStyle::~TSolidColorStyle() { delete m_tessellator; }

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

TColorStyle *TSolidColorStyle::clone() const {
  return new TSolidColorStyle(*this);
}

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

void TSolidColorStyle::makeIcon(const TDimension &size) {
  /*-- TSolidColorStyle, TColorCleanupStyle, TBlackCleanupStyle --*/
  if (getTagId() != 3 && getTagId() != 2001 &&
      getTagId() !=
          2002)  // e' un'istanza di una classe derivata da TSolidColorStyle
  {
    TColorStyle::makeIcon(size);
  } else {
    if (!m_icon || m_icon->getSize() != size) {
      TRaster32P ras(size);
      m_icon = ras;
    }
    TPixel32 col = m_color;
    if (col.m == 255)
      m_icon->fill(col);
    else {
      TRaster32P fg(size);
      fg->fill(premultiply(col));
      TRop::checkBoard(m_icon, TPixel32::Black, TPixel32::White,
                       TDimensionD(6, 6), TPointD());
      TRop::over(m_icon, fg);
    }
  }
}

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

void TSolidColorStyle::drawRegion(const TColorFunction *cf,
                                  const bool antiAliasing,
                                  TRegionOutline &boundary) const {
  m_tessellator->tessellate(cf, antiAliasing, boundary, m_color);
}

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

//=============================================================================

void TSolidColorStyle::doDrawStroke(const TColorFunction *cf,
                                    TStrokeOutline *outline,
                                    const TStroke *stroke,
                                    bool antialias) const {
  struct locals {
    static inline void fillOutlinedStroke(const std::vector<TOutlinePoint> &v) {
      static const int stride = sizeof(TOutlinePoint);
      glEnableClientState(GL_VERTEX_ARRAY);
      glVertexPointer(2, GL_DOUBLE, stride, &v[0]);
      glDrawArrays(QUAD_PRIMITIVE, 0, v.size());
      glDisableClientState(GL_VERTEX_ARRAY);
    }
  };  // locals

  TPixel32 color = m_color;
  if (cf) color = (*cf)(color);

  if (color.m == 0) return;

  tglColor(color);

  const std::vector<TOutlinePoint> &v = outline->getArray();

  if (v.empty()) return;

  if (true)  // color.m != 254) // !dummyStyle)
  {
    if (color.m < 255) {
      TStencilControl *stencil = TStencilControl::instance();

      /*
// invertendo l'ordine tra disegno interno e contorno, l'antialiasing
// peggiora un po'. Piu che altro, il mangiucchiamento che faceva su alcune
macchine,
// ma solo in drawOnScreen, ora lo fa anche quando si renderizza offline
stencil->beginMask(TStencilControl::DRAW_ON_SCREEN_ONLY_ONCE);
// center line
fillOutlinedStroke(v);
// outline with antialiasing
drawAntialiasedOutline(v);
stencil->endMask();
*/

      stencil->beginMask(TStencilControl::DRAW_ON_SCREEN_ONLY_ONCE);
      locals::fillOutlinedStroke(v);
      stencil->endMask();
      stencil->enableMask(TStencilControl::SHOW_OUTSIDE);

      if (antialias)
        drawAntialiasedOutline(v, stroke);
      else
        drawAliasedOutline(v, stroke);

      stencil->disableMask();

    } else {
      // outline with antialiasing
      if (antialias)
        drawAntialiasedOutline(v, stroke);
      else
        drawAliasedOutline(v, stroke);

      // center line
      locals::fillOutlinedStroke(v);
    }

  } else {
    int i = 0;
    for (i = 0; i < stroke->getControlPointCount(); i++) {
      TThickPoint p = stroke->getControlPoint(i);
      if (i % 2) {
        tglColor(TPixel32(255, 0, 0));
        tglDrawCircle(p, p.thick);
      } else {
        tglColor(TPixel32(255, 200, 200));
        tglDrawDisk(p, p.thick);
        tglColor(TPixel32(0, 255, 0));
        tglDrawCircle(p, p.thick);
      }
      tglColor(TPixel32(127, 127, 127));
      tglDrawDisk(p, 0.3);
    }
    tglColor(TPixel32(127, 127, 127));
    glBegin(GL_LINE_STRIP);
    for (i = 0; i < stroke->getControlPointCount(); i++)
      tglVertex(stroke->getControlPoint(i));
    glEnd();

    tglColor(TPixel32(0, 0, 0));
    drawAntialiasedOutline(v, stroke);
  }
}

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

QString TSolidColorStyle::getDescription() const { return "SolidColorStyle"; }

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

std::string TSolidColorStyle::getBrushIdName() const {
  return "SolidColorStyle";
}

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

std::size_t TSolidColorStyle::staticBrushIdHash() {
  static std::size_t bidHash = TColorStyle::generateHash("SolidColorStyle");
  return bidHash;
}

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

int TSolidColorStyle::getTagId() const { return 3; }

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

void TSolidColorStyle::loadData(TInputStreamInterface &is) {
  TPixel32 color;
  is >> color;
  m_color = color;
}

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

void TSolidColorStyle::saveData(TOutputStreamInterface &os) const {
  os << m_color;
}

//*************************************************************************************
//    TCenterLineStrokeStyle  implementation
//*************************************************************************************

TCenterLineStrokeStyle::TCenterLineStrokeStyle(const TPixel32 &color,
                                               USHORT stipple, double width)
    : m_color(color), m_stipple(stipple), m_width(width) {}

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

TColorStyle *TCenterLineStrokeStyle::clone() const {
  return new TCenterLineStrokeStyle(*this);
}

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

void TCenterLineStrokeStyle::drawStroke(const TColorFunction *cf,
                                        const TStroke *stroke) const {
  if (!stroke) return;

  // center line

  int i, n = stroke->getChunkCount();
  if (n == 0) return;
  TPixel32 color = m_color;
  if (cf) color = (*cf)(m_color);
  if (color.m == 0) return;
  tglColor(color);
  double pixelSize = sqrt(tglGetPixelSize2());

  if (m_stipple != 0) {
    glEnable(GL_LINE_STIPPLE);
    glLineStipple(1, m_stipple);
  }

  const TThickQuadratic *q = 0;
  q                        = stroke->getChunk(0);
  GLfloat rangeValue[2];
  glGetFloatv(GL_LINE_WIDTH_RANGE, &rangeValue[0]);

  float width = float(m_width);

  if (width < rangeValue[0])
    width = rangeValue[0];
  else if (width > rangeValue[1])
    width = rangeValue[1];

  width = 2 * width;

  if (width <= 1.0) {
    if (m_width != 0.0) width /= pixelSize;

    if (width ==
        0.F)  // http://www.opengl.org/sdk/docs/man/xhtml/glLineWidth.xml
      width = 0.00001;

    glLineWidth(width);

    glBegin(GL_LINE_STRIP);
    for (i = 0; i < n; i++) {
      q           = stroke->getChunk(i);
      double step = computeStep(*q, pixelSize);
      for (double t = 0; t < 1; t += step) tglVertex(q->getPoint(t));
    }
    assert(q);
    tglVertex(q->getP2());
    glEnd();
  } else {
    double s      = 0;
    double length = stroke->getLength();
    TPointD pos1, pos4;
    std::vector<TPointD> pv;
    width = width / 2.;
    while (s <= length) {
      double step = 1.0;
      double w    = stroke->getParameterAtLength(s);
      if (w <= 0) {
        s += 0.01;
        continue;
      }  // per tamponare il baco della getParameterAtLength()
      TPointD u = stroke->getSpeed(w);
      if (norm2(u) == 0) {
        s += 0.1;
        continue;
      }  // non dovrebbe succedere mai, ma per prudenza....
      TThickPoint pos = stroke->getThickPoint(w);
      u               = normalize(u);
      TPointD v       = rotate90(u) * (width);
      pos1            = pos + v;
      pos4            = pos - v;
      pv.push_back(pos1);
      pv.push_back(pos4);
      s += step;
    }
    double w = stroke->getParameterAtLength(length);
    if (w > 0) {
      TPointD u = stroke->getSpeed(w);
      if (norm2(u) != 0) {
        TThickPoint pos = stroke->getThickPoint(w);
        u               = normalize(u);
        TPointD v       = rotate90(u) * (width);
        pos1            = pos + v;
        pos4            = pos - v;
        pv.push_back(pos1);
        pv.push_back(pos4);
      }
    }

    if (pv.size() > 2) {
      glColor4ub(color.r, color.g, color.b, color.m);
      glBegin(QUAD_PRIMITIVE);
      std::vector<TPointD>::iterator it = pv.begin();
      for (; it != pv.end(); ++it) {
        tglVertex(*it);
      }
      glEnd();

      glBegin(GL_LINE_STRIP);

      std::vector<TPointD>::iterator it1 = pv.begin();
      for (; it1 != pv.end(); ++it1) {
        ++it1;
        tglVertex(*it1);
      }
      glEnd();
      glBegin(GL_LINE_STRIP);
      std::vector<TPointD>::iterator it2 = pv.begin();
      if (it2 != pv.end()) ++it2;
      for (; it2 != pv.end() - 1; ++it2) {
        ++it2;
        tglVertex(*it2);
      }
      glEnd();
    }
  }

  if (m_stipple != 0) glDisable(GL_LINE_STIPPLE);

  glLineWidth(1.0);
}

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

QString TCenterLineStrokeStyle::getDescription() const {
  return QCoreApplication::translate("TCenterLineStrokeStyle", "Constant");
}

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

std::string TCenterLineStrokeStyle::getBrushIdName() const {
  return "CenterLineStrokeStyle";
}

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

int TCenterLineStrokeStyle::getTagId() const { return 2; }

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

void TCenterLineStrokeStyle::loadData(TInputStreamInterface &is) {
  is >> m_color >> m_stipple >> m_width;
}

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

void TCenterLineStrokeStyle::saveData(TOutputStreamInterface &os) const {
  os << m_color << m_stipple << m_width;
}

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

int TCenterLineStrokeStyle::getParamCount() const { return 1; }

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

TColorStyle::ParamType TCenterLineStrokeStyle::getParamType(int index) const {
  assert(0 <= index && index < getParamCount());
  return TColorStyle::DOUBLE;
}

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

QString TCenterLineStrokeStyle::getParamNames(int index) const {
  return QCoreApplication::translate("TCenterLineStrokeStyle", "Thickness");
}

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

void TCenterLineStrokeStyle::getParamRange(int index, double &min,
                                           double &max) const {
  assert(0 <= index && index < getParamCount());
  min = 0.0, max = 10.0;
}

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

double TCenterLineStrokeStyle::getParamValue(TColorStyle::double_tag,
                                             int index) const {
  assert(0 <= index && index < getParamCount());
  return m_width;
}

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

void TCenterLineStrokeStyle::setParamValue(int index, double value) {
  assert(0 <= index && index < getParamCount());
  m_width = value;
}

/*
TPixel32 TTextureStyle::getMainColor() const
{return m_averageColor; }*/

//*************************************************************************************
//    TRasterImagePatternStrokeStyle  implementation
//*************************************************************************************

TRasterImagePatternStrokeStyle::TRasterImagePatternStrokeStyle()
    : m_level(), m_name(""), m_space(0), m_rotation(0) {}

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

TRasterImagePatternStrokeStyle::TRasterImagePatternStrokeStyle(
    const std::string &patternName)
    : m_level(), m_name(patternName), m_space(20), m_rotation(0) {
  if (m_name != "") loadLevel(m_name);
}

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

TColorStyle *TRasterImagePatternStrokeStyle::clone() const {
  return new TRasterImagePatternStrokeStyle(*this);
}

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

TColorStyle *TRasterImagePatternStrokeStyle::clone(
    std::string brushIdName) const {
  TRasterImagePatternStrokeStyle *style =
      new TRasterImagePatternStrokeStyle(*this);
  std::string patternName = getBrushIdNameParam(brushIdName);
  if (patternName != "") style->loadLevel(patternName);
  return style;
}

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

QString TRasterImagePatternStrokeStyle::getDescription() const {
  return "TRasterImagePatternStrokeStyle";
}

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

std::string TRasterImagePatternStrokeStyle::getBrushIdName() const {
  return "RasterImagePatternStrokeStyle:" + m_name;
}

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

void TRasterImagePatternStrokeStyle::makeIcon(const TDimension &size) {
  if (!m_level) loadLevel(m_name);
  m_icon                   = TRaster32P();
  TLevel::Iterator frameIt = m_level->begin();

  if (frameIt != m_level->end()) {
    TRasterImageP img = frameIt->second;
    TRaster32P src;
    if (img) src = img->getRaster();
    if (src && src->getLx() > 1 && src->getLy() > 1) {
      TRaster32P icon(size);
      icon->fill(TPixel32::White);
      double sx = (double)icon->getLx() / (double)src->getLx();
      double sy = (double)icon->getLy() / (double)src->getLy();
      double sc = 0.8 * std::min(sx, sy);
      TRop::resample(icon, src,
                     TScale(sc).place(src->getCenterD(), icon->getCenterD()));
      TRop::addBackground(icon, TPixel32::White);
      m_icon = icon;
    }
  }

  if (!m_icon) {
    TRaster32P icon(size);
    icon->fill(TPixel32::Black);
    int lx = icon->getLx();
    int ly = icon->getLy();
    for (int y = 0; y < ly; y++) {
      int x = ((lx - 1 - 10) * y / ly);
      icon->extractT(x, y, x + 5, y)->fill(TPixel32::Red);
    }
    m_icon = icon;
    return;
  }
}

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

int TRasterImagePatternStrokeStyle::getParamCount() const { return 2; }

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

TColorStyle::ParamType TRasterImagePatternStrokeStyle::getParamType(
    int index) const {
  assert(0 <= index && index < getParamCount());
  return TColorStyle::DOUBLE;
}

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

QString TRasterImagePatternStrokeStyle::getParamNames(int index) const {
  assert(0 <= index && index < getParamCount());
  return (index == 0) ? QCoreApplication::translate(
                            "TRasterImagePatternStrokeStyle", "Distance")
                      : QCoreApplication::translate(
                            "TRasterImagePatternStrokeStyle", "Rotation");
}

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

void TRasterImagePatternStrokeStyle::getParamRange(int index, double &min,
                                                   double &max) const {
  assert(0 <= index && index < getParamCount());

  if (index == 0) {
    min = -50;
    max = 50;
  } else {
    min = -180;
    max = 180;
  }
}

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

double TRasterImagePatternStrokeStyle::getParamValue(TColorStyle::double_tag,
                                                     int index) const {
  assert(0 <= index && index < getParamCount());
  return (index == 0) ? m_space : m_rotation;
}

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

void TRasterImagePatternStrokeStyle::setParamValue(int index, double value) {
  assert(0 <= index && index < getParamCount());

  if (index == 0) {
    if (m_space != value) {
      updateVersionNumber();  // aggiorna il vettore di affini
    }
    m_space = value;
  } else {
    if (m_rotation != value) {
      updateVersionNumber();  // aggiorna il vettore di affini
    }
    m_rotation = value;
  }
}

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

//
// carico il pattern 'patternName' dalla directory dei custom styles
//
void TRasterImagePatternStrokeStyle::loadLevel(const std::string &patternName) {
  struct locals {
    static TAffine getAffine(const TDimension &srcSize,
                             const TDimension &dstSize) {
      double scx = 1 * dstSize.lx / (double)srcSize.lx;
      double scy = 1 * dstSize.ly / (double)srcSize.ly;
      double sc  = std::min(scx, scy);
      double dx  = (dstSize.lx - srcSize.lx * sc) * 0.5;
      double dy  = (dstSize.ly - srcSize.ly * sc) * 0.5;
      return TScale(sc) * TTranslation(0.5 * TPointD(srcSize.lx, srcSize.ly) +
                                       TPointD(dx, dy));
    }
  };  // locals

  // button l'eventuale livello
  m_level = TLevelP();

  // aggiorno il nome
  m_name = patternName;

  // getRootDir() e' nulla se non si e' chiamata la setRoot(..)
  assert(!getRootDir().isEmpty());

  // leggo tutti i livelli contenuti
  TFilePathSet fps;
  TSystem::readDirectory(fps, getRootDir());

  // prendo il primo livello il cui nome sia patternName
  // (puo' essere un pli, ma anche un png, ecc.)
  TFilePath fp;
  TFilePathSet::iterator fpIt;
  for (fpIt = fps.begin(); fpIt != fps.end(); ++fpIt) {
    if (fpIt->getName() == patternName) {
      fp = *fpIt;
      break;
    }
  }

  // se non ho trovato nulla esco
  if (fp == TFilePath() || !TSystem::doesExistFileOrLevel(fp)) return;

  // Leggo i frames del livello e ne ricavo delle textures
  // che memorizzo in m_level come TRasterImage
  TLevelReaderP lr(fp);
  TLevelP level = lr->loadInfo();
  TLevel::Iterator frameIt;
  TOfflineGL *glContext = 0;

  for (frameIt = level->begin(); frameIt != level->end(); ++frameIt) {
    TImageP img = lr->getFrameReader(frameIt->first)->load();
    if (TRasterImageP ri = img) {
      // se il frame e' raster...
      TRaster32P ras = ri->getRaster();
      if (!ras) continue;
      // aggiusta le dimensioni
      ras = makeTexture(ras);
      if (!ras) continue;
      m_level->setFrame(frameIt->first, new TRasterImage(ras));
    } else if (TVectorImageP vi = img) {
      // se il frame e' vettoriale
      // lo rasterizzo creando una texture 256x256 (N.B. le dimensioni
      // delle textures openGL devono essere potenze di due)
      TRaster32P ras(256, 256);
      // se serve creo il contesto OpenGL offline (l'idea e' di crearne
      // uno solo per tutto il livello
      if (!glContext) glContext = new TOfflineGL(ras->getSize());
      // camera di default 640x480. probabilmente non e' la scelta
      // migliore.
      // TDimension cameraSize(768, 576);
      TDimension cameraSize(1920, 1080);

      // definisco i renderdata
      const TVectorRenderData rd(locals::getAffine(cameraSize, ras->getSize()),
                                 TRect(), level->getPalette(), 0, true, true);
      // rasterizzo
      glContext->draw(vi, rd);
      ras->copy(glContext->getRaster());
      m_level->setFrame(frameIt->first, new TRasterImage(ras));
    }
  }
  // cancello il contesto offline (se e' stato creato)
  delete glContext;
}

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

void TRasterImagePatternStrokeStyle::computeTransformations(
    std::vector<TAffine> &transformations, const TStroke *stroke) const {
  const int frameCount = m_level->getFrameCount();
  if (frameCount == 0) return;
  transformations.clear();
  const double length = stroke->getLength();

  std::vector<TDimensionD> images;
  assert(m_level->begin() != m_level->end());
  TLevel::Iterator lit;
  for (lit = m_level->begin(); lit != m_level->end(); ++lit) {
    TRasterImageP ri = lit->second;
    if (!ri) continue;
    TDimension d = ri->getRaster()->getSize();
    images.push_back(TDimensionD(d.lx, d.ly));
  }
  assert(!images.empty());
  if (images.empty()) return;

  double s  = 0;
  int index = 0;
  int m     = images.size();
  while (s < length) {
    double t      = stroke->getParameterAtLength(s);
    TThickPoint p = stroke->getThickPoint(t);
    TPointD v     = stroke->getSpeed(t);
    double ang    = rad2degree(atan(v)) + m_rotation;

    int ly    = std::max(1.0, images[index].ly);
    double sc = p.thick / ly;
    transformations.push_back(TTranslation(p) * TRotation(ang) * TScale(sc));
    double ds = std::max(2.0, sc * images[index].lx * 2 + m_space);
    s += ds;
  }
}

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

void TRasterImagePatternStrokeStyle::drawStroke(
    const TVectorRenderData &rd, const std::vector<TAffine> &transformations,
    const TStroke *stroke) const {
  TStopWatch sw;
  sw.start();
  CHECK_GL_ERROR

  const int frameCount = m_level->getFrameCount();
  if (frameCount == 0) return;

  // lo stroke viene disegnato ripetendo size volte le frameCount immagini
  // contenute in level, posizionando ognuna secondo transformations[i]
  UINT size = transformations.size();

  glEnable(GL_TEXTURE_2D);
  glEnable(GL_BLEND);

  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

  GLuint texId;
  glGenTextures(1, &texId);

  glBindTexture(GL_TEXTURE_2D, texId);

  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

  // l'utilizzo di modulate, con un colore di 1,1,1,1 permette di
  // tenere in conto il canale alfa delle texture
  glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

  glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);

  // visto che cambiare texture costa tempo il ciclo esterno e' sulle textures
  // piuttosto che sulle trasformazioni
  TLevel::Iterator frameIt = m_level->begin();
  for (int i = 0; i < (int)size && frameIt != m_level->end(); ++i, ++frameIt) {
    TRasterImageP ri = frameIt->second;
    TRasterP ras;
    if (ri) ras = ri->getRaster();
    if (!ras) continue;
    TextureInfoForGL texInfo;
    TRasterP texImage = prepareTexture(ras, texInfo);

    glTexImage2D(GL_TEXTURE_2D,
                 0,                       // one level only
                 texInfo.internalformat,  // pixel channels count
                 texInfo.width,           // width
                 texInfo.height,          // height
                 0,                       // border size
                 texInfo.type,    // pixel format           // crappy names
                 texInfo.format,  // pixel data type        // oh, SO much
                 texImage->getRawData());

    for (int j = i; j < (int)size; j += frameCount) {
      TAffine aff = rd.m_aff * transformations[j];
      glPushMatrix();
      tglMultMatrix(aff);

      double rx = ras->getLx();
      double ry = ras->getLy();

      glColor4d(1, 1, 1, 1);

      glBegin(QUAD_PRIMITIVE);
      glTexCoord2d(0, 0);
      glVertex2d(-rx, -ry);
      glTexCoord2d(1, 0);
      glVertex2d(rx, -ry);
      glTexCoord2d(0, 1);
      glVertex2d(-rx, ry);
      glTexCoord2d(1, 1);
      glVertex2d(rx, ry);
      glEnd();

      glPopMatrix();
    }
  }

  glDeleteTextures(1, &texId);

  glDisable(GL_TEXTURE_2D);
  glDisable(GL_BLEND);
}

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

void TRasterImagePatternStrokeStyle::loadData(TInputStreamInterface &is) {
  m_level = TLevelP();
  m_name  = "";
  std::string name;
  is >> name >> m_space >> m_rotation;
  if (name != "") {
    try {
      loadLevel(name);
    } catch (...) {
    }
  }
}

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

void TRasterImagePatternStrokeStyle::loadData(int ids,
                                              TInputStreamInterface &is) {
  if (ids != 100)
    throw TException("image pattern stroke style: unknown obsolete format");

  m_level = TLevelP();
  m_name  = "";
  std::string name;
  is >> name;
  if (name != "") {
    try {
      loadLevel(name);
    } catch (...) {
    }
  }
}

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

void TRasterImagePatternStrokeStyle::saveData(
    TOutputStreamInterface &os) const {
  os << m_name << m_space << m_rotation;
}

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

TFilePath TRasterImagePatternStrokeStyle::getRootDir() { return m_rootDir; }

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

TStrokeProp *TRasterImagePatternStrokeStyle::makeStrokeProp(
    const TStroke *stroke) {
  return new TRasterImagePatternStrokeProp(stroke, this);
}

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

void TRasterImagePatternStrokeStyle::getObsoleteTagIds(
    std::vector<int> &ids) const {
  ids.push_back(100);
}

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

TRectD TRasterImagePatternStrokeStyle::getStrokeBBox(
    const TStroke *stroke) const {
  TRectD rect = TColorStyle::getStrokeBBox(stroke);
  return rect.enlarge(std::max(rect.getLx() * 0.25, rect.getLy() * 0.25));
}

//*************************************************************************************
//    TVectorImagePatternStrokeStyle  implementation
//*************************************************************************************

TVectorImagePatternStrokeStyle::TVectorImagePatternStrokeStyle()
    : m_level(), m_name(""), m_space(0), m_rotation(0) {}

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

TVectorImagePatternStrokeStyle::TVectorImagePatternStrokeStyle(
    const std::string &patternName)
    : m_level(), m_name(patternName), m_space(20), m_rotation(0) {
  loadLevel(patternName);
}

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

TColorStyle *TVectorImagePatternStrokeStyle::clone() const {
  return new TVectorImagePatternStrokeStyle(*this);
}

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

TColorStyle *TVectorImagePatternStrokeStyle::clone(
    std::string brushIdName) const {
  TVectorImagePatternStrokeStyle *style =
      new TVectorImagePatternStrokeStyle(*this);
  std::string patternName = getBrushIdNameParam(brushIdName);
  if (patternName != "") style->loadLevel(patternName);
  return style;
}

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

QString TVectorImagePatternStrokeStyle::getDescription() const {
  return "TVectorImagePatternStrokeStyle";
}

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

std::string TVectorImagePatternStrokeStyle::getBrushIdName() const {
  return "VectorImagePatternStrokeStyle:" + m_name;
}

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

void TVectorImagePatternStrokeStyle::makeIcon(const TDimension &size) {
  TLevel::Iterator frameIt = m_level->begin();
  if (frameIt == m_level->end()) {
    TRaster32P icon(size);
    icon->fill(TPixel32::Black);
    int lx = icon->getLx();
    int ly = icon->getLy();
    for (int y = 0; y < ly; y++) {
      int x = ((lx - 1 - 10) * y / ly);
      icon->extractT(x, y, x + 5, y)->fill(TPixel32::Red);
    }
    m_icon = icon;
    return;
  }
  TFrameId fid      = frameIt->first;
  TVectorImageP img = m_level->frame(fid);
  if (!img) {
    TRaster32P icon(size);
    icon->fill(TPixel32::Black);
    int lx = icon->getLx();
    int ly = icon->getLy();
    for (int y = 0; y < ly; y++) {
      int x = ((lx - 1 - 10) * y / ly);
      icon->extractT(x, y, x + 5, y)->fill(TPixel32::Red);
    }
    m_icon = icon;
    return;
  }
  // img->setPalette(m_level->getPalette());
  TPalette *vPalette = m_level->getPalette();
  assert(vPalette);
  img->setPalette(vPalette);
  TOfflineGL *glContext = TOfflineGL::getStock(size);
  glContext->clear(TPixel32::White);
  TRectD bbox = img->getBBox();
  double scx  = 0.8 * size.lx / bbox.getLx();
  double scy  = 0.8 * size.ly / bbox.getLy();
  double sc   = std::min(scx, scy);
  double dx   = (size.lx - bbox.getLx() * sc) * 0.5;
  double dy   = (size.ly - bbox.getLy() * sc) * 0.5;
  TAffine aff = TScale(sc) * TTranslation(-bbox.getP00() + TPointD(dx, dy));
  TVectorRenderData rd(aff, size, vPalette, 0, true);
  glContext->draw(img, rd);
  if (!m_icon || m_icon->getSize() != size)
    m_icon = glContext->getRaster()->clone();
  else
    m_icon->copy(glContext->getRaster());
}

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

int TVectorImagePatternStrokeStyle::getParamCount() const { return 2; }

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

TColorStyle::ParamType TVectorImagePatternStrokeStyle::getParamType(
    int index) const {
  assert(0 <= index && index < getParamCount());
  return TColorStyle::DOUBLE;
}

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

QString TVectorImagePatternStrokeStyle::getParamNames(int index) const {
  assert(0 <= index && index < getParamCount());
  return (index == 0) ? QCoreApplication::translate(
                            "TVectorImagePatternStrokeStyle", "Distance")
                      : QCoreApplication::translate(
                            "TVectorImagePatternStrokeStyle", "Rotation");
}

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

void TVectorImagePatternStrokeStyle::getParamRange(int index, double &min,
                                                   double &max) const {
  assert(0 <= index && index < getParamCount());

  if (index == 0) {
    min = -50;
    max = 50;
  } else {
    min = -180;
    max = 180;
  }
}

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

double TVectorImagePatternStrokeStyle::getParamValue(TColorStyle::double_tag,
                                                     int index) const {
  assert(0 <= index && index < getParamCount());
  return index == 0 ? m_space : m_rotation;
}

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

void TVectorImagePatternStrokeStyle::setParamValue(int index, double value) {
  assert(0 <= index && index < getParamCount());

  if (index == 0) {
    if (m_space != value) {
      updateVersionNumber();  // aggiorna il vettore di affini
    }
    m_space = value;
  } else {
    if (m_rotation != value) {
      updateVersionNumber();  // aggiorna il vettore di affini
    }
    m_rotation = value;
  }
}

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

void TVectorImagePatternStrokeStyle::loadLevel(const std::string &patternName) {
  m_level = TLevelP();
  m_name  = patternName;
  assert(!getRootDir()
              .isEmpty());  // se e' vuota, non si e' chiamata la setRoot(..)
  TFilePath fp = getRootDir() + (patternName + ".pli");
  TLevelReaderP lr(fp);
  m_level = lr->loadInfo();
  TLevel::Iterator frameIt;
  std::map<TPixel32, int> colors;
  for (frameIt = m_level->begin(); frameIt != m_level->end(); ++frameIt) {
    TVectorImageP img = lr->getFrameReader(frameIt->first)->load();
    if (img) m_level->setFrame(frameIt->first, img);
  }
}

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

void TVectorImagePatternStrokeStyle::computeTransformations(
    std::vector<TAffine> &transformations, const TStroke *stroke) const {
  const int frameCount = m_level->getFrameCount();
  if (frameCount == 0) return;
  transformations.clear();
  const double length = stroke->getLength();
  assert(m_level->begin() != m_level->end());
  TLevel::Iterator lit = m_level->begin();
  double s             = 0;

  while (s < length) {
    TFrameId fid      = lit->first;
    TVectorImageP img = m_level->frame(fid);
    if (++lit == m_level->end()) lit = m_level->begin();
    assert(img);
    if (img->getType() != TImage::VECTOR) return;
    double t       = stroke->getParameterAtLength(s);
    TThickPoint p  = stroke->getThickPoint(t);
    TPointD v      = stroke->getSpeed(t);
    double ang     = rad2degree(atan(v)) + m_rotation;
    TRectD bbox    = img->getBBox();
    TPointD center = 0.5 * (bbox.getP00() + bbox.getP11());
    double ry      = bbox.getLy() * 0.5;
    if (ry * ry < 1e-5) ry = p.thick;
    double sc = p.thick / ry;
    if (sc < 0.0001) sc = 0.0001;
    TAffine aff =
        TTranslation(p) * TRotation(ang) * TScale(sc) * TTranslation(-center);
    transformations.push_back(aff);
    double ds = std::max(2.0, sc * bbox.getLx() + m_space);
    s += ds;
  }
}

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

void TVectorImagePatternStrokeStyle::clearGlDisplayLists() {
#ifdef _WIN32
  DisplayListManager *pmgr = DisplayListManager::instance();
  assert(pmgr);
  pmgr->clearLists();
#endif
}

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

void TVectorImagePatternStrokeStyle::drawStroke(
    const TVectorRenderData &rd, const std::vector<TAffine> &transformations,
    const TStroke *stroke) const {
  const int frameCount = m_level->getFrameCount();
  if (frameCount == 0) return;
  //------------------------------------------
  // if the average thickness of the stroke, with the current pixelSize,
  // is less than 2, do not drow anything
  UINT cpCount    = stroke->getControlPointCount();
  UINT sampleStep = (cpCount < 10) ? 1 : (UINT)((double)cpCount / 10.0);
  double thickSum = 0;
  UINT count      = 0;
  for (UINT cp = 0; cp < cpCount; cp += sampleStep) {
    thickSum += stroke->getControlPoint(cp).thick;
    count++;
  }
  double averageThick = thickSum / (double)count;
  glPushMatrix();
  tglMultMatrix(rd.m_aff);
  double pixelSize2 = tglGetPixelSize2();
  glPopMatrix();
  if (averageThick * averageThick < 4 * pixelSize2) {
    CHECK_GL_ERROR

    glPushMatrix();

    CHECK_GL_ERROR

    tglMultMatrix(rd.m_aff);

    TLevel::Iterator lit = m_level->begin();

    TFrameId fid      = lit->first;
    TVectorImageP img = m_level->frame(fid);

    std::set<int> styles;
    img->getUsedStyles(styles);

    std::set<int>::iterator si = styles.begin();

    // Calcolo media dei colori utilizzati

    int numStyles  = 0, styleID;
    int averageRed = 0, averageBlue = 0, averageGreen = 0;
    TPixel32 color, averageColor;
    TColorStyle *colorStyle;

    for (; si != styles.end(); si++) {
      styleID = (int)*si;

      if (styleID != 0) {
        colorStyle = m_level->getPalette()->getStyle(styleID);
        color      = colorStyle->getAverageColor();
        if (color.m != 0) {
          numStyles++;
          averageRed += color.r;
          averageGreen += color.g;
          averageBlue += color.b;
        }
      }
    }

    if (numStyles != 0) {
      averageColor.r = averageRed / numStyles;
      averageColor.g = averageGreen / numStyles;
      averageColor.b = averageBlue / numStyles;
    } else
      averageColor = TPixel32::Black;

    tglColor(averageColor);

    glLineStipple(2, 0xAAAA);
    glEnable(GL_LINE_STIPPLE);

    UINT n = stroke->getLength() *
             0.05;  // TODO: funzione di stroke length e pixelsize
    glBegin(GL_LINE_STRIP);
    for (UINT i = 0; i < n; i++) {
      if (n != 1) {
        TThickPoint p = stroke->getPoint(i / (double)(n - 1));
        glVertex2d(p.x, p.y);
      }
    }
    glEnd();

    glDisable(GL_LINE_STIPPLE);

    CHECK_GL_ERROR

    glPopMatrix();

  } else {
    //--------------------------------------------
    assert(m_level->begin() != m_level->end());
    TLevel::Iterator lit = m_level->begin();
    UINT i, size = transformations.size();

    for (i = 0; i < size; i++) {
      TFrameId fid      = lit->first;
      TVectorImageP img = m_level->frame(fid);
      if (++lit == m_level->end()) lit = m_level->begin();
      assert(img);
      if (!img) continue;
      if (img->getType() != TImage::VECTOR) return;
      TVectorImage *imgPointer    = img.getPointer();
      TAffine totalTransformation = rd.m_aff * transformations[i];
      if (rd.m_clippingRect != TRect() && !rd.m_is3dView &&
          !(convert(totalTransformation * imgPointer->getBBox())
                .overlaps(rd.m_clippingRect)))
        continue;
      // drawing
      CHECK_GL_ERROR

      glPushMatrix();

      CHECK_GL_ERROR

      tglMultMatrix(totalTransformation);

      CHECK_GL_ERROR
#ifdef _WIN32
      // currently drawing with the display list doesn't work properly

      // GLuint listId = DisplayListManager::instance()->getDisplayListId(
      //    imgPointer, m_name, fid, rd);
      // if (listId != 0) {
      //  CHECK_GL_ERROR
      //  glCallList(listId);
      //  CHECK_GL_ERROR
      //} else
#endif

      {
        TAffine identity;
        TRect rect;
        TPalette *vPalette = imgPointer->getPalette();
        assert(vPalette);
        TVectorRenderData rd2(identity, rect, vPalette, rd.m_cf, true, true);
        CHECK_GL_ERROR

        tglDraw(rd2, imgPointer);

        CHECK_GL_ERROR
      }
      glPopMatrix();
    }
  }
}

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

void TVectorImagePatternStrokeStyle::loadData(TInputStreamInterface &is) {
  m_level = TLevelP();
  m_name  = "";
  std::string name;
  is >> name >> m_space >> m_rotation;
  if (name != "") {
    try {
      loadLevel(name);
    } catch (...) {
    }
  }
}

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

void TVectorImagePatternStrokeStyle::loadData(int ids,
                                              TInputStreamInterface &is) {
  if (ids != 100)
    throw TException("image pattern stroke style: unknown obsolete format");

  m_level = TLevelP();
  m_name  = "";
  std::string name;
  is >> name;
  if (name != "") {
    try {
      loadLevel(name);
    } catch (...) {
    }
  }
}

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

void TVectorImagePatternStrokeStyle::saveData(
    TOutputStreamInterface &os) const {
  os << m_name << m_space << m_rotation;
}

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

TFilePath TVectorImagePatternStrokeStyle::getRootDir() { return m_rootDir; }

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

TStrokeProp *TVectorImagePatternStrokeStyle::makeStrokeProp(
    const TStroke *stroke) {
  return new TVectorImagePatternStrokeProp(stroke, this);
}

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

void TVectorImagePatternStrokeStyle::getObsoleteTagIds(
    std::vector<int> &ids) const {
  // ids.push_back(100);
}

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

TRectD TVectorImagePatternStrokeStyle::getStrokeBBox(
    const TStroke *stroke) const {
  TRectD rect = TColorStyle::getStrokeBBox(stroke);
  return rect.enlarge(std::max(rect.getLx() * 0.25, rect.getLy() * 0.25));
}

//*************************************************************************************
//    Style declarations  instances
//*************************************************************************************

namespace {

TColorStyle::Declaration s0(new TCenterLineStrokeStyle());
TColorStyle::Declaration s1(new TSolidColorStyle());
TColorStyle::Declaration s3(new TRasterImagePatternStrokeStyle());
TColorStyle::Declaration s4(new TVectorImagePatternStrokeStyle());
}  // namespace
