/*

  xmunipack - display renderer

  Copyright © 2018-9 F.Hroch (hroch@physics.muni.cz)

  This file is part of Munipack.

  Munipack 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.

  Munipack 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 Munipack.  If not, see <http://www.gnu.org/licenses/>.

*/

#include "xmunipack.h"
#include "display.h"
#include <wx/wx.h>
#include <wx/thread.h>
#include <vector>

using namespace std;

// ---- DisplayShrinkRender ---------------------------------

DisplayShrinkRender::DisplayShrinkRender(wxEvtHandler *eh, const FitsImage& i,
					 const FitsTone& t, int s, int id):
  wxThread(wxTHREAD_DETACHED),handler(eh),image(i),tone(t),shrink(s),thisid(id)
{
  wxASSERT(handler && shrink >= 1 && image.IsOk() );
  //  wxLogDebug("DisplayShrinkRender id = %d",thisid);
}

DisplayShrinkRender::~DisplayShrinkRender()
{
  //  wxLogDebug("DisplayShrinkRender::~DisplayShrinkRender");

  wxCriticalSectionLocker
    enter(static_cast<MuniDisplayCanvas *>(handler)->renderCS);
  static_cast<MuniDisplayCanvas *>(handler)->render = 0;
}

wxThread::ExitCode DisplayShrinkRender::Entry()
{
  bool test_destroy = false;

  FitsImage output;
  FitsBaseDisplay *display;
  if( image.IsColour() ) {
    display = new FitsColourDisplay(image);
    long ns[3] = {image.GetWidth() / shrink, image.GetHeight() / shrink, 3};
    output = FitsImage(3,ns);
  }
  else {
    display = new FitsGrayDisplay(image);
    long ns[2] = {image.GetWidth() / shrink, image.GetHeight() / shrink};
    output = FitsImage(2,ns);
  }
  display->SetShrink(shrink);
  display->SetTone(tone);

  // length of square side
  // This value is choosed to be the duration of a single iteration
  // shorter than an user reaction time (~0.1 sec). Note that
  // short sides implicates frequent generation of events and memory
  // allocations, so the value should by adjusted carefully.
  int side = min(max(shrink,3*137),min(image.GetHeight(),image.GetWidth()));
  int box = side / shrink;
  side = box*shrink;

  int y0 = image.GetHeight();
  int j0 = image.GetHeight() - side;
  int m0 = output.GetHeight() - box;
  for(int j = 0, m = 0; j < image.GetHeight(); j = j + side, m = m + box) {
    int y = max(j0 - j,0);
    int h = y0 - y;
    y0 = y;
    for(int i = 0, n = 0; i < image.GetWidth(); i = i + side, n = n + box) {
      int x = i;
      int w = min(side,image.GetWidth() - i);

      //      wxLogDebug("render: %d %d %d %d %d %d",x,y,n,m,w,h);
      if( w / shrink > 0 && h / shrink > 0 ) {

	// rendering
	FitsImage sub(image.GetSubImage(x,y,w,h));
	display->SetImage(sub);
	display->SetOperations(OP_TUNE_ZOOM | OP_TUNE_SCALE | OP_TUNE_RGB);
	display->Render();

	FitsImage img(display->GetShrinked());
	output.SetSubImage(n,max(m0-m,0),img);

	// immediate update
	MuniRenderEvent ev(EVT_RENDER,ID_SUBRENDER);
	ev.id = thisid;
	ev.picture = display->GetBitmap();
	ev.x = n;
	ev.y = m;
	wxQueueEvent(handler,ev.Clone());
      }

      if( TestDestroy() ) {
	test_destroy = true;
	goto breake;
      }
    } // i
  } // j

 breake:

  MuniRenderEvent ev(EVT_RENDER,ID_RENDER_SHRINK);
  ev.image = test_destroy ? FitsImage() : output;
  ev.completed = test_destroy == false;
  wxQueueEvent(handler,ev.Clone());

  delete display;

  return (wxThread::ExitCode) 0;
}



// ---   TUNE rendering  ---------------------------------------------

DisplayTuneRender::DisplayTuneRender(wxEvtHandler *eh, const FitsImage& i,
				     const FitsTone& t, const FitsItt& l,
				     const FitsPalette& p, const FitsColor& c,
				     int id):
  wxThread(wxTHREAD_DETACHED),handler(eh),image(i),tone(t),itt(l),pal(p),
  colour(c),thisid(id)
{
  wxASSERT(handler && image.IsOk() );
  //  wxLogDebug("DisplayTuneRender id = %d",thisid);
}

DisplayTuneRender::~DisplayTuneRender()
{
  //  wxLogDebug("DisplayTuneRender::~DisplayTuneRender");

  wxCriticalSectionLocker
    enter(static_cast<MuniDisplayCanvas *>(handler)->renderCS);
  static_cast<MuniDisplayCanvas *>(handler)->render = 0;
}

wxThread::ExitCode DisplayTuneRender::Entry()
{
  bool test_destroy = false;

  FitsBaseDisplay *display;
  if( image.IsColour() ) {
    display = new FitsColourDisplay(image);
    display->SetColour(colour);
  }
  else {
    display = new FitsGrayDisplay(image);
    display->SetPalette(pal);
  }
  display->SetTone(tone);
  display->SetItt(itt);

  int side = min(3*137,min(image.GetHeight(),image.GetWidth()));

  int y0 = image.GetHeight();
  int j0 = image.GetHeight() - side;
  for(int j = 0; j < image.GetHeight(); j = j + side) {
    int y = max(j0 - j,0);
    int h = y0 - y;
    y0 = y;
    for(int i = 0; i < image.GetWidth(); i = i + side) {
      int x = i;
      int w = min(side,image.GetWidth() - i);

      //      wxLogDebug("render: %d %d %d %d %d %d",x,y,i,j,w,h);
      if( w > 0 && h > 0 ) {

	// rendering
	FitsImage sub(image.GetSubImage(x,y,w,h));
	display->SetImage(sub);
	display->SetOperations(OP_TUNE_SCALE | OP_TUNE_ITT | OP_TUNE_PAL |
			       OP_TUNE_COLOUR | OP_TUNE_RGB);
	display->Render();

	// immediate update
	MuniRenderEvent ev(EVT_RENDER,ID_SUBRENDER);
	ev.id = thisid;
	ev.picture = display->GetBitmap();
	ev.x = i;
	ev.y = j;
	wxQueueEvent(handler,ev.Clone());
      }

      if( TestDestroy() ) {
	test_destroy = true;
	goto breake;
      }
    } // i
  } // j

 breake:

  MuniRenderEvent ev(EVT_RENDER,ID_RENDER_TUNE);
  ev.completed = test_destroy == false;
  wxQueueEvent(handler,ev.Clone());

  delete display;

  return (wxThread::ExitCode) 0;
}
