/****************************************************************************
* VCGLib                                                            o o     *
* Visual and Computer Graphics Library                            o     o   *
*                                                                _   O  _   *
* Copyright(C) 2004-2016                                           \/)\/    *
* Visual Computing Lab                                            /\/|      *
* ISTI - Italian National Research Council                           |      *
*                                                                    \      *
* All rights reserved.                                                      *
*                                                                           *
* 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 (http://www.gnu.org/licenses/gpl.txt)          *
* for more details.                                                         *
*                                                                           *
****************************************************************************/
#ifndef __VCG_OUTLINE2_PACKER_H__
#define __VCG_OUTLINE2_PACKER_H__

#include <limits>
#include <fstream>
#include <stdio.h>
#include <assert.h>
#include <vcg/space/box2.h>
#include <vcg/space/rect_packer.h>
#include <vcg/space/point2.h>
#include <vcg/math/similarity2.h>



namespace vcg
{

template <class SCALAR_TYPE>
class PolyPacker
{
  typedef typename vcg::Box2<SCALAR_TYPE> Box2x;
  typedef typename vcg::Point2<SCALAR_TYPE> Point2x;
  typedef typename vcg::Similarity2<SCALAR_TYPE> Similarity2x;

public:

  static Box2x getPolyBB(const std::vector<Point2x> &poly)
  {
    Box2x bb;
    for(size_t i=0;i<poly.size();++i)
      bb.Add(poly[i]);

    return bb;
  }

  static Box2x getPolyOOBB(const std::vector<Point2x> &poly, float &rot)
  {
    const int stepNum=32;
    float bestAngle;
    float bestArea = std::numeric_limits<float>::max();
    Box2x bestBB;

    for(int i=0;i<stepNum;++i)
    {
      float angle = float(i)*(M_PI/2.0)/float(stepNum);
      Box2x bb;
      for(size_t j=0;j<poly.size();++j)
      {
        Point2x pp=poly[j];
        pp.Rotate(angle);
        bb.Add(pp);
      }

      if(bb.Area()<bestArea)
      {
        bestAngle=angle;
        bestArea=bb.Area();
        bestBB=bb;
      }
    }
    rot=bestAngle;
    return bestBB;
  }

static  bool PackAsEqualSquares(const std::vector< std::vector<Point2x> > &polyVec,
                  const Point2i containerSizeX,
                  std::vector<Similarity2x> &trVec,
                  Point2x &/*coveredContainer*/)
{
  int minSide = std::min(containerSizeX[0],containerSizeX[1]);
  const vcg::Point2i containerSize(minSide,minSide);
  int polyPerLine = ceil(sqrt((double)polyVec.size()));
  int pixelPerPoly = minSide / (polyPerLine);
  if(pixelPerPoly < 1) return false;

  trVec.clear();
  trVec.resize(polyVec.size());
  Box2x bbMax;
  std::vector<Box2x> bbVec;
  for(size_t i=0;i<polyVec.size();++i)
  {
    bbVec.push_back(getPolyBB(polyVec[i]));
    bbMax.Add(bbVec.back());
  }

  float unitScale = 1.0/std::max(bbMax.DimX(), bbMax.DimY());
  float polyScale = unitScale * pixelPerPoly;

  int baseX =0;
  int baseY=0;
  for(size_t i=0;i<polyVec.size();++i)
  {
    trVec[i].sca = polyScale; // the same scaling for all the polygons;
    trVec[i].tra = Point2f(baseX+(0.5*pixelPerPoly), baseY+(0.5*pixelPerPoly)) - bbVec[i].Center()*polyScale;
    baseX +=pixelPerPoly;

    if(baseX +pixelPerPoly>minSide)
    {
      baseY+=pixelPerPoly;
      baseX=0;
    }
  }
  return true;
}

static bool PackAsAxisAlignedRect(const std::vector< std::vector<Point2x> > &polyVec,
                  const Point2i containerSizeX,
                  std::vector<Similarity2x> &trVec,
                  Point2x &coveredContainer)
{
  trVec.clear();
  trVec.resize(polyVec.size());
  std::vector<Box2x> bbVec;
  for(size_t i=0;i<polyVec.size();++i)
  {
    assert(polyVec[i].size()>0);
    bbVec.push_back(getPolyBB(polyVec[i]));
  }
  return RectPacker<SCALAR_TYPE>::Pack(bbVec,containerSizeX,trVec,coveredContainer);
}

static bool PackAsObjectOrientedRect(const std::vector< std::vector<Point2x> > &polyVec,
                  const Point2i containerSizeX,
                  std::vector<Similarity2x> &trVec,
                  Point2x &coveredContainer,
                  SCALAR_TYPE border=0)
{
  trVec.clear();
  trVec.resize(polyVec.size());
  std::vector<Box2x> bbVec;
  std::vector<SCALAR_TYPE> rotVec;
  for(size_t i=0;i<polyVec.size();++i)
  {
    float rot;
    bbVec.push_back(getPolyOOBB(polyVec[i],rot));
    if (border>0)
        bbVec.back().Offset(border);
    rotVec.push_back(rot);
  }

  bool ret= RectPacker<SCALAR_TYPE>::Pack(bbVec,containerSizeX,trVec,coveredContainer);

  for(size_t i=0;i<polyVec.size();++i)
  {
    trVec[i].rotRad=rotVec[i];
  }
  return ret;
}


static bool PackMultiAsObjectOrientedRect(const std::vector< std::vector<Point2x> > &polyVec,
                  const Point2x containerSizeX, const int containerNum,
                  std::vector<Similarity2x> &trVec, std::vector<int> &indVec,
                  std::vector<Point2x> &coveredContainerVec)
{
  trVec.clear();
  trVec.resize(polyVec.size());
  std::vector<Box2x> bbVec;
  std::vector<float> rotVec;
  for(size_t i=0;i<polyVec.size();++i)
  {
    float rot;
    bbVec.push_back(getPolyOOBB(polyVec[i],rot));
    rotVec.push_back(rot);
  }
  const Point2i containerSizeI=Point2i::Construct(containerSizeX);
  bool ret= RectPacker<float>::PackMulti(bbVec,containerSizeI,containerNum,trVec,indVec,coveredContainerVec);

  for(size_t i=0;i<polyVec.size();++i)
  {
    trVec[i].rotRad=rotVec[i];
  }
  return ret;
}

static bool WritePolyVec(const std::vector< std::vector<Point2x> > &polyVec, const char *filename)
{
  FILE *fp=fopen(filename,"w");
  if(!fp) return false;
  fprintf(fp,"%lu\n",polyVec.size());
  for(size_t i=0;i<polyVec.size();++i)
  {
    fprintf(fp,"%lu\n",polyVec[i].size());
    for(size_t j=0;j<polyVec[i].size();++j)
      fprintf(fp,"%f %f ",polyVec[i][j].X(),polyVec[i][j].Y());
    fprintf(fp,"\n");
  }
  fclose(fp);
  return true;
}

static bool ReadPolyVec(std::vector< std::vector<Point2x> > &polyVec, const char *filename)
{
	std::ifstream ifs(filename, std::ifstream::in);
	if (!ifs.is_open()) return false;
	int sz;
	ifs >> sz;
	polyVec.resize(sz);
	for (std::size_t i = 0; i < sz; ++i){
		int isz;
		ifs >> isz;
		polyVec[i].resize(isz);
		for (std::size_t j = 0; j < isz; ++j){
			float x, y;
			ifs >> x >> y;
			polyVec[i][j].X() = x;
			polyVec[i][j].Y() = y;
		}
	}
	ifs.close();
	return true;
}


}; // end class
} // end namespace vcg
#endif // POLY_PACKER_H
