package nxm.ucl.lib;

import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.Frame;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.InputStream;
import java.net.Authenticator;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URL;

import javax.swing.JComboBox;
import javax.swing.JEditorPane;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;
import javax.swing.event.HyperlinkEvent;
import javax.swing.event.HyperlinkListener;
import javax.swing.text.html.HTMLDocument;
import javax.swing.text.html.HTMLFrameHyperlinkEvent;

/**
  Displays a web page.

  @author MSZ
  @author Jay Hallam
*/
@SuppressWarnings({"rawtypes","unchecked"}) // <-- NOTE: JDK7 has some unchecked & rawtypes Xlint warnings that CANNOT be fixed since we still support JDK6
public class HttpWin {
   private JEditorPane hpane;
   private String lastpage;
   private JFrame sframe = null;
   private static String user;
   private static String pass;
   private String failreason;
   private encoder encode;
   private GraphicsDevice[] screens;
   private int screencount=1;
   private int curdisplay=0;
   private GraphicsConfiguration[] gwins;
   private JComboBox displays = null;

   private HttpWinCallBackIF callBack = null;

   /** Interface for callbacks
    *  @author Jay Hallam
    *  @since NeXtMidas 2.3.0
    */
   public interface HttpWinCallBackIF {
     /** Callback method for when the Window is being closed */
     public void httpWinClosed();
   }

   /** Register a callback
       @param cb The callback
   */
   public void registerCallBack(HttpWinCallBackIF cb) { callBack = cb; }

   /** No argument constructor */
   public HttpWin() {
     encode = new encoder();

     screens = GraphicsEnvironment.getLocalGraphicsEnvironment().getScreenDevices();
     int slen = screens.length;
     GraphicsDevice gd;
     Rectangle virtualBounds = new Rectangle();
     screencount=0;
     gwins = new GraphicsConfiguration[slen];

     for (int i=0; i<slen; i++) {
       gd = screens[i];
       GraphicsConfiguration[] gc = gd.getConfigurations();
       int maxx=0,maxy=0;
       for (int j=0; j<gc.length; j++) {
          virtualBounds = gc[j].getBounds();
          if (virtualBounds.x != 0) maxx=virtualBounds.x;
          if (virtualBounds.y != 0) maxy=virtualBounds.y;
       }
       if (maxx== 0 && maxy==0) {
         gwins[screencount]=gc[0];
         screencount++;
       }
     }
     sframe = createFrame(0);
     sframe.addWindowListener(new WindowAdapter() {
       @Override
       public void windowClosing(WindowEvent e) {
         if (callBack != null) { callBack.httpWinClosed(); }
       }
     });
   }

  /** Dispose of the frame */
  public void destroyFrame() {
    if (sframe != null ) {
      sframe.dispose();
      sframe = null;
    }

  }

  /** Set the title
   *  @param title the title to be displayed in the frame's border
   */
  public void setTitle(String title) {
    sframe.setTitle(title);
  }

  /** Create the frame on the given display
   *  @param display the display
   *  @return the frame that was created
   */
  public JFrame createFrame(int display) {
     int gs = gwins.length;
     curdisplay=display;
     if (gs>0 && display<screencount && display<=gs) sframe=new JFrame("HTML",gwins[display]);
     sframe.toFront();
     hideOnClose();
     Container scontent = sframe.getContentPane();
     scontent.setLayout(new BorderLayout(0,1));
     hpane = new JEditorPane();
     hpane.setEditable(false);
     hpane.setContentType("text/html");
     Hyperactive ha = new Hyperactive();
     hpane.addHyperlinkListener(ha);

     Font boldf = new Font("Dialog", Font.BOLD, 12);
     JPanel buttonpanel = new JPanel();
     buttonpanel.setLayout(new FlowLayout());

     displays = new JComboBox();
     displays.setToolTipText("Set Display Window");
     displays.addItem("Display 0");
     for (int i=1; i<screencount; i++) {
       displays.addItem("Display "+i);
     }
     displays.setSelectedIndex(display);
     displays.addActionListener(new DisplayActionListener());

     buttonpanel.add(displays,BorderLayout.NORTH);
     scontent.add(buttonpanel,BorderLayout.NORTH);

     JScrollPane sscrollPane = new JScrollPane(hpane);
     scontent.add(sscrollPane,BorderLayout.CENTER);

     scontent.validate();
//   SWMouse aSWMouse = new SWMouse(); // commented out in 3.3.2 since variable is not used anywhere
     lastpage=null;

     sframe.setSize(600,800);
     return sframe;
  }

  /** Set the JFrame to hide on close */
  public void hideOnClose() {
     sframe.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
  }

  /** Set the JFrame to exit on close */
  public void exitOnClose() {
     sframe.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
  }

  /** Main driver method for running HttpWin as an instance
   *  @param args commandline arguments for the web page
   */
  public static void main(String args[]) {
    try {
      HttpWin h = new HttpWin();
      h.setPage(args[0]);
      h.show();
    } catch (Exception e) {
      e.printStackTrace();
    }
    return;
  }

  /** Set the user name and password
   *  @param puser user name
   *  @param ppass user password
   */
  public void setUserPass(String puser,String ppass) {
    user=puser;
    pass=ppass;
    Authenticator.setDefault(new MyAuthenticator(puser,ppass));
    return;
  }

  /** Hide the frame on the current display */
  public void show() { show(curdisplay); }

  /** Show the frame on the given display
   *  @param sdisplay the display */
  public void show(final int sdisplay) {
    try {
      SwingUtilities.invokeLater( new Runnable() {
        @Override
        public void run() {
          if (sframe==null) return;
          if (sframe.getState() == Frame.ICONIFIED) sframe.setState(Frame.NORMAL);
          if (sdisplay>=0 && sdisplay!=curdisplay) { sframe.dispose(); sframe=createFrame(sdisplay); }
          if (!sframe.isVisible()) sframe.setVisible(true);
        }
      });
    } catch (Exception e) {
      System.err.println("Error:"+e);
    }
  }

  /** Hide the frame */
  public void hide() {
    try {
      if (sframe!=null) sframe.setVisible(false);
    } catch (Exception e) {
      System.err.println("Error:"+e);
    }
  }

  /** Set the current text given the text and mime type
    @param httpdata The data String
    @param mimetype The Mime-Type
  */
  public void setText(String httpdata,String mimetype) {
    try {
      if (mimetype==null) { System.out.println("Null mimetype to setText"); return; }
      hpane.setContentType(mimetype);
      hpane.setText(httpdata);
    } catch (Exception e) {
      System.err.println("Error:"+e);
      e.printStackTrace();
      System.err.println("Page="+httpdata);
    }
  }

  /** Set the current page given the page and mime type
    @param httpurl The URL string
    @param mimetype The Mime-Type
  */
  public void setPage(String httpurl,String mimetype) {
    try {
      if (httpurl==null) { System.out.println("Null httpurl to setPage"); return; }
      if (mimetype==null) { System.out.println("Null mimetype to setPage"); return; }
      URL url = (new URI(httpurl)).toURL();
      HttpURLConnection hcon = (HttpURLConnection)url.openConnection();
      String encoding = encode.encodeBase64(user+":"+pass);
      hcon.setRequestProperty("Authorization", "Basic " + encoding);
      hcon.connect();
      int codeValue = hcon.getResponseCode();
      if (codeValue != HttpURLConnection.HTTP_ACCEPTED && codeValue != HttpURLConnection.HTTP_OK ) {
        System.err.println("Failed to get Page for User="+user+". Due to "+codeValue+" "+hcon.getResponseMessage()+" for "+httpurl);
        user=null;
        pass=null;
      } else {
        hpane.setContentType(mimetype);
        InputStream his = hcon.getInputStream();
        byte[] hisdata = new byte[his.available()+65536];
        int err = his.read(hisdata);
        String shttpdata = new String(hisdata,0,err);
        hpane.setText(shttpdata);
      }
    } catch (Exception e) {
      System.err.println("Error:"+e);
      e.printStackTrace();
      System.err.println("Page="+httpurl);
    }
  }

  /** Set the current page
    @param httpdata The page
  */
  public void setPage(String httpdata) {
    try {
      if (httpdata==null) { System.out.println("Null setPage"); return; }
      if (hpane.getPage()==null) lastpage=null;
      else lastpage=hpane.getPage().toString();
      hpane.setPage(httpdata);
    } catch (Exception e) {
      System.err.println("Error:"+e);
      e.printStackTrace();
      System.err.println("Page="+httpdata);
    }
    return;
  }

// INNER HELPER CLASSES
  class SWMouse extends MouseAdapter {
    public void mouseClicked(MouseEvent event) {
      Object object = event.getSource();
    }
  }

  class Hyperactive implements HyperlinkListener {

    public void hyperlinkUpdate(HyperlinkEvent e) {
      if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
         hpane = (JEditorPane) e.getSource();
         if (e instanceof HTMLFrameHyperlinkEvent) {
            HTMLFrameHyperlinkEvent evt = (HTMLFrameHyperlinkEvent)e;
            HTMLDocument doc = (HTMLDocument)hpane.getDocument();
            doc.processHTMLFrameHyperlinkEvent(evt);
         } else {
           try {
             if (e.getURL()==null) return;
             if (hpane.getPage()==null) lastpage=null;
             else lastpage=hpane.getPage().toString();
             hpane.setPage(e.getURL());
           } catch (Throwable t) {
             t.printStackTrace();
           }
         }
      }
    }
  }
  class DisplayActionListener implements ActionListener {
    public void actionPerformed(ActionEvent ae) {
      Object object = ae.getSource();
      if (object == displays) {
        String chosensetting = "";
        try {
          chosensetting = (String)displays.getSelectedItem();
          chosensetting=chosensetting.substring(8);
          int display=Integer.parseInt(chosensetting);
          URL tu = hpane.getPage();
          if (sframe!=null) sframe.dispose();
          sframe=createFrame(display);
          if (tu != null) hpane.setPage(tu.toString());
          show(display);
        } catch (Exception e) {
          System.err.println("Invalid Display:"+chosensetting);
        }
        return;
      }
    }
  }
}