import javax.swing.*;
import java.awt.*;
import java.awt.image.*;
import java.awt.event.*;
import java.net.*;
import java.util.Locale;
import java.io.*;



/**
 * Demonstracja dekompozycji falkowej obrazu, kwantyzacji, kompresji i dekompresji obrazów
 * @author  jwr
 */
public class Jpeg2000DemoLocal extends JFrame{
    public static final String INFO="INFO:";
    
    public static final int BANKS_LIMIT=5; 
    public static final int GAIN=10; 
    public ImagePanel imagePanel=new ImagePanel();
    public ImagePanel mrwPanel=new ImagePanel();
    public JTextArea infoArea=new JTextArea(INFO);
    public JPanel controlPanel=new JPanel();
    public JButton reconstruct=new JButton("Odtwórz");
    public JButton info=new JButton("Info");
    public JButton moreBanks=new JButton("+ poziom");
    public JButton lessBanks=new JButton("- poziom");
    public JButton openImage=new JButton("Otwórz obraz");
    public JButton quantization=new JButton("Kwantyzuj");
    public JButton quantizationBP=new JButton("KwantyzujBP");
    public JButton end=new JButton("KONIEC");
    public JTextField qStep = new JTextField("1");
    public JTextField qStepBP = new JTextField("8");
    public JTextField recBanks = new JTextField("1");
    JPanel leftPanel = new JPanel();
    JPanel rightPanel = new JPanel();
    public static File katalog=null;
    public PrintStream log=System.out;
    
    public int[] imageData;
    public double[] data;
    public double [][] dataTMP=null;
    public int w;
    public int h;
    public int step=1;
    public int stepBP=8;
    public JFrame parent;
    public int noOfBanks=1;
    public int banks=1;
    public boolean procesStatus = false;
    
    
    
    /** Creates a new instance of Jpeg2000Demo */
    public Jpeg2000DemoLocal() {
        super("Wymiana obrazow z zastosowaniem dekompozycji falkowej i kwantyzacji JPEG2000 - Jacek Ruminski");
        parent=this;
        init();
    }
    
    public void init(){
        
        JPanel infoGroup = new JPanel();
        infoGroup.setLayout(new GridLayout(4,1));
        setLayout(new GridLayout(1,2));
        leftPanel.setLayout(new BorderLayout());
        rightPanel.setLayout(new BorderLayout());
        getContentPane().add(leftPanel);
        getContentPane().add(rightPanel);
        JPanel qant= new JPanel();
        qant.setLayout(new GridLayout(1,2));
        qant.add(new JLabel("PODAJ SKOK (MIANOW.) KWANTYZACJI:"));
        qStep.setColumns(20);
        qant.add(qStep);
      
        
        JPanel qantBP= new JPanel();
        qantBP.setLayout(new GridLayout(1,2));
        qantBP.add(new JLabel("L. BITÓW (PŁASZCZ.) DO KWANTYZACJI:"));
        qStepBP.setColumns(20);
        qantBP.add(qStepBP);
        
        JPanel banksP= new JPanel();
        banksP.setLayout(new GridLayout(1,2));
        banksP.add(new JLabel("PODAJ L. WARSTW DO REKONSTRUKCJI:"));
        recBanks.setColumns(20);
        banksP.add(recBanks);
        leftPanel.add(imagePanel,BorderLayout.CENTER);
        
        infoGroup.add(qant);
        infoGroup.add(qantBP);
        infoGroup.add(banksP);
        infoGroup.add(infoArea);
        leftPanel.add(infoGroup,BorderLayout.SOUTH);
        
        rightPanel.add(mrwPanel,BorderLayout.CENTER);
        rightPanel.add(controlPanel,BorderLayout.SOUTH);
        controlPanel.setLayout(new GridLayout(2,4));
        controlPanel.add(openImage);
        
        controlPanel.add(moreBanks);
        controlPanel.add(lessBanks);
        controlPanel.add(reconstruct);
        controlPanel.add(info);
        controlPanel.add(quantization);
        controlPanel.add(quantizationBP);
        end.setForeground(Color.red);
        controlPanel.add(end);
        
        imagePanel.setPreferredSize(new Dimension(512, 512));
        mrwPanel.setPreferredSize(new Dimension(512, 512));
        
        info.addActionListener(new ActionListener(){
            public void actionPerformed(ActionEvent ae){
               JOptionPane.showMessageDialog(parent,new String("Program demonstracyjny elementy JPEG2000: \nsterowaniem dynamiką rozdzielczości przestrzennej i jakości.\n Jacek Rumiński"),new String("INFORMACJI !"),JOptionPane.INFORMATION_MESSAGE   ); 
                
            }
         });
        
        moreBanks.addActionListener(new ActionListener(){
            public void actionPerformed(ActionEvent ae){
                if (noOfBanks< BANKS_LIMIT) {
                    //if (!procesStatus){
                        noOfBanks++;
                        infoArea.setText("LICZBA WARSTW = "+noOfBanks);
                        process();
                    //}
                }
                
            }
         });
         
         lessBanks.addActionListener(new ActionListener(){
            public void actionPerformed(ActionEvent ae){
                if (noOfBanks>1) {
                    //if (!procesStatus) {
                        noOfBanks--;
                        infoArea.setText("LICZBA WARSTW = "+noOfBanks);
                        process();
                    //}
                }
            }
         });
         
         reconstruct.addActionListener(new ActionListener(){
            public void actionPerformed(ActionEvent ae){
                String recBanksS = recBanks.getText();
                try{
                    
                    banks=Integer.parseInt(recBanksS);
                    
                    System.out.println("L. poz. do rek.: "+banks+", l. poz.: "+noOfBanks+", limit: "+BANKS_LIMIT);
                    if (banks>noOfBanks) throw (new NumberFormatException());
                    //System.out.println("Liczba BANKS: "+noOfBanks);
                    
                    if(banks>BANKS_LIMIT) throw (new NumberFormatException());
                    //noOfBanks=banks;
                    if (procesStatus) reconstruct();
                    
                }catch (NumberFormatException e){
                    JOptionPane.showMessageDialog(parent,new String("Wartość liczby warstw musi być liczbą całkowitą, mniejszą od "+BANKS_LIMIT+". \n Dodatkowe informacje: "+e),new String("OSRZEŻENIE !"),JOptionPane.WARNING_MESSAGE); 
                }
                
            }
         });
         
        quantization.addActionListener(new ActionListener(){
            public void actionPerformed(ActionEvent ae){
                String stepS = qStep.getText();
               
                try{
                    step=Integer.parseInt(stepS);
                    Quantization q=new Quantization();
                    dataTMP=q.quantizy(dataTMP, step);
                    
                }catch (Exception e){
                    JOptionPane.showMessageDialog(parent,new String("Wartość kroku kwantyzacji musi być liczbą całkowitą"),new String("OSRZEŻENIE !"),JOptionPane.WARNING_MESSAGE); 
                }
              
                 
            }
        });
        
        
        quantizationBP.addActionListener(new ActionListener(){
            public void actionPerformed(ActionEvent ae){
                
                String stepBPS = qStepBP.getText();
               
                try{
                    stepBP=Integer.parseInt(stepBPS);
                    Quantization q=new Quantization();
                    dataTMP=q.quantizyBP(dataTMP, stepBP);
                    
                }catch (Exception e){
                    JOptionPane.showMessageDialog(parent,new String("Wartość kroku kwantyzacji musi być liczbą całkowitą"),new String("OSRZEŻENIE !"),JOptionPane.WARNING_MESSAGE); 
                }
                 
            }
        });
        
        
        end.addActionListener(new ActionListener(){
            public void actionPerformed(ActionEvent ae){
                shutdown();
            }
        });
        
        openImage.addActionListener(new ActionListener(){
            public void actionPerformed(ActionEvent ae){
                URL imageFileURL=openFileDialog();
                //Image img = Toolkit.getImage(imageFileURL);                
                if(imageFileURL!=null){
                    Image img = Toolkit.getDefaultToolkit().getImage(imageFileURL); 
                    MediaTracker mt = new MediaTracker(parent);
                    mt.addImage(img,0);
                    if (mt.isErrorAny()) System.out.println("BŁĄD ŁADOWANIA OBRAZU");
                    try { mt.waitForID(0); }
                    catch (InterruptedException ie) {return;  }
                    if (mt.isErrorID(0)) return;
                    w = img.getWidth(null);
                    h = img.getHeight(null);
                    if((w>512)||(h>512)){
                        JOptionPane.showMessageDialog(parent,new String("Obraz za duży skaluję do rozmiaru do 512/512"),new String("OSRZEŻENIE !"),JOptionPane.WARNING_MESSAGE);
                        double scale=1;
                        if(w>=h)
                            scale=w/512.0;
                        else  
                            scale=h/512.0;
                        w=(int)(w/scale);
                        h=(int)(h/scale);
                        //PONI�EJ TYMCZASOWE USTAWIENIE - BRAK DODAWANIA LINII CZY KOLUMN
                        w=512;
                        h=512;
                        img=img.getScaledInstance(w,h,Image.SCALE_SMOOTH);
                        mt = new MediaTracker(parent);
                        mt.addImage(img,0);
                        if (mt.isErrorAny()) System.out.println("BŁĄD ŁADOWANIA OBRAZU");
                        try { mt.waitForID(0); }
                        catch (InterruptedException ie) {return;  }
                        if (mt.isErrorID(0)) return;
                    }
                    
                    BufferedImage bObraz = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
                    Graphics2D big = bObraz.createGraphics();
                    big.drawImage(img,0,0,null);
                    bI_to_int(bObraz);
                    resetImagePanel();
                    dataTMP=null;
                    addWaveletPanel(imageData);
                    //ob=new Obraz(bObraz);
                }
                    //openImage(imageFileURL);
                
            }
        });
        
    }
    
    
    
    private URL openFileDialog(){
        URL fileURL=null;
        boolean cancel=false;
        try{
            JFileChooser chooser = new JFileChooser();
            if (Jpeg2000Demo.katalog!=null)
                   chooser.setCurrentDirectory(Jpeg2000Demo.katalog);

            chooser.setLocation(this.getWidth()+10,0);
            chooser.setBackground(Color.black);
            chooser.setDialogTitle("OTWÓRZ");
            chooser.setApproveButtonText("OTWÓRZ");
            chooser.setToolTipText("Tekst");
            chooser.setDefaultLocale(new Locale("pl","PL"));
            int returnVal = chooser.showOpenDialog(this);
            
            if(returnVal == JFileChooser.APPROVE_OPTION) {
                  File file=chooser.getSelectedFile();
                  fileURL=new URL("file","localhost",file.getAbsolutePath());
                  log.println("OTO URL: "+fileURL);
            }else
                   cancel=true;
            Jpeg2000Demo.katalog=chooser.getCurrentDirectory();
            log.println("KATALOG: "+Jpeg2000Demo.katalog.getAbsolutePath());
        }catch(Exception e){
            if(!cancel)
                log.println("Błąd dostępu do pliku, przy próbie jego otwarcia! Kod"+e);
        }
 


        return fileURL;
    }//private URL openFileDialog(){
    
    public static void shutdown(){
        System.exit(1);
    }//shutdown()
    
    
    
    /**
    * bI_to_int - metoda pobrania wartości macierzy obrazu z jego 
    * reprezentacji w formie BufferedImage, do postaci macierzy luminancji 
    * Y=(R+G+B)/3
    */
    public void bI_to_int(BufferedImage bOb){
            /*
            int w=bOb.getWidth();
            int h=bOb.getHeight();
            this.w=w;
            this.h=h;
             */
             log.println("Width:"+w);
             log.println("Height:"+h);
            int MAX=Integer.MIN_VALUE;
            int MIN=Integer.MAX_VALUE;
            //int [][]mob=new int[h][w];
            imageData=new int[w*h];
            data=new double[w*h];
            int pixel=0;
            int R=0,G=0,B=0;
            for(int i=0;i<w;i++){
                    for(int j=0;j<h;j++){
                            pixel=bOb.getRGB(i,j);
                            B=pixel&0xFF;
                            G=(pixel>>8)&0xFF;
                            R=(pixel>>16)&0xFF;
                            //mob[j][i]=(R+G+B)/3;
                            int l=(R+G+B)/3;
                            data[j*w+i]=(double)l;
                            imageData[j*w+i]=(255 << 24)| (l << 16) |(l << 8) | l;
                            if (l>MAX) MAX=l;
                            if (l<MIN) MIN=l;
                            //if (mob[j][i]>MAX) MAX=mob[j][i];
                            //if (mob[j][i]<MIN) MIN=mob[j][i];
                    }
            }
           
            //imageData=Macierz.macWektor(h,w,mob);
            //mob=new int[1][1];
    }// koniec bI_to_int
    
    public void resetImagePanel(){
        //Graphics g=imagePanel.getGraphics();
        Image img=produceImage(imageData, w,h);
        imagePanel.setImage(img);
        validate();
        repaint();
    
    }//end of resetImagePanel
    
    public void addWaveletPanel(int []imageData){
        infoArea.setText("LICZBA WARSTW = "+noOfBanks);
        process();
    }
    
    /**
     * Dekompozycja falkowa obrazu
     */
    public void process(){
        Jpeg2000Procesor jp = new Jpeg2000Procesor(5);
        if (dataTMP==null)
            dataTMP = new double[h][w];
        int offset=1;
        double [][] dataPart;
        //if(!procesStatus){
            for(int i=0; i<h; i++){
                offset=i*w;
                 for(int j=0; j<w; j++){
                     dataTMP[i][j]= data[offset+j];
                     //if(i==5) System.out.print(";"+dataTMP[i][j]);
                 }
            }
        //}
        
        for (int i=0; i<noOfBanks; i++){
            if(i>0){
                dataPart=Jpeg2000Data.getBank(dataTMP,i);
                dataPart=jp.procesDWT(dataPart,  5, true);
                dataPart=jp.procesDWT(dataPart,  5,  false);
                dataTMP=Jpeg2000Data.setBank(dataTMP,dataPart);
            } else {
                dataTMP=jp.procesDWT(dataTMP,  5, true);
                dataTMP=jp.procesDWT(dataTMP,  5,  false);
            }
        }
        
        int [] results = new int[w*h];
        offset=1;
        int l;
        int max=Integer.MIN_VALUE;
        int min=Integer.MAX_VALUE;
        int h_half=h/(2);
        int w_half=w/(2);
        for(int i=0; i<h; i++){
            offset=i*w;
             for(int j=0; j<w; j++){
                 l=results[offset+j]=(int)dataTMP[i][j];
                 if (l<min) min =l;
                 if(l>max) max=l;
                 if((i>h_half)||(j>w_half)){
                     l=results[offset+j]=GAIN*l;
                 }else {
                     
                 }
                    
             }
        }
        
        double a = 255.0/(max-min);
        double b= -1*a*min;
        //a=1.0;
        //b=0.0;
        //System.out.println("Oto skal:"+a+", "+b);
        for (int i=0; i<results.length; i++){
            l=(int)(a*results[i]+b);
            results[i]=(255 << 24)| (l << 16) |(l << 8) | l;
        }
         
        mrwPanel.setImage(produceImage(results,w,h));
        rightPanel.validate();
        rightPanel.repaint();
        validate();
        repaint();
        procesStatus=true;
    }
    
    
    /**
     * Dekompresja - rekonstrukcja obrazu
     */
    public void reconstruct(){
        //mrwPanel=new ImagePanel();
        
        Jpeg2000Procesor jp = new Jpeg2000Procesor(5);
        //dataTMP = new double[h][w];
        int offset=1;
        int diff=noOfBanks-banks;
        
        int rH=h;
        int rW=w;
        if (diff>0) rH= (int)(h/(Math.pow(2.0,(double)(diff))));
        if (diff>0) rW= (int)(w/(Math.pow(2.0,(double)(diff))));
        
        
        double [][] dataRES = new double [rH][rW];
        for(int i=0; i<rH; i++){
            offset=i*rW;
             for(int j=0; j<rW; j++){
                 dataRES[i][j]= dataTMP[i][j];
                 //if(i==5) System.out.print(";"+dataTMP[i][j]);
             }
        }
        System.out.println("TEST przed IWT, banks= "+banks);
         double [][] dataPart;
         for (int i=banks; i>0; i--){
            if(i>1){
                dataPart=Jpeg2000Data.getBank(dataRES,(i-1));
                dataPart=jp.procesIDWT(dataPart,  5, false);
                dataPart=jp.procesIDWT(dataPart,  5,  true);
                dataRES=Jpeg2000Data.setBank(dataRES,dataPart);
                System.out.println("TEST nr "+i);
            } else {
                    dataRES=jp.procesIDWT(dataRES,  5, false);
                    dataRES=jp.procesIDWT(dataRES,  5, true);
            }
         }
        System.out.println("data:");
        int [] results = new int[rW*rH];
        offset=1;
        int l;
        int max=Integer.MIN_VALUE;
        int min=Integer.MAX_VALUE;
        for(int i=0; i<rH; i++){
            offset=i*rW;
             for(int j=0; j<rW; j++){
                 l=results[offset+j]=(int)dataRES[i][j];
                 //if(i==5) System.out.print(";"+dataTMP[i][j]);
                 if(l>max) max=l;
                 if (l<min) min =l;         
             }
        }
        
        double a = 255.0/(max-min);
        double b= -1*a*min;
        System.out.println("Oto skal:"+a+", "+b);
        for (int i=0; i<results.length; i++){
            l=(int)(a*results[i]+b);
            l=l&0xff;
            results[i]=(255 << 24)| (l << 16) |(l << 8) | l;
        }
         
        mrwPanel.setImage(produceImage(results,rW,rH));
        rightPanel.validate();
        rightPanel.repaint();
        validate();
        repaint();
        procesStatus=false;
        noOfBanks=0;
    }
    
    public Image produceImage(int [] imageData, int w, int h){
       
       Image img = createImage(new MemoryImageSource(w, h, imageData, 0, w)); 
       
        //tworzony jest obraz w RGB o szerokości w, wysokości h, 
        //na podstawie tablicy próbek pix, bez przesunięcia w tej tablicy z w elementami w linii 
        return img; 
   
    
    }
    
    public static void main (String args[]){
        Jpeg2000DemoLocal jp = new Jpeg2000DemoLocal();
        jp. setSize(1024,768);
        jp.setVisible(true);
    }//end of main()
    
}//class
