import java.awt.*;
import java.awt.event.*;
import java.io.*;
import javax.swing.*;
import javax.swing.border.*;
import javax.swing.filechooser.FileNameExtensionFilter;

/**
 * IgoDialog クラスは、囲碁Viewerのメインダイアログを提供します。
 */
public class IgoDialog implements IgoView,ActionListener{

    private static final int FONTSIZE=29;
    private static final int MENUBARFONTSIZE=16;
    private static final int BUTTONFONTSIZE=15;
    private static final int AUTOPLAYFONTSIZE=12;
    private static final int MODEFONTSIZE=15;
    private static final int PLAYERFONTSIZE=17;
    private static final int PLAYERLABELFONTSIZE=20;
    private static final int FRAMESIZEX=840;
    private static final int FRAMESIZEY=700;
    private static final int BOARDSIZEX=630;
    private static final int BOARDSIZEY=630;
    private static final int BUTTONPANELSIZEX=200;
    private static final int BUTTONPANELSIZEY=144;
    private static final int PLAYERPANELSIZEX=200;
    private static final int PLAYERPANELSIZEY=60;
    private static final Font FONT_MENUBAR=new Font("MS UI Gothic",Font.PLAIN,MENUBARFONTSIZE);
    private static final Font FONT_MODE=new Font("MS UI Gothic",Font.BOLD,MODEFONTSIZE);
    private static final Font FONT_BUTTON=new Font("MS UI Gothic",Font.PLAIN,BUTTONFONTSIZE);
    private static final Font FONT_EXAM=new Font("MS UI Gothic",Font.BOLD,BUTTONFONTSIZE);
    private static final Font FONT_AUTOPLAY=new Font("MS UI Gothic",Font.BOLD,AUTOPLAYFONTSIZE);
    private static final Font FONT_PLAYER=new Font("MS UI Gothic",Font.BOLD,PLAYERFONTSIZE);
    private static final Font FONT_PLAYERLABEL=new Font("MS UI Gothic",Font.PLAIN,PLAYERLABELFONTSIZE);
    private static final Color color1=new Color(230,230,250);

    private IgoModel controller;
    private JFrame jf;
    private JMenuBar jmb;
    private JMenu jmFile;
    private JMenuItem jmiOpen;
    private JMenuItem jmiRecord;
    private JMenuItem jmiSave;
    private JMenuItem jmiSaveAs;
    private JMenuItem jmiClose;
    private JMenuItem jmiExit;
    private JMenu jmEdit;
    private JMenuItem jmiStartEdit;
    private JMenuItem jmiOpenGameInfo;
    private JMenu jmView;
    private JMenu jmBoard;
    private JMenuItem jmiGraphicBoard;
    private JMenuItem jmiImageBoard;
    private JMenuItem jmiTextBoard;
    private IgoPanel jp_ban;
    private IgoGraphicPanel graphicBan;
    private IgoImagePanel imageBan;
    private IgoTextPanel textBan;
    private JPanel jp_button;

    private JPanel jpBlack;
    private JLabel jlBlackPlayer;
    private JLabel jlBlackAgehama;

    private JPanel jpWhite;
    private JLabel jlWhitePlayer;
    private JLabel jlWhiteAgehama;

    private JButton bt_back;
    private JButton bt_forward;
    private JButton bt_back5;
    private JButton bt_forward5;
    private JButton bt_backAll;
    private JButton bt_forwardAll;
    private JButton bt_pass;
    private JButton bt_examine;
    private JLabel jlMode;
    private JLabel jlNumOfMoves;
    private JLabel jlDummy;
    private JButton bt_autoPlay;
    private JButton bt_playSpeed;

    private File lastDir;
    private String eventName,gameName;

    /** 
      * 空のインスタンスを生成します。
      */
    IgoDialog(){
        eventName=new String("");
        gameName=new String("");
    }

    /** 
     * インスタンスを初期化します。
     * @param controller コントローラー
     */
    public void initialize(IgoModel controller){

        try {

            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());

        }catch(Exception exc){

            System.err.println("Error loading L&F: " + exc);
        }



        this.controller=controller;
        lastDir=new File(".");
        initializePanel();
        initializeContentPane();
    }

    private void initializePanel(){
        bt_back=new JButton("<");
        bt_forward=new JButton(">");
        bt_back5=new JButton("<<");
        bt_forward5=new JButton(">>");
        bt_backAll=new JButton("|<");
        bt_forwardAll=new JButton(">|");
        bt_pass=new JButton("パス");
        bt_examine=new JButton("検討");
        bt_autoPlay=new JButton("自動再生");
        bt_playSpeed=new JButton("再生速度");
        jlMode=new JLabel("",JLabel.CENTER);
        jlMode.setOpaque(true);
        jlMode.setBackground(new Color(238,238,238));
        jlMode.setBorder(new EtchedBorder(EtchedBorder.LOWERED));
        jlNumOfMoves=new JLabel("",JLabel.CENTER);
        jlNumOfMoves.setOpaque(true);
        jlNumOfMoves.setBackground(new Color(238,238,238));
        jlNumOfMoves.setBorder(new EtchedBorder(EtchedBorder.LOWERED));
        jlDummy=new JLabel("",JLabel.CENTER);
        jlDummy.setOpaque(false);

        bt_back.addActionListener(this);
        bt_forward.addActionListener(this);
        bt_back5.addActionListener(this);
        bt_forward5.addActionListener(this);
        bt_backAll.addActionListener(this);
        bt_forwardAll.addActionListener(this);
        bt_pass.addActionListener(this);
        bt_examine.addActionListener(this);
        bt_autoPlay.addActionListener(this);
        bt_playSpeed.addActionListener(this);

        bt_back.setFont(FONT_BUTTON);
        bt_forward.setFont(FONT_BUTTON);
        bt_back5.setFont(FONT_BUTTON);
        bt_forward5.setFont(FONT_BUTTON);
        bt_backAll.setFont(FONT_BUTTON);
        bt_forwardAll.setFont(FONT_BUTTON);
        bt_pass.setFont(FONT_BUTTON);
        bt_examine.setFont(FONT_BUTTON);
        jlMode.setFont(FONT_MODE);
        jlNumOfMoves.setFont(FONT_MODE);
        bt_autoPlay.setFont(FONT_BUTTON);
        bt_playSpeed.setFont(FONT_BUTTON);

        jp_button=new JPanel();
        jp_button.setOpaque(false);
        jp_button.setSize(BUTTONPANELSIZEX,BUTTONPANELSIZEY);
        jp_button.setLocation(BOARDSIZEX,PLAYERPANELSIZEY*2);
        jp_button.setLayout(new GridLayout(6,2));
        jp_button.add(bt_back);
        jp_button.add(bt_forward);
        jp_button.add(bt_back5);
        jp_button.add(bt_forward5);
        jp_button.add(bt_backAll);
        jp_button.add(bt_forwardAll);
        jp_button.add(bt_pass);
        jp_button.add(bt_autoPlay);
        jp_button.add(bt_examine);
        jp_button.add(bt_playSpeed);
        jp_button.add(jlMode);
        jp_button.add(jlNumOfMoves);
//    jp_button.add(jlDummy);

        graphicBan=new IgoGraphicPanel();
        imageBan=new IgoImagePanel();
        textBan=new IgoTextPanel();
        jp_ban=graphicBan;
        setupBoard();

        jlBlackPlayer=new JLabel("●");
        jlBlackPlayer.setFont(FONT_PLAYER);
        jlBlackPlayer.setOpaque(false);
        jlBlackAgehama=new JLabel("");
        jlBlackAgehama.setFont(FONT_PLAYER);
        jlBlackAgehama.setOpaque(true);
        jlBlackAgehama.setBackground(Color.white);
        jlBlackAgehama.setHorizontalAlignment(JLabel.CENTER);

        jpBlack=new JPanel();
        jpBlack.setOpaque(false);
        jpBlack.setLayout(new GridLayout(2,1));
        jpBlack.setSize(PLAYERPANELSIZEX,PLAYERPANELSIZEY);
        jpBlack.setLocation(BOARDSIZEX,0);
        jpBlack.add(jlBlackPlayer);
        jpBlack.add(jlBlackAgehama);

        jlWhitePlayer=new JLabel("○");
        jlWhitePlayer.setFont(FONT_PLAYER);
        jlWhitePlayer.setOpaque(false);
        jlWhiteAgehama=new JLabel("");
        jlWhiteAgehama.setFont(FONT_PLAYER);
        jlWhiteAgehama.setOpaque(true);
        jlWhiteAgehama.setBackground(Color.white);
        jlWhiteAgehama.setHorizontalAlignment(JLabel.CENTER);

        jpWhite=new JPanel();
        jpWhite.setOpaque(false);
        jpWhite.setLayout(new GridLayout(2,1));
        jpWhite.setSize(PLAYERPANELSIZEX,PLAYERPANELSIZEY);
        jpWhite.setLocation(BOARDSIZEX,PLAYERPANELSIZEY);
        jpWhite.add(jlWhitePlayer);
        jpWhite.add(jlWhiteAgehama);
    }

    private void setupBoard(){
        jp_ban.setSize(BOARDSIZEX,BOARDSIZEY);
        jp_ban.setLocation(0,0);
        jp_ban.setIgoGameController(controller);
    }

    private void initializeContentPane(){
        jf=new JFrame("囲碁Viewer");
        jf.setSize(FRAMESIZEX,FRAMESIZEY);
        jf.setLocation(200,30);
        jf.getContentPane().setBackground(new Color(230,230,250));
        jmb=new JMenuBar();
        jmFile=new JMenu("ファイル");
        jmFile.setFont(FONT_MENUBAR);
        jmiOpen=new JMenuItem("開く");
        jmiOpen.setFont(FONT_MENUBAR);
        jmiRecord=new JMenuItem("新規作成");
        jmiRecord.setFont(FONT_MENUBAR);
        jmiSave=new JMenuItem("上書き保存");
        jmiSave.setFont(FONT_MENUBAR);
        jmiSave.setEnabled(false);
        jmiSaveAs=new JMenuItem("名前を付けて保存");
        jmiSaveAs.setFont(FONT_MENUBAR);
        jmiSaveAs.setEnabled(false);
        jmiClose=new JMenuItem("閉じる");
        jmiClose.setFont(FONT_MENUBAR);
        jmiClose.setEnabled(false);
        jmiExit=new JMenuItem("終了");
        jmiExit.setFont(FONT_MENUBAR);
        jmb.add(jmFile);
        jmFile.add(jmiOpen);
        jmFile.add(jmiRecord);
        jmFile.addSeparator();
        jmFile.add(jmiSave);
        jmFile.add(jmiSaveAs);
        jmFile.addSeparator();
        jmFile.add(jmiClose);
        jmFile.add(jmiExit);
        jmEdit=new JMenu("編集");
        jmEdit.setFont(FONT_MENUBAR);
        jmiStartEdit=new JMenuItem("現在の棋譜を編集");
        jmiStartEdit.setFont(FONT_MENUBAR);
        jmiStartEdit.setEnabled(false);
        jmiOpenGameInfo=new JMenuItem("対局情報");
        jmiOpenGameInfo.setFont(FONT_MENUBAR);
        jmiOpenGameInfo.setEnabled(false);
        jmb.add(jmEdit);
        jmEdit.add(jmiOpenGameInfo);
        jmEdit.add(jmiStartEdit);
        jmView=new JMenu("表示");
        jmView.setFont(FONT_MENUBAR);
        jmBoard=new JMenu("碁盤    ");
        jmBoard.setFont(FONT_MENUBAR);
        jmiGraphicBoard=new JMenuItem("グラフィック碁盤");
        jmiGraphicBoard.setFont(FONT_MENUBAR);
        jmiGraphicBoard.setEnabled(true);
        jmiImageBoard=new JMenuItem("イメージ碁盤");
        jmiImageBoard.setFont(FONT_MENUBAR);
        jmiImageBoard.setEnabled(true);
        jmiTextBoard=new JMenuItem("テキスト碁盤");
        jmiTextBoard.setFont(FONT_MENUBAR);
        jmiTextBoard.setEnabled(true);
        jmb.add(jmView);
        jmView.add(jmBoard);
        jmBoard.add(jmiGraphicBoard);
        jmBoard.add(jmiImageBoard);
        jmBoard.add(jmiTextBoard);

        jmiOpen.addActionListener(new ActionListener(){
            public void actionPerformed(ActionEvent e){
                controller.open();
            } 
        });
        jmiRecord.addActionListener(new ActionListener(){
            public void actionPerformed(ActionEvent e){
                controller.record();
            } 
        });
        jmiSave.addActionListener(new ActionListener(){
            public void actionPerformed(ActionEvent e){
                controller.save();
            } 
        });
        jmiSaveAs.addActionListener(new ActionListener(){
            public void actionPerformed(ActionEvent e){
                controller.saveAs();
            } 
        });
        jmiExit.addActionListener(new ActionListener(){ 
            public void actionPerformed(ActionEvent e){
                controller.exit();
            } 
        });
        jmiStartEdit.addActionListener(new ActionListener(){
            public void actionPerformed(ActionEvent e){
                controller.startEdit();
            } 
        });
        jmiClose.addActionListener(new ActionListener(){
            public void actionPerformed(ActionEvent e){
                controller.close();
            } 
        });
        jmiOpenGameInfo.addActionListener(new ActionListener(){
            public void actionPerformed(ActionEvent e){
                controller.displayGameInfo();
            } 
        });
        jmiGraphicBoard.addActionListener(new ActionListener(){
            public void actionPerformed(ActionEvent e){
                changeBoard(IgoPanel.GRAPHIC_BOARD);
            } 
        });
        jmiImageBoard.addActionListener(new ActionListener(){
            public void actionPerformed(ActionEvent e){
                changeBoard(IgoPanel.IMAGE_BOARD);
            } 
        });
        jmiTextBoard.addActionListener(new ActionListener(){
            public void actionPerformed(ActionEvent e){
                changeBoard(IgoPanel.TEXT_BOARD);
            } 
        });

        jf.getRootPane().setJMenuBar(jmb);
        jf.getContentPane().setLayout(null);
        jf.getContentPane().add(jp_ban);
        jf.getContentPane().add(jp_button);
        jf.getContentPane().add(jpBlack);
        jf.getContentPane().add(jpWhite);
        jf.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
        jf.addWindowListener(new WindowAdapter(){
            public void windowClosing(WindowEvent e){
                controller.exit();
            }
        });
    }

    /** 
     *  フレームを取得します。
     *  @return  フレーム
     */
    public JFrame getFrame(){
        return jf;
    }

    /** 
     *  指定されたコントローラーに変更します。
     *  @param controller コントローラー
     */
    public void changeController(IgoModel controller){
        this.controller=controller;
        jp_ban.setIgoGameController(controller);
    }

    /** 
     *  指定されたパラメータに従って、ビューを更新します。
     *  @param mode 動作モード
     *  @param isExam 検討モードかどうかの真偽値
     *  @param intBoard 盤面データ
     *  @param gameInfo 対局情報
     *  @param numOfMoves 手数
     */
    public void updateView(Mode mode,boolean isExam,IntBoard intBoard,
                           GameInformation gameInfo,int numOfMoves){
        jlBlackPlayer.setText("● " + gameInfo.getBlackPlayer() + " " + gameInfo.getBlackRank());
        jlBlackAgehama.setText(Integer.toString(intBoard.getBhama()));
        jlWhitePlayer.setText("○ " + gameInfo.getWhitePlayer() + " " + gameInfo.getWhiteRank());
        jlWhiteAgehama.setText(Integer.toString(intBoard.getWhama()));
        jf.setTitle("囲碁Viewer " + gameInfo.getEvent() + gameInfo.getGameName());
        jp_ban.update(intBoard);

        eventName=gameInfo.getEvent();
        gameName=gameInfo.getGameName();

        if(isExam){
            bt_examine.setForeground(Color.red);
            bt_examine.setFont(FONT_EXAM);
            bt_examine.setText("検討中");
        }else{
            bt_examine.setForeground(Color.black);
            bt_examine.setFont(FONT_BUTTON);
            bt_examine.setText("検討");
        }
        int dispNum=numOfMoves + 1;
        switch(mode.getMode()){
            case Mode.MODE_INIT:
                jlMode.setText("");
                jlMode.setBackground(new Color(238,238,238));
                jmiOpen.setEnabled(true);
                jmiRecord.setEnabled(true);
                jmiStartEdit.setEnabled(false);
                jmiSave.setEnabled(false);
                jmiSaveAs.setEnabled(false);
                jmiClose.setEnabled(false);
                jmiOpenGameInfo.setEnabled(false);
                jlNumOfMoves.setText("");
                break;
            case Mode.MODE_PLAY:
                jlMode.setText("再生中");
                jlMode.setBackground(new Color(153,255,255));
                jmiOpen.setEnabled(true);
                jmiRecord.setEnabled(true);
                jmiSave.setEnabled(false);
                jmiSaveAs.setEnabled(false);
                jmiClose.setEnabled(true);
                jmiOpenGameInfo.setEnabled(true);
                jlNumOfMoves.setText(dispNum + "手");
                if(isExam){
                    jmiStartEdit.setEnabled(false);
                }else{
                    jmiStartEdit.setEnabled(true);
                }
                break;
            case Mode.MODE_NEW:
                jlMode.setText("新規作成中");
                jlMode.setBackground(new Color(255,182,193));
                jmiOpen.setEnabled(false);
                jmiRecord.setEnabled(false);
                jmiSave.setEnabled(false);
                jmiSaveAs.setEnabled(true);
                jmiStartEdit.setEnabled(false);
                jmiClose.setEnabled(true);
                jmiOpenGameInfo.setEnabled(true);
                jlNumOfMoves.setText(dispNum + "手");
                break;
            case Mode.MODE_MOD:
                jlMode.setText("更新中");
                jlMode.setBackground(new Color(255,182,193));
                jmiOpen.setEnabled(false);
                jmiRecord.setEnabled(false);
                jmiSave.setEnabled(true);
                jmiSaveAs.setEnabled(true);
                jmiStartEdit.setEnabled(false);
                jmiClose.setEnabled(true);
                jmiOpenGameInfo.setEnabled(true);
                jlNumOfMoves.setText(dispNum + "手");
                break;
        }
    }

    /** 
     *  指定されたパラメータに従って、ビューを更新します。
     *  @param mode 動作モード
     *  @param isExam 検討モードかどうかの真偽値
     *  @param intBoard 盤面データ
     *  @param gameInfo 対局情報
     *  @param numOfMoves 手数
     *  @param isAutoPlay 自動再生かどうかの真偽値
     */
    public void updateView(Mode mode,boolean isExam,IntBoard intBoard,
                           GameInformation gameInfo,int numOfMoves,boolean isAutoPlay){

        updateView(mode,isExam,intBoard,gameInfo,numOfMoves);
        if(isAutoPlay){
            bt_autoPlay.setForeground(Color.red);
            bt_autoPlay.setFont(FONT_AUTOPLAY);
            bt_autoPlay.setText("自動再生中");
        }else{
            bt_autoPlay.setForeground(Color.black);
            bt_autoPlay.setFont(FONT_BUTTON);
            bt_autoPlay.setText("自動再生");
        }

    }

    /** 
     *  アクションが発生すると呼び出されます。
     *  選択されたアクション(ボタン操作等)に従った処理を行います。 
     */
    public void actionPerformed(ActionEvent ae){
        if ((JButton)ae.getSource()==bt_back)
        { controller.back();}
        if ((JButton)ae.getSource()==bt_forward)
        { controller.forward();}
        if ((JButton)ae.getSource()==bt_back5)
        { controller.back5();}
        if ((JButton)ae.getSource()==bt_forward5)
        { controller.forward5();}
        if ((JButton)ae.getSource()==bt_backAll)
        { controller.backAll();}
        if ((JButton)ae.getSource()==bt_forwardAll)
        { controller.forwardAll();}
        if ((JButton)ae.getSource()==bt_pass)
        { controller.pass();}
        if ((JButton)ae.getSource()==bt_autoPlay)
        { controller.autoPlay();}
        if ((JButton)ae.getSource()==bt_playSpeed)
        { controller.updateInterval();}
        if ((JButton)ae.getSource()==bt_examine)
        { controller.examine();}
    }

    /** 
     *  「閉じる」「終了」の操作確認をするダイアログを表示します。 
     *  @return 確認のダイアログで選択された結果コードを表す int
     */
    public int askClosedOperation(){
        ClosedOperationDialog dialog=new ClosedOperationDialog();
        dialog.setLocation(300,200);
        return dialog.showClosedDialog(jf,"囲碁Viewer");
    }

    /** 
     *  「開く」のダイアログを表示します。 
     *  @return  ファイル名(フルパス指定)を表す String
     */
    public String showOpenDialog(){

        String fileName=new String("");
        JFileChooser jfc=new JFileChooser(lastDir);
        FileNameExtensionFilter filter = new FileNameExtensionFilter("SGF Text", "sgf");
        jfc.setFileFilter(filter);
        if (jfc.showOpenDialog(jf)==JFileChooser.APPROVE_OPTION){    //「開く」ボタン押下時 
            File file=jfc.getSelectedFile();
            fileName=file.getPath();
            lastDir=file.getParentFile();
        }
        return fileName;
    }

    /** 
     *  「上書き保存」のダイアログを表示します。 
     *  @return  ファイル名(フルパス指定)を表す String
     */
    public String showSaveDialog(){

        String fileName=new String("");
        JFileChooser jfc=new JFileChooser(lastDir);
        FileNameExtensionFilter filter = new FileNameExtensionFilter("SGF Text", "sgf");
        jfc.setFileFilter(filter);
        jfc.setSelectedFile(new File(lastDir,eventName + gameName + ".sgf"));
        if(jfc.showSaveDialog(jf)==JFileChooser.APPROVE_OPTION){
            File file=jfc.getSelectedFile();
            fileName=file.getPath();
            lastDir=file.getParentFile();
        }
        return fileName;
    }

    /** 
     *  再生速度ボタンが押下されたときに呼び出します。
     *  @return  再生速度(秒数)
     */
    public int askIntervalTime(){
        SelectIntervalDialog dialog=new SelectIntervalDialog(jf,"再生速度",true);
        dialog.setLocation(850,320);
        return dialog.showIntervalDialog();
    }

    /** 
     *  対局情報ダイアログを表示します。
     *  @param gameInfo 対局情報を表す GameInformation
     */
    public void showGameInfoDialog(GameInformation gameInfo){
        GameInfoDialog gameInfoDialog=new GameInfoDialog(jf,"対局情報",true,controller);
        gameInfoDialog.showDialog(gameInfo);
    }

    //------------------------------------------------------------------------
    private void changeBoard(int boardType){
        switch(boardType){
            case IgoPanel.GRAPHIC_BOARD:
                jf.getContentPane().remove(jp_ban);
                jp_ban=graphicBan;
                setupBoard();
                jf.getContentPane().add(jp_ban);
                jf.getContentPane().validate();
                break;
            case IgoPanel.IMAGE_BOARD:
                jf.getContentPane().remove(jp_ban);
                jp_ban=imageBan;
                setupBoard();
                jf.getContentPane().add(jp_ban);
                jf.getContentPane().validate();
                break;
            case IgoPanel.TEXT_BOARD:
                jf.getContentPane().remove(jp_ban);
                jp_ban=textBan;
                setupBoard();
                jf.getContentPane().add(jp_ban);
                jf.getContentPane().validate();
                break;
        }
        jf.repaint();
        controller.doUpdateView();
    }

}