/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*                                                                           */
/*                  This file is part of the program and library             */
/*         SCIP --- Solving Constraint Integer Programs                      */
/*                                                                           */
/*  Copyright (c) 2002-2023 Zuse Institute Berlin (ZIB)                      */
/*                                                                           */
/*  Licensed under the Apache License, Version 2.0 (the "License");          */
/*  you may not use this file except in compliance with the License.         */
/*  You may obtain a copy of the License at                                  */
/*                                                                           */
/*      http://www.apache.org/licenses/LICENSE-2.0                           */
/*                                                                           */
/*  Unless required by applicable law or agreed to in writing, software      */
/*  distributed under the License is distributed on an "AS IS" BASIS,        */
/*  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */
/*  See the License for the specific language governing permissions and      */
/*  limitations under the License.                                           */
/*                                                                           */
/*  You should have received a copy of the Apache-2.0 license                */
/*  along with SCIP; see the file LICENSE. If not visit scipopt.org.         */
/*                                                                           */
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

/**
 * @author Timo Berthold
 * Frame displaying solutions of a TSP and updating from file
 */

import java.awt.Graphics;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.util.LinkedList;
import java.util.ListIterator;
import java.util.StringTokenizer;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JLabel;
import javax.swing.JButton;
import java.awt.BorderLayout;

public class TSPViewer extends Thread {
	
   private static File file;
   private static File lock; //lock file forbidding other programms to change the file during it is read
   private int actionsteps = 0;
   
   public TSPViewer(String s)
   {
      file = new File(s);	
      lock = new File(s+".lock");
      System.out.println("Watching file <" + file.getPath() + ">");
   }
	
   // read in a file
   private static TSPSolution readFile(int number)
   {
		
      int nnodes = 0;
      int i;
      String name = "";
      double obj = -1.0;
	  
      double[][] coords = new double[2][];

      try
      {
         String line;
         String token;
         StringTokenizer strtok;
         int count = 0;
			
         // if the file is in use, wait
         while( lock.exists() && count < 10 )
         {
            sleep(500);
            count++;
         }
         if( lock.exists() )
         {
            System.out.println("Cannot read file <" + file.getPath() + ">, because lockfile <"
               + lock.getPath() + "> still exists");
            return null;
         }
			
         // create lock file forbidding other programms to use the file while we read
         lock.createNewFile();
         BufferedReader in = new BufferedReader(new FileReader(file));
         
         if( (line = in.readLine()) != null )
         {
            if( line.equals("RESET") )
            {
               lock.delete();
               in.close();
               return null;
            }
            else	
               nnodes = Integer.parseInt(line); 
         }
         if( ((line = in.readLine()) != null ))
            name = new String(line);
         if( ((line = in.readLine()) != null ))
            obj = Double.parseDouble(line);
         
         
         coords[0] = new double[nnodes];
         coords[1] = new double[nnodes];
			
         // read out the coordinates
         i = 0;	
         while( i < nnodes && (line = in.readLine()) != null )
         {         
            strtok = new StringTokenizer(line);	
            strtok.nextToken();
            coords[0][i] = Double.parseDouble(strtok.nextToken());
            coords[1][i] = Double.parseDouble(strtok.nextToken());		
            i++;
         }
         in.close();
         lock.delete();
      } 
      catch( Exception e )
      {
         System.err.println("TSP Viewer: TSPFrame: File input error");
      }

      return new TSPSolution(coords, obj, number, name);	
   }
  
   /** 
    * monitors whether the input file has been changed and if so, repaints.
    */
   public void run()
   {
      long updated = file.lastModified();
	  	
	  
      // create and set up the frame
      JFrame.setDefaultLookAndFeelDecorated(true);
      JFrame frame = new JFrame("TSP Viewer");
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.setSize(800, 600);
      Graphics g = frame.getGraphics();
        
      // add the components
      TSPPanel tsppanel = new TSPPanel();    
      JPanel panel = new JPanel();
      JButton prev = new JButton("<<");
      prev.addActionListener( new ActionListener() {
            public void actionPerformed(ActionEvent e) {
               actionsteps--;
            }
         } );
      prev.setEnabled(false);
      JButton next = new JButton(">>");
      next.addActionListener( new ActionListener() {
            public void actionPerformed(ActionEvent e) {
               actionsteps++;
            }
         } );
      next.setEnabled(false);
      JLabel label = new JLabel("No solution found yet");
      
      panel.add(prev);
      panel.add(next);
      panel.add(label);
      
      frame.getContentPane().setLayout(new BorderLayout());
      frame.getContentPane().add(tsppanel, BorderLayout.CENTER);
      frame.getContentPane().add(panel, BorderLayout.SOUTH);
      tsppanel.paint(g);

      // display the window
      frame.setVisible(true);

      
      LinkedList solutions = new LinkedList();
      int currsol = 0;
      updated = -1;
      int nsols = 0;
      // keep active as long as the frame exists	
      while( frame != null )
      {
         // check, whether file has been updated. If so, repaint
         long upd = updated;
         if( file.exists() )
            upd = file.lastModified();
         if( upd != updated )
         {
            updated = upd;
            TSPSolution sol = readFile(nsols+1);
            
            if( sol == null )
            {
               nsols = 0;
               currsol = 0;
               next.setEnabled(false);
               prev.setEnabled(false);          
               label.setText("no solution found yet");
               tsppanel.setTour(null);
               solutions = new LinkedList();
            }
            else
            {
               solutions.addLast( sol );
               tsppanel.setTour(sol.getTourcoords());
               label.setText(sol.getNumber()+". solution, found by "+sol.getHeur()+" , objective: "+sol.getObjval());
               currsol = nsols;
               nsols++;
               if(nsols > 1)
                  prev.setEnabled(true);
               next.setEnabled(false);
               actionsteps = 0;
            }             
            frame.repaint();
         } 
         // check if action was performed on the buttons. If so, repaint
         else if( actionsteps != 0 )
         {
            currsol = Math.max(0, Math.min(currsol+actionsteps, nsols-1));
            ListIterator it = solutions.listIterator(currsol);
            TSPSolution sol = (TSPSolution) it.next();
            tsppanel.setTour(sol.getTourcoords());
            /*double[][] t = sol.getTourcoords();
              System.out.print("Painting tour number " + sol.getNumber()+": ");
              for(int j = 0; j < t.length; j++)
              System.out.print("("+t[j][0]+"|"+t[j][1]+") - ");
              System.out.print("\n");*/
            label.setText(sol.getNumber()+". solution, found by "+sol.getHeur()+" , objective: "+sol.getObjval());
            if( currsol != 0 )
               prev.setEnabled(true);
            else
               prev.setEnabled(false);
            if( currsol != nsols -1 )
               next.setEnabled(true);
            else
               next.setEnabled(false);
            actionsteps = 0;
            frame.repaint();
         }
         // if nothing has happened, sleep a bit
         else
         {
            try 
            {
               sleep(500);
            } 
            catch( InterruptedException e )
            {
               e.printStackTrace();
            }
         }
      }
   }
	
   /**
    * creates one Frame and makes it run
    * @param args The name of the file to display
    */
   public static void main(String[] args) 
   {
      String filename = "../temp.tour";

      if( args.length >= 1 )
         filename = args[0];

      TSPViewer window = new TSPViewer(filename);
      window.run();
   }
}
