import kdu_jni.Kdu_dims;
import kdu_jni.Kdu_coords;
import kdu_jni.Kdu_region_decompressor;

import java.awt.Rectangle;

import java.util.LinkedList;



public class JP2Render implements Runnable
{
  private JP2Image image;                        // Imagen JPEG2000 origen
  private Thread myThread;                       // Hilo creado
  private boolean finish;                        // Indica si se debe parar la ejecucion
  private int buffer[];                          // Buffer donde se almacena el resultado de la descompresion
  private int bufferSize;                        // Tamaño actual del buffer
  private int nextBufferSize;                    // Tamaño del buffer requerido para la siguiente region
  private Kdu_dims incompletePart;               // Parte incompleta de la region actual
  private Kdu_dims decompressedPart;             // La ultima parte descomprimida
  private Rectangle decompressedPartR;           // La ultima parte descomprimida (Rectangle)
  private Kdu_region_decompressor decompressor;  // Descompresor de Kakadu
  private LinkedList regionsList;
  private JP2ImageView actualView;

  public JP2Render(JP2Image jp2Image)
  {
    bufferSize = 0;
    myThread = null;
    image = jp2Image;
    nextBufferSize = 100000;

    incompletePart = new Kdu_dims();
    decompressedPart = new Kdu_dims();
    decompressedPartR = new Rectangle();

    decompressor = new Kdu_region_decompressor();
  }

  public void start()
  {
    if(myThread != null) return;
    if(image.getActualView() == null) return;

    actualView = image.getActualView();
    regionsList = actualView.getRegionsToDecode();

    finish = false;

    myThread = new Thread(this);
    myThread.setPriority(Thread.MIN_PRIORITY);
    myThread.start();
  }

  public void stop()
  {
    if(myThread == null) return;

    finish = true;

    try { myThread.join(); } catch(InterruptedException ex) { }
    myThread = null;
  }

  public boolean isStopped()
  {
    return (myThread == null);
  }

  public void setBufferSize(int size)
  {
    nextBufferSize = size;
  }

  public void run()
  {
    try {
      while(!finish && !regionsList.isEmpty()) {
        Rectangle actualRegion = (Rectangle)regionsList.removeFirst();
        if(!actualView.isContentCompleted()) regionsList.add(actualRegion);

        incompletePart.Access_pos().Set_x(actualRegion.x);
        incompletePart.Access_pos().Set_y(actualRegion.y);
        incompletePart.Access_size().Set_x(actualRegion.width);
        incompletePart.Access_size().Set_y(actualRegion.height);

        if(bufferSize != nextBufferSize) {
          bufferSize = nextBufferSize;
          buffer = new int[bufferSize];
        }

        image.lockCodestream();

        decompressor.Start(image.getCodestream(), image.getChannels(), -1,
                           image.getDiscardLevels(), 0, incompletePart,
                           image.getExpansion());

        while(!finish && decompressor.Process(buffer, new Kdu_coords(0, 0), 0, 0,
                                              bufferSize, incompletePart,
                                              decompressedPart)) {

          decompressedPartR.setBounds(
            decompressedPart.Access_pos().Get_x(),
            decompressedPart.Access_pos().Get_y(),
            decompressedPart.Access_size().Get_x(),
            decompressedPart.Access_size().Get_y()
          );

          actualView.updateNewRegion(decompressedPartR, buffer);
        }

        if(incompletePart.Access_size().Get_x() != 0 &&
           incompletePart.Access_size().Get_y() != 0)

          regionsList.add(new Rectangle(
            incompletePart.Access_pos().Get_x(),
            incompletePart.Access_pos().Get_y(),
            incompletePart.Access_size().Get_x(),
            incompletePart.Access_size().Get_y()
          ));

        decompressor.Finish();
        image.unlockCodestream();
        Thread.yield();
      }

      myThread = null;

      if(regionsList.size() == 0)
        actualView.setCompleted();

    } catch(Exception ex) {

      System.err.println("Error en el sistema de descompresión.");
      ex.printStackTrace();
    }
  }
}
