/**
 * IgoPlayModel クラスは、再生モードのモデルを提供します。
 * @author Jiro Suzuki
 */
public class IgoPlayModel extends IgoModel implements Runnable{

    private Kifu kifuForPlay;
    private int numForPlay;
    private boolean isAutoPlay;
    private int second;
    private int milSecond1;
    private int milSecond2;
    private Thread autoPlay;

    /**
     * 指定された親インスタンスに対するインスタンスを生成します。
     * @param parent 親インスタンス
     */
    IgoPlayModel(IgoModel parent){
        super(parent);
    }


    /**
     * インスタンスを初期化します。
     * @param view ビュー
     */
    public void initialize(IgoView view){
        super.initialize(view);
        kifuForPlay=new Kifu();
        numForPlay=-1;
        isAutoPlay=false;
        second=0;
        milSecond1=0;
        milSecond2=0;
    }

    /**
      * ファイルを開くときに呼び出します。
      */
    public void open(){
        isAutoPlay=false;
        view.changeController(parent);
        parent.start();
        parent.open();
    }

    /**
      * ファイルを新規作成するときに呼び出します。
      */
    public void record(){
        isAutoPlay=false;
        IgoNewModel newModel=new IgoNewModel(parent);
        newModel.initialize(view);
        view.changeController(newModel);
        newModel.start();
    }

    /**
      * 現行モードを終了するときに呼び出します。
      */
    public void exit(){
        System.exit(0);
    }

    /**
      * メインダイアログを閉じるときに呼び出します。
      */
    public void close(){
        isAutoPlay=false;
        view.changeController(parent);
        parent.start();
    }

    /**
      * 編集状態にするときに呼び出します。
      */
    public void startEdit(){
        isAutoPlay=false;
        IgoModModel modModel=new IgoModModel(parent);
        view.changeController(modModel);
        modModel.initialize(view);
        modModel.start(kifu,numOfMoves,fileName,turn);
    }

    /**
      * 一手戻るときに呼び出します。
      */
    public void back(){
        isAutoPlay=false;
        if(numOfMoves>0){
            numOfMoves--;
            intBoard.initialize();
            for(int i=0;i<=numOfMoves;i++){
                Move move=kifu.getOrderOfMoves().getMove(i);
                intBoard.update(move.getStoneColor(),move.getRow(),move.getCol());
            }
            if(isExam){
                turn.setTurn(kifu.getOrderOfMoves().getMove(numOfMoves).getStoneColor());
                turn.changeTurn();
            }
        }else if(numOfMoves==0){
            numOfMoves--;
            intBoard.initialize();
            if(isExam){
                turn.setTurn(Turn.TURN_BLACK);
            }
        }
        view.updateView(mode,isExam,intBoard,kifu.getGameInformation(),numOfMoves,isAutoPlay);
    }

    /**
      * 一手進むときに呼び出します。
      */
    public void forward(){
        OrderOfMoves order=kifu.getOrderOfMoves();
        if(numOfMoves<order.size()-1){
            numOfMoves++;
            Move move=order.getMove(numOfMoves);
            intBoard.update(move.getStoneColor(),move.getRow(),move.getCol());
            turn.setTurn(move.getStoneColor());
            turn.changeTurn();
            view.updateView(mode,isExam,intBoard,kifu.getGameInformation(),numOfMoves,isAutoPlay);
        }
    }

    /**
      * 五手戻るときに呼び出します。
      */
    public void back5(){
        isAutoPlay=false;
        intBoard.initialize();
        if(numOfMoves>4){
            numOfMoves=numOfMoves-5;
            for(int i=0;i<=numOfMoves;i++){
                Move move=kifu.getOrderOfMoves().getMove(i);
                intBoard.update(move.getStoneColor(),move.getRow(),move.getCol());
            }
            turn.setTurn(kifu.getOrderOfMoves().getMove(numOfMoves).getStoneColor());
            turn.changeTurn();
        }else{
            numOfMoves=-1;
            turn.setTurn(Turn.TURN_BLACK);
        }
        view.updateView(mode,isExam,intBoard,kifu.getGameInformation(),numOfMoves,isAutoPlay);
    }

    /**
      * 五手進むときに呼び出します。
      */
    public void forward5(){
        isAutoPlay=false;
        numOfMoves=numOfMoves+5;
        OrderOfMoves order=kifu.getOrderOfMoves();
        if(numOfMoves>order.size()-1){
            numOfMoves=order.size()-1;
        }
        intBoard.initialize();
        for(int i=0;i<=numOfMoves;i++){
            Move move=order.getMove(i);
            intBoard.update(move.getStoneColor(),move.getRow(),move.getCol());
        }
        if(numOfMoves>-1){
            turn.setTurn(kifu.getOrderOfMoves().getMove(numOfMoves).getStoneColor());
            turn.changeTurn();
        }else{
            turn.setTurn(Turn.TURN_BLACK);
        }
        view.updateView(mode,isExam,intBoard,kifu.getGameInformation(),numOfMoves,isAutoPlay);
    }

    /**
      * 初手に戻るときに呼び出します。
      */
    public void backAll(){
        isAutoPlay=false;
        numOfMoves=-1;
        turn.setTurn(Turn.TURN_BLACK);
        intBoard.initialize();
        view.updateView(mode,isExam,intBoard,kifu.getGameInformation(),numOfMoves,isAutoPlay);
    }

    /**
      * 終局面にするときに呼び出します。
      */
    public void forwardAll(){
        isAutoPlay=false;
        OrderOfMoves order=kifu.getOrderOfMoves();
        numOfMoves=order.size()-1;
        intBoard.initialize();
        for(int i=0;i<=numOfMoves;i++){
            Move move=order.getMove(i);
            intBoard.update(move.getStoneColor(),move.getRow(),move.getCol());
        }
        if(numOfMoves>-1){
            turn.setTurn(kifu.getOrderOfMoves().getMove(numOfMoves).getStoneColor());
            turn.changeTurn();
        }else{
            turn.setTurn(Turn.TURN_BLACK);
        }
        view.updateView(mode,isExam,intBoard,kifu.getGameInformation(),numOfMoves,isAutoPlay);
    }

    /**
      * パスをするときに呼び出します。
      */
    public void pass(){
        if(isExam){
            turn.changeTurn();
        }
    }

    /**
      * 設計途中で変更が生じたため、棚上げ。
      */
    public void pause(){} //棚上げ

    /**
      * 検討モードと再生モードを行き来するときに呼び出します。
      */
    public void examine(){
        isAutoPlay=false;
        if(isExam){
            kifuForExam=null;
            turn=new Turn();
            isExam=false;
            intBoard.initialize();
            kifu=kifuForPlay;
            numOfMoves=numForPlay;
            for(int i=0;i<=numOfMoves;i++){
                Move move=kifu.getOrderOfMoves().getMove(i);
                intBoard.update(move.getStoneColor(),move.getRow(),move.getCol());
            }
            view.updateView(mode,isExam,intBoard,kifu.getGameInformation(),numOfMoves,isAutoPlay);
        }else{
            kifuForExam=kifu.createExamClone(numOfMoves);
            kifu=kifuForExam;
            numForPlay=numOfMoves;
            if(numOfMoves>-1){
                int stoneColor=kifu.getOrderOfMoves().getMove(numOfMoves).getStoneColor();
                turn.setTurn(stoneColor);
                turn.changeTurn();
            }else{
                turn.setTurn(Turn.TURN_BLACK);
            }
            isExam=true;
            view.updateView(mode,isExam,intBoard,kifu.getGameInformation(),numOfMoves,isAutoPlay);
        }
    }

    /**
     * 石を置くときに呼び出します。
     * @param row 行番号
     * @param col 列番号
     */
    public void putStone(int row,int col){
        if(isExam){
            OrderOfMoves order=kifu.getOrderOfMoves();
            if((intBoard.getIntBoard())[row][col]==0){
                for(int i=order.size()-1;i>numOfMoves;i--){
                    order.remove(i);
                }
                kifu.addMove(new Move(turn.getTurn(),row,col));
                turn.changeTurn();
                numOfMoves++;
                Move move=order.getMove(numOfMoves);
                intBoard.update(move.getStoneColor(),move.getRow(),move.getCol());
                view.updateView(mode,isExam,intBoard,kifu.getGameInformation(),numOfMoves,isAutoPlay);
            }
        }
    }

    /**
      * 対局情報を更新するときに呼び出します。
      */
    public void updateGameInfo(GameInformation gameInfo){
        isAutoPlay=false;
        kifu.getGameInformation().setBlackPlayer(gameInfo.getBlackPlayer());
        kifu.getGameInformation().setWhitePlayer(gameInfo.getWhitePlayer());
        kifu.getGameInformation().setBlackRank(gameInfo.getBlackRank());
        kifu.getGameInformation().setWhiteRank(gameInfo.getWhiteRank());
        kifu.getGameInformation().setEvent(gameInfo.getEvent());
        kifu.getGameInformation().setGameName(gameInfo.getGameName());
        kifu.getGameInformation().setDate(gameInfo.getDate());
        kifu.getGameInformation().setKomi(gameInfo.getKomi());
        kifu.getGameInformation().setResult(gameInfo.getResult());
        view.updateView(mode,isExam,intBoard,kifu.getGameInformation(),numOfMoves,isAutoPlay);
    }

    /**
      * 自動再生にするときに呼び出します。
      */
    public void autoPlay(){
        if(isAutoPlay){
            stopAutoPlay();
        }else if(isExam==false && numOfMoves<kifu.getOrderOfMoves().size()-1){
            startAutoPlay();
        }
    }

    /**
      * 自動再生の再生速度を変更するときに呼び出します。
      */
    public void updateInterval(){
        isAutoPlay=false;
        if(numOfMoves<kifu.getOrderOfMoves().size()-1){
            second=view.askIntervalTime();
            startThread();
        }
    }

    //--------------------------------------- 自動再生関連メソッド 
    private void stopAutoPlay(){
        isAutoPlay=false;
        view.updateView(mode,isExam,intBoard,kifu.getGameInformation(),numOfMoves,isAutoPlay);
    }

    private void startAutoPlay(){
        if(second==0){
            second=view.askIntervalTime();
        }
        startThread();
    }

    private boolean isContinue(){
        if(isAutoPlay && numOfMoves<kifu.getOrderOfMoves().size()-1){
            return true;
        }else{
            return false;
        }
    }

    private void startThread(){
        if(second!=0){
            isAutoPlay=true;
            view.updateView(mode,isExam,intBoard,kifu.getGameInformation(),numOfMoves,isAutoPlay);
            milSecond1=700;
            milSecond2=second * 1000 - milSecond1;
            autoPlay=new Thread(this);
            autoPlay.start();
        }
    }

    /**
      * 独立して実行されるスレッド内で、このメソッドが呼び出されます。
      * Runnable インターフェースが実装しているメソッドのオーバーライドです。
      */
    public void run(){
        while(isContinue()){
            try{ 
                autoPlay.sleep(milSecond1); 
            }catch(InterruptedException e){}
            forward();
            if(!isContinue()){
                break;
            }
            try{ 
                autoPlay.sleep(milSecond2); 
            }catch(InterruptedException e){
                System.out.println(e);
            }
        }
        stopAutoPlay();
    }

    /**
      * 対局情報を表示ときに呼び出します。
      */
    public void displayGameInfo(){
        view.showGameInfoDialog(kifu.getGameInformation());
    }

    /**
      * ビューを更新するときに呼び出します。
      */
    public void doUpdateView(){
        view.updateView(mode,isExam,intBoard,kifu.getGameInformation(),numOfMoves,isAutoPlay);
    }

    /**
      * 再生モードを開始するときに呼び出します。
      * @param fileName ファイル名(フルパス指定)
      */
    public void start(String fileName){
        this.fileName=fileName;
        SgfReader sr=new SgfReader();
        kifuForPlay=sr.read(fileName);
        kifu=kifuForPlay;
        mode.setMode(Mode.MODE_PLAY);
        view.updateView(mode,isExam,intBoard,kifu.getGameInformation(),numOfMoves,isAutoPlay);
    }
}