
package menu;

import java.awt.*;
import java.awt.geom.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
import javax.swing.border.Border.*;
import util.*;
import DrawTop.*;
import shape.*;


public class DialogOfPageLayout extends JDialog{
    JScrollPane scrollPane=null;
    Dimension scrollPaneSize=null;
    PagePanel pagePanel=null;
    PageLayoutListener listener=null;
    
    public DialogOfPageLayout(){
        super(ObjectTable.getDrawMain(), "page layout");
        this.setName("DialogOfPageLayout");  
        this.listener=new PageLayoutListener(this);
        this.addComponentListener(this.listener);
        this.addWindowListener(this.listener);
        ObjectTable.getPageManager().saveCurrentPage();
    }
    
    public void showDialog(){
        MenuUtil menuUtil=ObjectTable.getMenuUtil();
        DialogOfPageLayout openedDialog
                =(DialogOfPageLayout)menuUtil.getMenuComponent("DialogOfPageLayout");
        if(openedDialog!=null) {
            JOptionPane.showMessageDialog(ObjectTable.getDrawMain(), 
                    "page layout dialog is already opened", "", JOptionPane.ERROR_MESSAGE);
            return;
        }
        Dimension frameSize=ObjectTable.getDrawMain().getSize();
        double width=frameSize.getWidth();
        double height=frameSize.getHeight();
        this.scrollPaneSize=new Dimension((int)(width-160), (int)(height/4));
        Component owner=this.getOwner();
        Point centerP=new Point(owner.getX()+60, owner.getY()+100);
        this.setLocation(centerP);
        
        Container contentPane=this.getContentPane();
        JPanel pagesPanel=createLayoutPanel();
        contentPane.add(pagesPanel, BorderLayout.NORTH);
        this.pack();
        this.setVisible(true);
        menuUtil.setMenuComponent(this);
    }
    
    protected void setScrollViewPosition(){
        this.scrollPane.getViewport().setViewPosition(new Point(0,0));
    }
    
    private JPanel createLayoutPanel(){
        JPanel basePanel=new JPanel();
        basePanel.setLayout(new FlowLayout(FlowLayout.LEADING, 0, 0));
//Information Panel   
        this.pagePanel=new PagePanel(this);
        this.scrollPane=new JScrollPane();
        this.scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
        this.scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
        JViewport viewport=new JViewport();
        this.scrollPane.setViewport(viewport);
        viewport.setView(this.pagePanel);
        viewport.setViewPosition(new Point(0,0));
        this.scrollPane.setPreferredSize(this.scrollPaneSize);
    // Layout BasePanel
        basePanel.add(this.scrollPane, BorderLayout.NORTH);
        return basePanel;
    }

    protected JScrollPane getScrollPane(){
        return this.scrollPane;
    }
    
    protected JPanel getPagePanel(){
        return this.pagePanel;
    }
}

class PagePanel extends JPanel implements MouseListener, MouseMotionListener{
    DialogOfPageLayout dialog=null;
    //Dimension pageFrameSize=null;
    int xNumOfFrames=0, yNumOfFrames=0;
    //Dimension pagePanelSize=null;
    private Rectangle2D[] pageFrameRects=null;
    int selectedPage=0;
    int gapIndex=-1;
    //final Insets scrollInsets=new Insets(30,4,4,4);
    final Dimension gap=new Dimension(20, 20);
    public static int debug=0;
    
    public PagePanel(DialogOfPageLayout dialog){
        this.dialog=dialog;
        this.addMouseListener(this);
        this.addMouseMotionListener(this);
    }
    
    public void paint(Graphics g) {
      //---------------------//  
        this.setPageLayout();
      //---------------------//    
        Graphics2D g2=(Graphics2D)g;
        Rectangle viewRect=this.dialog.getScrollPane().getViewport().getViewRect();
        Shape currentClip=g2.getClip();
        Stroke currentStroke=g2.getStroke();
        g2.setClip(viewRect);
        Dimension pagePanelSize=this.getPagePanelSize();
        Rectangle2D panelRect=new Rectangle2D.Double( 0, 0, pagePanelSize.getWidth(), 
                pagePanelSize.getHeight());
        g2.setColor(Color.WHITE);
        g2.fill(panelRect);
      //Draw bold frame around the selected page 
        g2.setColor(Color.BLACK);
        if(this.selectedPage>=1){
            Rectangle2D enlargedRect
                =ShapeElementUtil.getEnlargedRectangle(this.pageFrameRects[this.selectedPage-1], 3d, 3d);
            BasicStroke defaultStroke=new BasicStroke(2f, BasicStroke.CAP_SQUARE, 
                BasicStroke.JOIN_MITER, 10.0f);
            g2.setStroke(defaultStroke);
            g2.draw(enlargedRect);
        }
      //Draw bold vertical line at the destination
        double delta=0.5d*this.gap.getWidth();
        double lineW=this.getPageFrameSize().getWidth();
        double lineH=this.getPageFrameSize().getHeight();
        BasicStroke  defaultStroke=new BasicStroke(2f, BasicStroke.CAP_SQUARE, 
                    BasicStroke.JOIN_MITER, 10.0f);
        g2.setStroke(defaultStroke);
        if(this.gapIndex>=1&&this.gapIndex<=this.pageFrameRects.length){
            double lineX=this.pageFrameRects[this.gapIndex-1].getX();
            double lineY=this.pageFrameRects[this.gapIndex-1].getY();
            Line2D line=new Line2D.Double(lineX+lineW+delta, lineY, 
                    lineX+lineW+delta, lineY+lineH);
            g2.draw(line);
        }
        if(this.gapIndex>=0&&this.gapIndex<this.pageFrameRects.length){
            double lineX=this.pageFrameRects[this.gapIndex].getX();
            double lineY=this.pageFrameRects[this.gapIndex].getY();
            Line2D line=new Line2D.Double(lineX-delta, lineY, lineX-delta, lineY+lineH);
            g2.draw(line);
        }   
      // Draws the contents of each page
        double X=this.gap.getWidth();
        double Y=this.gap.getHeight();
        for(int pageIndex=1;pageIndex<=pageFrameRects.length;pageIndex++){
            g2.setStroke(currentStroke);
            g2.draw(this.pageFrameRects[pageIndex-1]);
            this.drawPage(g, pageIndex, this.pageFrameRects[pageIndex-1]);
            X+=this.pageFrameRects[pageIndex-1].getWidth()+this.gap.getWidth();
            if(pageIndex-(int)(pageIndex/this.xNumOfFrames)*this.xNumOfFrames==0){
                Y+=this.pageFrameRects[pageIndex-1].getHeight()+this.gap.getHeight();
                X=this.gap.getWidth();
            }
        }
        g2.setClip(currentClip);
    }

    public void setPageLayout(){
        Dimension pageFrameSize=this.getPageFrameSize();
        
        JScrollPane scrollpane=this.dialog.getScrollPane();
        Dimension scrollSize=scrollpane.getPreferredSize();
        Insets scrollInsets=new Insets(30,4,4,4);
        Dimension displayAreaSize
                =new Dimension((int)(scrollSize.getWidth()-scrollInsets.left-scrollInsets.right),
                (int)(scrollSize.getWidth()-scrollInsets.top-scrollInsets.bottom));
        this.xNumOfFrames=(int)(displayAreaSize.getWidth()/(pageFrameSize.getWidth()+this.gap.getWidth()));
        PageManager pageManager=ObjectTable.getPageManager("PagePanel");
        int numOfPages=pageManager.getPageList().size();
        this.yNumOfFrames=(int)(numOfPages/this.xNumOfFrames);
        if(numOfPages-this.yNumOfFrames*this.xNumOfFrames>0) this.yNumOfFrames++;

        //this.pagePanelSize=this.getPagePanelSize();
        if(debug>0) System.out.println(" -- PagePanel displayAreaSize="+displayAreaSize+
                ", num of pages="+numOfPages+", pageFrameSize="+pageFrameSize+
                ", x-num of sheets="+this.xNumOfFrames+", y-num of sheets="+this.yNumOfFrames);
        this.setPreferredSize(this.getPagePanelSize());
        this.setBackground(Color.WHITE);
        this.pageFrameRects=this.getPageFrameRects();
        this.revalidate();
    }
    
    private Dimension getPageFrameSize(){
        Dimension A4SizeMM=DrawParameters.A4_Size;
        double PixelPerMM=DrawParameters.InchToPixels/DrawParameters.InchToMM;
        double Ratio=0.125d;
        Dimension pageSize=new Dimension((int)(A4SizeMM.getHeight()*PixelPerMM*Ratio), 
                (int)(A4SizeMM.getWidth()*PixelPerMM*Ratio));
        return pageSize;
    }
    
    private Dimension getPagePanelSize(){
        Dimension scrollPaneSize=this.dialog.getScrollPane().getPreferredSize();
        Dimension scrollBarSize=this.dialog.getScrollPane().getVerticalScrollBar().getSize();
        Insets insets=this.dialog.getScrollPane().getInsets();
        int width=(int)(scrollPaneSize.getWidth()-insets.left-insets.right
                -scrollBarSize.getWidth());
        //int width=(int)(xNumOfFrames*(pageFrameSize.getWidth()+this.gap.getWidth())+
        //this.gap.getWidth());
        Dimension pageFrameSize=this.getPageFrameSize();
        int height=(int)(yNumOfFrames*(pageFrameSize.getHeight()+
                this.gap.getHeight())+this.gap.getHeight());
        Dimension size=new Dimension(width,height);
        return size;
    }
    
    private Rectangle2D[] getPageFrameRects(){
        PageManager pageManager=ObjectTable.getPageManager("PagePanel");
        int numOfPages=pageManager.getPageList().size();
        Rectangle2D[] rectangles=new Rectangle2D[numOfPages];
        double X=this.gap.getWidth();
        double Y=this.gap.getHeight();
        Dimension pageFrameSize=this.getPageFrameSize();
        double width=pageFrameSize.getWidth();
        double height=pageFrameSize.getHeight();
        for(int pageIndex=1;pageIndex<=numOfPages;pageIndex++){
            rectangles[pageIndex-1]=new Rectangle2D.Double( X, Y, width, height);
            X+=width+this.gap.getWidth();
            if(pageIndex-(int)(pageIndex/this.xNumOfFrames)*this.xNumOfFrames==0){
                Y+=height+this.gap.getHeight();
                X=this.gap.getWidth();
            }
        }
        return rectangles;
    }

    private void drawPage(Graphics g, int pageIndex, Rectangle2D rect){
        PageManager pageManager=ObjectTable.getPageManager();
        ArrayList list=pageManager.getContainerList(pageIndex);
        ViewData viewData=pageManager.getViewData(pageIndex);
        ContainerManager containerManager=new ContainerManager();
        containerManager.setContainerList(list);
      //------------------------------------------// 
        double scale=this.getScale(viewData, rect);
        if(debug>0) System.out.println(" -- DrawPage pageIndex="+pageIndex+", rect="+rect+
                ", scale="+scale);
      //------------------------------------------// 
        Graphics2D g2=(Graphics2D)g;
        Color currentColor=g2.getColor();
        AffineTransform  currentTransform=g2.getTransform();
        g2.translate(rect.getX(), rect.getY());
        int orientation=viewData.getSheetOrientation();
        if(orientation==DrawParameters.Portrait) {
            g2.translate(0, rect.getHeight());
            g2.rotate(-0.5*Math.PI);    
        }
        g2.scale(scale, scale);
        ShapeContainer[] containers=containerManager.getContainers();
        for(int i=0;i<containers.length;i++){
            containers[i].drawShape(g);
        }
        g2.setTransform(currentTransform);
        String pageStr="page-"+pageIndex;
        g2.drawString(pageStr, (int)(rect.getX()+rect.getWidth()/2-15), 
                (int)(rect.getY()+rect.getHeight()+12));
        g2.setColor(currentColor);
    }
    
    private double getScale(ViewData viewData, Rectangle2D rect){
        int sheetSize=viewData.getSheetSize();
        Dimension sheetDimensionMM=DrawParameters.SheetSizesMM[sheetSize];
        sheetDimensionMM=new Dimension((int)sheetDimensionMM.getHeight(), 
                (int)sheetDimensionMM.getWidth());
        double pixelPerMM=DrawParameters.InchToPixels/DrawParameters.InchToMM;
        Dimension sheetDimension=new Dimension((int)(sheetDimensionMM.getWidth()*pixelPerMM), 
                (int)(sheetDimensionMM.getHeight()*pixelPerMM));
        double width=rect.getWidth();
        double scale=width/sheetDimension.getWidth();
        return scale;
    }
    
    public void mousePressed(MouseEvent e){
        double X = e.getX();
        double Y = e.getY();
        Point2D startPoint=new Point2D.Double(X,Y);
        int index=this.ptInside(startPoint);
        if(index>0) this.selectedPage=index;
        else this.selectedPage=0;
        this.repaint();
    }
    
    public void mouseDragged(MouseEvent e) {
        double X = e.getX();
        double Y = e.getY();
        Point2D currentPoint=new Point2D.Double(X,Y);
        this.gapIndex=this.getNearestGap(currentPoint);
        this.repaint();
    } //mouseDragged
    
    public void mouseReleased(MouseEvent e) {
        double X = e.getX();
        double Y = e.getY();
        Point2D endPoint=new Point2D.Double(X,Y);
        this.gapIndex=this.getNearestGap(endPoint);
        PageManager pageManager=ObjectTable.getPageManager("PagePanel");
        if(debug>0) System.out.println("mouseReleased  this.selectedPage="+this.selectedPage+
                ", this.gapIndex="+this.gapIndex);
        if(this.selectedPage>=1&&this.gapIndex>=0)
            pageManager.movePage(this.selectedPage, this.gapIndex+1);
        this.selectedPage=0;
        this.gapIndex=-1;
        this.repaint();
    } //mouseReleased
    
    public void mouseClicked(MouseEvent e) {}

    public void mouseEntered(MouseEvent e) {}
    
    public void mouseExited(MouseEvent e) {}

    public void mouseMoved(MouseEvent e) {}
    
    public int ptInside(Point2D point){
        int index=-1;
        for(int i=1;i<=this.pageFrameRects.length;i++){
            if(this.pageFrameRects[i-1].contains(point)) index=i;
        }
        return index;
    }

    public int getNearestGap(Point2D point){
        int page=-1;
        int side=-1;
        double distMin=Math.pow(10, 4);
        double X=point.getX();
        double Y=point.getY();
        for(int i=1;i<=this.pageFrameRects.length;i++){
            double x=this.pageFrameRects[i-1].getX();
            double y=this.pageFrameRects[i-1].getY();
            double w=this.pageFrameRects[i-1].getWidth();
            double h=this.pageFrameRects[i-1].getHeight();
            if(Y<y||Y>y+h) continue;
            double dist1=Math.abs(X-x);
            double dist2=Math.abs(X-x-w);
            if(dist1<distMin){
                distMin=dist1;
                page=i;
                side=0;
            }
            if(dist2<distMin){
                distMin=dist2;
                page=i;
                side=1;
            }
        }
        int gapIndex=-1;
        if(page>0&&side==0)  gapIndex=page-1;
        if(page>0&&side==1)  gapIndex=page;
        //if(page>0&&side==0)  gapIndex=page;
        //if(page>0&&side==1)  gapIndex=page+1;
        if(debug>0) System.out.println("  -- nearest gap="+gapIndex+", X,Y="+X+","+Y);
        return gapIndex;
    }

} //End of PagePanel class

class PageLayoutListener extends WindowAdapter implements ComponentListener{
    DialogOfPageLayout dialog=null;
    int debug=0;
    
    PageLayoutListener(DialogOfPageLayout dialog){
        this.dialog=dialog;
    }
    
    public void componentResized(ComponentEvent e){
        if(debug>0) System.out.println("** Dialog componentResized called");
        String componentName=e.getComponent().getClass().getSimpleName();
        if(debug>1) System.out.println(" - componentResized  componentName="+componentName);
        Dimension sizeOfDialog=this.dialog.getSize();
        Insets dialogInsets=this.dialog.getInsets();
        if(sizeOfDialog==null) return;
        JScrollPane scrollPane=this.dialog.getScrollPane();
        Dimension newSize=new Dimension((int)(sizeOfDialog.getWidth()-dialogInsets.left-dialogInsets.right), 
                (int)(sizeOfDialog.getHeight()-dialogInsets.top-dialogInsets.bottom));
        scrollPane.setPreferredSize(newSize);
        if(debug>1) System.out.println(" - componentResized  sizeOfDialog="+Util.Dim(sizeOfDialog)+
                ", newSize="+Util.Dim(newSize));
        this.dialog.getScrollPane().revalidate();
        this.dialog.validate();
        this.repaint();
    }
    
    public void componentHidden(ComponentEvent e){}
    public void componentMoved(ComponentEvent e) {
        //if(debug>0) System.out.println("** Dialog componentMoved called");
        //this.repaint();
    }
    public void componentShown(ComponentEvent e){
        if(debug>0) System.out.println("** Dialog componentShown called");
      //------------------------------------//  
        this.dialog.setScrollViewPosition();
      //------------------------------------// 
        this.repaint();
    } 

    //public void windowClosed(WindowEvent e) {}
    public void windowClosing(WindowEvent e) {
        this.closeDialog();
    }

    private void closeDialog(){
        this.dialog.setVisible(false);
        MenuUtil menuUtil=ObjectTable.getMenuUtil();
        Component component=menuUtil.getMenuComponent(this.dialog.getName());
        if(component!=null) menuUtil.removeMenuComponent(component);
    }
    
    private void repaint(){
        this.dialog.getScrollPane().repaint();
        this.dialog.repaint();
    }
}
