import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;

class detecta extends Frame {
  Image imagenFuente;                 // Imagen cargada del disco
  int iniAncho;
  int iniAlto;
  // Imagen modificada
  // Valores del borde para el objeto contenedor
  int insetArriba;
  int insetIzqda;
  
  // Método de control del programa
  public static void main( String[] args ) {
    // Se instancia un objeto de esta clase
    detecta obj = new detecta(args);
    }

  // Constructor de la clase
  public detecta(String[] args) {
    kmeans kluster;
    int k = Integer.parseInt(args[1]);
    boolean debug=false;
    if( args.length > 2 ) debug = true;
    // Se carga la imagen desde el fichero que se indique, que se 
    // supone situado en el directorio actual del disco duro
    imagenFuente = Toolkit.getDefaultToolkit().getImage( args[0] );

    // Se utilzia un objeto MediaTracker para bloquear la tarea hasta
    // que la imagen se haya cargado o hayan transcurrido 10 segundos
    // desde que se inicia la carga
    MediaTracker tracker = new MediaTracker( this );
    tracker.addImage( imagenFuente,1 );

    try {
      if( !tracker.waitForID( 1,10000 ) ) {
        System.out.println( "Error en la carga de la imagen" );
        System.exit( 1 );
      }
    } catch( InterruptedException e ) {
      System.out.println( e );
      }
    
    // La imagen ya está cargada, así que se establece la anchura y
    // altura de la ventana, para que sus dimensiones se adecúen a la
    // imagen
    iniAncho = imagenFuente.getWidth( this );
    iniAlto = imagenFuente.getHeight( this );
    // Se hace visible el Frame
    this.setVisible( true );
    
    //Get and store inset data for the Frame object so 
    // that it can be easily avoided.
    insetArriba = this.getInsets().top;
    insetIzqda = this.getInsets().left;

    // Se usan las dimensiones de insets y el tamaño de la imagen
    // fuente para establecer el tamaño total del Frame. La altura se
    // hace doble, para que se puedan presentar la imagen original y
    // debajo de ella, la imagen modificada. Exactamente no es doble, 
    // para permitir que la imagen modificada se superponga un poco
    // sobre la original.    
    this.setSize( insetIzqda+iniAncho,insetArriba+iniAlto );
    this.setTitle( "IA1 - Practica 1" );
    this.setBackground( Color.black );

    // Se declara un array para guardar la representación de la imagen
    // en pixels individuales
    int[] pix = new int[iniAncho * iniAlto];
    // Se convierte la "imagenFuente" a representación numérica que
    // corresponde a sus pixels, de forma que se puedan manipular
    // Esto hay que colocarlo en un bloque try-catch, porque tenemos
    // que estar prevenidos par recoger las excepciones de tipo
    // "InterrruptedException" que puede lanzar el método grabPixels()
    try {
      // Se instancia un objeto de tipo PixelGrabber, pasándole como
      // parámetro el array de pixels en donde queremos guardar la 
      // representación numérica de la imagen que vamos manipular
      PixelGrabber pgObj = new PixelGrabber( imagenFuente,
        0,0,iniAncho,iniAlto,pix,0,iniAncho );

      // Se invoca ahora el método grabPixels() sobre el objeto de tipo
      // PixelGrabber que se acaba de instanciar, para la imagen se
      // convierta en un array de pixels. también se comprueba que el
      // proceso se realiza satisfactoriamente
      if( pgObj.grabPixels() && 
        ( (pgObj.getStatus() & ImageObserver.ALLBITS ) != 0 ) ) {
        for( int i=0; i < (iniAncho*iniAlto); i++ ) {
          pix[i] = pix[i] & 0xFFFFFFFF;
          }
        }
      else {
        System.out.println( "Problemas al descomponer la imagen" );
        }
    } catch( InterruptedException e ) {
      System.out.println( e );
      }
/**
* Constructor kmeans: kmeans( k, img, tamanyo, debug)
*   k = numero de clases a encontrar
*   img = pixeles de la imagen en un int[]
*   tamanyo = tamanyo de la imagen en pixeles, igual que tamanyo de img
*   debug = bool, activa el debug
*/
    kluster=new kmeans(k,pix,(iniAncho*iniAlto),debug);
    int[] colores=kluster.getClusters();
    if(debug) for( int i=0; i < k; i++) System.out.println("Color k-means " + i + ": rojo=" + ( (colores[i] & 0xFF0000) / 0x10000) +"; verde=" + ( (colores[i] & 0x00FF00) / 0x100) + " azul=" + (colores[i] & 0x0000FF) );

    // Ahora se utiliza el método createImage() para obtener una nueva
    // imagen a partir del array de pixels que hemos alterado
    imagenFuente = this.createImage( new MemoryImageSource(iniAncho,iniAlto,kluster.getkImage(),0,iniAncho ) );

    // Se llama directamente al método que va a presentar la imagen
    this.repaint();

    /**
    * Ahora adaptamos la imágen a la nueva paleta, la del ejercicio.
    */
    int[] paleta = new int[11];
    paleta[0] = Color.white.getRGB() & 0x00FFFFFF;
    paleta[1] = Color.gray.getRGB() & 0x00FFFFFF;
    paleta[2] = Color.black.getRGB() & 0x00FFFFFF;
    paleta[3] = Color.red.getRGB() & 0x00FFFFFF;
    paleta[4] = Color.green.getRGB() & 0x00FFFFFF;
    paleta[5] = Color.blue.getRGB() & 0x00FFFFFF;
    paleta[6] = Color.yellow.getRGB() & 0x00FFFFFF;
    paleta[7] = Color.orange.getRGB() & 0x00FFFFFF;
    paleta[8] = 0xA52A2A; // brown
    paleta[9] = 0x800080; // purple
    paleta[10] = Color.pink.getRGB() & 0x00FFFFFF;

    kluster=new kmeans(k,pix,(iniAncho*iniAlto),false);

    kluster.setPaleta(paleta,11);
    colores = kluster.getPaleta();
    for( int i=0; i<k; i++) System.out.println("Color k-paleta " + i + ": rojo=" + ( (colores[i] & 0xFF0000) / 0x10000) +"; verde=" + ( (colores[i] & 0x00FF00) / 0x100) + " azul=" + (colores[i] & 0x0000FF) );

    imagenFuente = this.createImage( new MemoryImageSource(iniAncho,iniAlto,kluster.getkImagep(),0,iniAncho ) );

    // Se llama directamente al método que va a presentar la imagen
    this.repaint();

    System.out.println("----fin-----");


    // Clase anidada que permite terminar la ejecución de la animación
    this.addWindowListener(
      // Definición de la clase anónima para controlar el cierre de
      // la ventana
      new WindowAdapter() {
        public void windowClosing( WindowEvent evt ) {
          // Se concluye el programa
          System.exit( 0 );
        }
      }
    );
  }
  
  // Sobrecargamos el método paint() para presentar las dos imágenes
  // del ejemplo, la original leida del fichero, y la modificada que se
  // ha creado tras la manipulación de los pixels de la original
  public void paint( Graphics g ) {
    if( imagenFuente != null ) {
      g.drawImage( imagenFuente,insetIzqda,insetArriba,this );
      }
    }
  }
