/****************************************************************************
  $Workfile: FileSpecialHandler.java $
  $Revision: 1 $
  $Modtime:: $
  $Copyright:

  Copyright (c) 1997 Novell, Inc.  All Rights Reserved.

  THIS WORK IS  SUBJECT  TO  U.S.  AND  INTERNATIONAL  COPYRIGHT  LAWS  AND
  TREATIES.   NO  PART  OF  THIS  WORK MAY BE  USED,  PRACTICED,  PERFORMED
  COPIED, DISTRIBUTED, REVISED, MODIFIED, TRANSLATED,  ABRIDGED, CONDENSED,
  EXPANDED,  COLLECTED,  COMPILED,  LINKED,  RECAST, TRANSFORMED OR ADAPTED
  WITHOUT THE PRIOR WRITTEN CONSENT OF NOVELL, INC. ANY USE OR EXPLOITATION
  OF THIS WORK WITHOUT AUTHORIZATION COULD SUBJECT THE PERPETRATOR TO
  CRIMINAL AND CIVIL LIABILITY.$

 ***************************************************************************/

import java.awt.*;
import java.awt.event.*;
import java.io.*;

import javax.naming.*;

import com.novell.service.file.nw.*;
import com.novell.service.file.nw.naming.*;

/**
 * This class implements a special handler for a FileDirContext object.
 */

public class FileSpecialHandler extends Frame
                                implements NSISpecialHandler,
                                WindowListener,
                                ActionListener
{
   static final int width = 500;
   static final int height = 400;
   static final int hexDisplayByteCount = 16;

   FileDirContext fileCtx;
   String fileName;

   Button hexButton = new Button ("Hex");
   Button textButton = new Button ("Text");
   MenuItem saveText = new MenuItem ("&Save");
   MenuItem closeWindow = new MenuItem ("Close");

   TextArea fileTextArea = null;

   /**
    * Must have a default constructor.
    */
   public FileSpecialHandler ()
   {
   } // FileSpecialHandler ();

   /**
    * Over-rides the NSISpecialHandler initialize method.
    *
    * @param name             (in) Relative name for the object.
    * @param obj              (in) The object to be handled.
    * @returns                True if initialization was successful.
    *                         False otherwise which indicates not to
    *                         call the handleObject method.
    */
   public boolean initialize (Name name, Object obj)
   {
      if (obj instanceof FileDirContext)
      {
         fileCtx = (FileDirContext) obj;
         fileName = name.get (name.size() - 1);

         setTitle (fileName);
      }
      else
      {
         new NSIMessageBox ("Error", "Object is not a file context.");

         return (false);
      }

      Menu m = new Menu ("&File");

      m.add (saveText);
      m.addSeparator ();
      m.add (closeWindow);

      MenuBar mb = new MenuBar ();

      mb.add (m);

      setMenuBar (mb);

      // Setup the Listeners
      addWindowListener (this);
      hexButton.addActionListener (this);
      textButton.addActionListener (this);
      saveText.addActionListener (this);
      closeWindow.addActionListener (this);

      return (true);
   }

   /**
    * Over-rides the NSISpecialHandler handleObject method.
    */
   public void handleObject ()
   {
      showHex ();    // default to showing hex for now
      show ();
   }

   /**
    * Displays the file in hex form.
    */
   void showHex ()
   {
      this.removeAll ();

      // Construct and add the name list display area
      Panel listPanel = new Panel ();
      List fileContentsList = new List ();

      fileContentsList.setMultipleMode (false);
      fileContentsList.setFont (new Font ("Courier", Font.PLAIN, 12));

      listPanel.setLayout (new BorderLayout ());
      listPanel.add ("Center", fileContentsList);

      Panel buttonPanel = new Panel ();
      buttonPanel.add (textButton);

      listPanel.add ("South", buttonPanel);
      add ("Center", listPanel);

      // Get rid of wasted space before the frame gets displayed
      pack ();

      this.setCursor (Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));    // We're probably going to be a while

      try
      {
         BufferedReader in = 
            new BufferedReader(
                  new InputStreamReader(new NFileInputStream(fileCtx)));

         char a[] = new char [hexDisplayByteCount];
         char disp[] = new char[12 + a.length * 3 + 1 + a.length];
         char hex[] = { '0','1','2','3','4','5','6','7',
                        '8','9','A','B','C','D','E','F' };
         int numBytesRead = 0;
         int totalBytes = 0;
         boolean finished = false;
         int maxTextWidth = 0, dispWidth;

         while (!finished)
         {
            int i = 0;

            for (int j = 8; j > 0; j--)
            {
               disp[i++] = hex[(totalBytes >>> (4*j)) & 0x0F];
            }
            disp[i++] = ':';
            disp[i++] = ' ';

            // Read in the next section and check for EOF
            numBytesRead = in.read (a, 0, a.length);
            if (numBytesRead < a.length)
               finished = true;     // EOF

            // fill in hex columns
            for (int j = 0; j < a.length; j++)
            {
               if (j < numBytesRead)
               {
                  disp[i++] = hex[(a[j] >>> 4) & 0x0F];
                  disp[i++] = hex[(a[j] >>> 0) & 0x0F];
               }
               else
               {
                  // spaces for bytes not read
                  disp[i++] = ' ';
                  disp[i++] = ' ';
               }

               if (j == (a.length / 2) - 1)
               {
                  disp[i++] = ' ';
                  disp[i++] = !finished ? '-' : ' ';
               }

               disp[i++] = ' ';
            }

            disp[i++] = ' ';

            // fill in printable characters, dots for unprintables
            for (int j = 0; j < a.length; j++)
            {
               if (j < numBytesRead)
               {
                  if (a[j] < (char) 32 || a[j] > (char) 127)
                     disp[i++] = '.';
                  else
                     disp[i++] = (char) a[j];
               }
               else
                  disp[i++] = ' ';
            }

            totalBytes += numBytesRead;
            fileContentsList.addItem (new String (disp));

            dispWidth = fileContentsList.getFontMetrics (
                  fileContentsList.getFont ()).
                  charsWidth (disp, 0, disp.length);

            if (maxTextWidth < dispWidth)
               maxTextWidth = dispWidth;
         }
         in.close ();

         setSize (maxTextWidth + 40, height);
      }
      catch (IOException e)
      {
         System.out.println ("Unable to process file");
         System.out.println ("Unexpected io exception " + e);
         e.printStackTrace ();
      }
      this.setCursor (Cursor.getDefaultCursor());    // Finally, restore the cursor
   } // showHex ()

   /**
    * Displays the file in text form.
    */
   void showText ()
   {
      this.removeAll ();

      fileTextArea = new TextArea ();
      fileTextArea.setEditable (true);

      Panel buttonPanel = new Panel ();
      buttonPanel.add (hexButton);

      setLayout (new BorderLayout ());
      add ("Center", fileTextArea);
      add ("South", buttonPanel);

      // Get rid of wasted space before the frame gets displayed
      pack ();

      this.setCursor (Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));    // We're probably going to be a while

      String newLine = System.getProperty ("line.separator");

      try
      {
         BufferedReader in = 
            new BufferedReader ( 
                  new InputStreamReader(new NFileInputStream(fileCtx)));

         boolean finished = false;
         String s = null;

         try
         {
            s = in.readLine ();
            while (s != null)
            {
               fileTextArea.append (s + newLine);
               s = in.readLine ();
            }
         }
         catch (EOFException e)
         {
            fileTextArea.append (newLine + newLine +
                  "<<< Interrupted >>>" + newLine);
         }
         in.close ();

         setSize (width, height);
         show ();
      }
      catch (IOException e)
      {
         System.out.println ("Unable to process file");
         System.out.println ("Unexpected io exception " + e);
         e.printStackTrace ();
      }
      this.setCursor (Cursor.getDefaultCursor());    // Finally, restore the cursor
   }

   /**
    * This is the action handler for the main window (frame).
    *
    * @param     evt             (in) The event
    */
   public void actionPerformed (
      ActionEvent evt)
   {
      Object target = evt.getSource();

      if (target == hexButton)
      {
         setVisible (false);
         showHex ();
         setVisible (true);
      }
      else if (target == textButton)
      {
         setVisible (false);
         showText ();
         setVisible (true);
      }
      else if (target == saveText)
      {
         try
         {
            this.setCursor (Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));

            DataOutputStream out = 
               new DataOutputStream(new NFileOutputStream(fileCtx));

            out.writeBytes (fileTextArea.getText ());
            out.close ();

            this.setCursor (Cursor.getDefaultCursor());
         }
         catch (IOException e)
         {
            System.out.println ("Unable to process file");
            System.out.println ("Unexpected io exception " + e);
            e.printStackTrace ();
         }
      }
      else if (target == closeWindow)
      {
         dispose ();
      }
   } // action ()

   /**
    * Implement WindowListener functions.  We really only want the
    * windowClosing one.
    */

   public void windowClosed (
      WindowEvent event)
   {
   }

   public void windowDeiconified (
      WindowEvent event)
   {
   }

   public void windowIconified (
      WindowEvent event)
   {
   }

   public void windowActivated (
      WindowEvent event)
   {
   }

   public void windowDeactivated (
      WindowEvent event)
   {
   }

   public void windowOpened (
      WindowEvent event)
   {
   }

   public void windowClosing (
      WindowEvent event)
   {
      dispose ();
   }

} // class FileSpecialHandler
