/*********************************************************************/
/*                                                                   */
/* Copyright 2022-2023 Rémi Synave - remi.synave@univ-littoral.fr    */
/*                                                                   */
/* This file is part of DSL.                                         */
/* This software uses Qt to build the Graphical User Interface       */
/* https://www.qt.io/                                                */
/*                                                                   */
/* DSL 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 3 of the License, */
/* or (at your option) any later version.                            */
/*                                                                   */
/* DSL 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 DSL. If not, see <http://www.gnu.org/licenses/>.       */
/*                                                                   */
/*********************************************************************/

#include <QFile>
#include <QColor>
#include <QTextStream>
#include <QJsonObject>
#include <QJsonArray>
#include <QJsonDocument>

#include <QDebug>
#include <QStringList>

#include "WorkArea.hpp"

#include "StrengthLine.hpp"

WorkArea::WorkArea (int screenSizeX, int screenSizeY, QWidget * parent):
  QWidget(parent)
{
  this->screenSizeX = screenSizeX;
  this->screenSizeY = screenSizeY;
  readOnly = false;
  modificationInProgress = false;
  img = NULL;
  resizeFactor = 1;
  indexPointClicked = -1;
  maxLines = 0;
}

WorkArea::WorkArea (int screenSizeX, int screenSizeY, const std::string & filename, QWidget * parent):QWidget(parent)
{
  this->screenSizeX = screenSizeX;
  this->screenSizeY = screenSizeY;
  readOnly = false;
  modificationInProgress = false;
  img = NULL;
  resizeFactor = 1;
  indexPointClicked = -1;
  maxLines = 0;
  loadImage(filename);
}

WorkArea::WorkArea (int screenSizeX, int screenSizeY, const std::string & imageFilename, const std::string & SLFilename, bool readOnly, QWidget * parent):QWidget(parent)
{
  this->screenSizeX = screenSizeX;
  this->screenSizeY = screenSizeY;
  readOnly = false;
  modificationInProgress = false;
  img = NULL;
  resizeFactor = 1;
  indexPointClicked = -1;
  maxLines = 0;
  loadImage(imageFilename);
  this->readOnly = readOnly;
  loadSL(SLFilename);  
}

WorkArea::~WorkArea ()
{
}

void
WorkArea::loadImage (const std::string & filename)
{
  original = new QImage (QString::fromStdString (filename));
  img =
    new QImage (original->scaled (screenSizeX, screenSizeY - this->geometry ().y (),
		Qt::KeepAspectRatio, Qt::FastTransformation));
  
  resizeFactor = (img->width()*1.0)/original->width();
  liste_points.clear ();
  
  setFixedSize (img->width (), img->height ());
}

void
WorkArea::loadSL (const std::string & filename)
{
  liste_points.clear();

  QFile fin(QString::fromStdString (filename));
  fin.open(QIODevice::ReadOnly);
  QByteArray ba2 = fin.readAll();
  QJsonParseError parseError;
  QJsonDocument doc2 = QJsonDocument::fromJson(ba2, &parseError);
  QJsonArray lines = doc2["lines"].toArray();
  for(int i=0; i<lines.size(); i++){
    QJsonArray line = lines[i].toArray();
    QJsonArray point1 = line[0].toArray();
    QJsonArray point2 = line[1].toArray();
    liste_points << new QPoint((int)(point1[0].toDouble()*resizeFactor), (int)(point1[1].toDouble()*resizeFactor));
    liste_points << new QPoint((int)(point2[0].toDouble()*resizeFactor), (int)(point2[1].toDouble()*resizeFactor));
  }
}

void
WorkArea::saveStrengthLine (const std::string & filename)
{
  QJsonObject root;
  QJsonArray lines;
  
  for (int i = 0; i < liste_points.size () / 2; i++){
    QJsonArray line;
    QJsonArray point1;
    point1.push_back(liste_points[i*2]->x ()/resizeFactor);
    point1.push_back(liste_points[i*2]->y ()/resizeFactor);
    line.push_back(point1);
    QJsonArray point2;
    point2.push_back(liste_points[i*2+1]->x ()/resizeFactor);
    point2.push_back(liste_points[i*2+1]->y ()/resizeFactor);
    line.push_back(point2);

    lines.push_back(line);
  }
  
  root["lines"] = lines;

  QByteArray ba = QJsonDocument(root).toJson();
  QFile fout(QString::fromStdString (filename));
  fout.open(QIODevice::WriteOnly);
  fout.write(ba);

  this->modificationInProgress = false;
}

void
WorkArea::exportPNG (const std::string & filename)
{
  QImage tempImg(*original);
  QPainter painter(&tempImg);
  for (int i = 0; i < liste_points.length (); i++)
    {
      if ((i % 2) == 1)
	{
	  painter.setPen (QPen (Qt::red, epaisseurLigne/resizeFactor));
	  std::pair < QPoint *, QPoint * > endPoints =
	    StrengthLine (liste_points[i - 1], liste_points[i]).toDraw (&tempImg);
	  painter.drawLine (*(std::get < 0 > (endPoints))/resizeFactor,
			    *(std::get < 1 > (endPoints))/resizeFactor);
	}

    }
  tempImg.save(QString::fromStdString (filename));
  
}

void
WorkArea::setReadOnly(bool readOnly){
  this->readOnly = readOnly;
}

bool
WorkArea::getReadOnly(){
  return this->readOnly;
}

void
WorkArea::setModificationInProgress(bool mip){
  this->modificationInProgress = mip;
}

bool
WorkArea::getModificationInProgress(){
  return this->modificationInProgress;
}


void
WorkArea::paintEvent (QPaintEvent * event)
{
  QPainter painter (this);
  paint (painter);
}

void
WorkArea::paint (QPainter & painter)
{
  if (img != NULL)
    painter.drawImage (0, 0, *img);

  for (int i = 0; i < liste_points.length (); i++)
    {
      if(readOnly==false)
	{
	  painter.setBrush (QBrush (Qt::green));
	  painter.setPen (QPen (Qt::black));
	  painter.drawEllipse (liste_points[i]->x () - taillePoint / 2,
			       liste_points[i]->y () - taillePoint / 2,
			       taillePoint, taillePoint);
	}


      if ((i % 2) == 1)
	{
	  painter.setPen (QPen (Qt::red, epaisseurLigne));
	  std::pair < QPoint *, QPoint * >endPoints =
	    StrengthLine (liste_points[i - 1], liste_points[i]).toDraw (img);
	  painter.drawLine (*(std::get < 0 > (endPoints)),
			    *(std::get < 1 > (endPoints)));
	}

    }

}

void
WorkArea::mousePressEvent (QMouseEvent * event)
{
  if(readOnly==false)
    {
      if (img != NULL)
	{
	  this->modificationInProgress = true;
	  // Est ce qu'on a cliqué sur un point existant ?
	  indexPointClicked = -1;
	  for (int i = 0; i < liste_points.length (); i++)
	    {
	      QPoint point = event->pos () - *(liste_points[i]);
	      if (point.manhattanLength () < taillePoint)
		{
		  indexPointClicked = i;
		}
	    }

	  // Clic gauche
	  if (event->button () == Qt::LeftButton)
	    {
	      if (indexPointClicked == -1)
		{
		  if((maxLines == 0) || (liste_points.size()<(maxLines*2))){
		    indexPointClicked = liste_points.size ();
		    liste_points << new QPoint (event->x (), event->y ());
		  }
		}
	    }
	  // Clic droit
	  if (event->button () == Qt::RightButton)
	    {
	      if (indexPointClicked != -1)
		{
		  if (indexPointClicked == (liste_points.size () - 1))
		    liste_points.removeAt (indexPointClicked);
		  else
		    {
		      if ((indexPointClicked % 2) == 0)
			{
			  QPoint *single =
			    liste_points.takeAt (indexPointClicked + 1);
			  liste_points.removeAt (indexPointClicked);
			  liste_points << single;
			}
		      else
			{
			  QPoint *single =
			    liste_points.takeAt (indexPointClicked - 1);
			  liste_points.removeAt (indexPointClicked - 1);
			  liste_points << single;
			}
		    }
		  indexPointClicked = -1;
		}
	    }
	}
    }
  repaint ();
}

void
WorkArea::mouseMoveEvent (QMouseEvent * event)
{
  if(readOnly==false)
    {
      if (indexPointClicked != -1)
	{
	  liste_points[indexPointClicked]->setX (event->x ());
	  liste_points[indexPointClicked]->setY (event->y ());
	}
    }
  repaint ();
}

void
WorkArea::mouseReleaseEvent (QMouseEvent * event)
{
  indexPointClicked = -1;
}
