package jongs;

import java.awt.Color;
import java.awt.Font;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.Rectangle;
import java.util.Vector ;
import java.util.HashMap ;
import java.io.File ;
import java.io.IOException;

import javax.imageio.ImageIO;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.ImageObserver;
import java.awt.image.BufferedImage;
//import java.awt.image.VolatileImage;


// 
// JONGS - Java Open Novels Game Script
// 
//    JongsDisplayManagement.java 
//
//  Copyright 2007  DENZI TOMIZKA
//
//  ver 1.00 Create by DENZI TOMIZKA 2007.5.25
//  ver 1.01           DENZI TOMIZKA 2007.5.28
//  ver 1.02           DENZI TOMIZKA 2007.5.31
//
/*
 *-----------------------------------------------------------------------
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *----------------------------------------------------------------------
 */


public class jongsDisplayManagement {

	//セル位置の定数
	public final static int POS_UPPER      =0x7fffffff ;
	public final static int POS_LOWER      =0x7ffffffe ;
	public final static int POS_MIDDLE     =0x7ffffffd ;
	public final static int POS_LEFT       =0x7ffffffc ;
	public final static int POS_RIGHT      =0x7ffffffb ;
	public final static int POS_CENTER     =0x7ffffffa ;

	//画像セル系
	HashMap <String , int[]> positionMap ;
	jongsImage [] cellImages ;
	//画面バッファ
	BufferedImage disp_buf00 = null ; //１次画面バッファ
	Graphics   gdb00  ;	
	//画面バッファ
	BufferedImage disp_buf02 = null ; //Shift押下時次画面バッファ
	Graphics   gdb02  ;	
	
	//Display本体
	Frame dispFrame ;
	jongsDisplay disp ;
	Thread dispThread;
	
	//文字列
	String Text;
	
	//選択系パラメータ
	Rectangle selectArea00 ;
	int [] selectColor00 ;
	Font selectFont00;

	
	public jongsDisplayManagement() {
		initJDM(800 , 600);
	}

	public jongsDisplayManagement(int w , int h) {
		initJDM(w , h);
	}
	
	private void initJDM( int w , int h ) {
		initPositionCode();
		createDisplay(w , h);
		
		//選択ボタンのディフォルトパラメータ
		selectArea00 = new Rectangle(w/10 , h/2 , (w * 8) / 10 , h / 5 );
		selectColor00 = new int[] { 0xffffff , 0xaaaaff , 0xffffcc , 0x000044 };
		selectFont00 = new Font("Dialog",Font.BOLD,8);
	
	}
	
	/**
	 *型・基本処理系予約語の登録 
	 */
	private void initPositionCode() {		
		// 位置定数予約語の登録
		positionMap = new HashMap <String , int[] >();
		positionMap.put("_upper", this.getIntArray(POS_UPPER) ); 
		positionMap.put("_lower", this.getIntArray(POS_LOWER) ); 
		positionMap.put("_top", this.getIntArray(POS_UPPER) ); 
		positionMap.put("_bottom", this.getIntArray(POS_LOWER) ); 
		positionMap.put("_middle", this.getIntArray(POS_MIDDLE) ); 
		positionMap.put("_left", this.getIntArray(POS_LEFT) ); 
		positionMap.put("_right", this.getIntArray(POS_RIGHT) ); 
		positionMap.put("_center", this.getIntArray(POS_CENTER) ); 
	}

	public int[] getIntArray(int i ){
		int [] a = new int[1];
		a[0] = i;
		return a ;
	}

	public void waitMiliSec(int w) {
		if (w == 0 ) return ;
		long start = System.currentTimeMillis();
		long wtime = w;
		while ( wtime < (System.currentTimeMillis() - start) ) ;	
	}	
	
	
	/**
	 * 画像を読み込む
	 * @param filename
	 * @return
	 */
	public BufferedImage loadImage(String filename,ImageObserver iob) {
		BufferedImage bimg ;
		try {
		    bimg = ImageIO.read(new File("./"+filename));		    
		} catch (IOException ioe) {
		    return null ;
		}
		return bimg ;
	}
	
	
	/**
	 * 座標を指定し、セルをクリックしていたならばセル番号を返す
	 * @param zx
	 * @param zy
	 * @return 該当セルなし -1
	 */
	public int checkCellClick(int zx , int zy ) {
		//上に載せてあるセルからチェックする
		for (int n = (this.cellImages.length-1) ; n >= 0  ; n--  ) {
			if ( this.cellImages[n] != null ) {
				if ( this.cellImages[n].checkImageClick(zx, zy) == true) 
					return n ;
			}
		}
		return -1 ;
	}


	
	public boolean diaplayBusy() {
		boolean busy = ( disp.commandCode != 0 || disp.busy == true);
		return busy;
	}
	
	/**
	 * 可視状態のセル数を数える
	 * @return
	 */
	public int visibleCellNum() {
		int n , v ;
		for ( n = v = 0 ; n < this.cellImages.length ; n++) {
			if ( cellImages[n] != null )
				if ( cellImages[n].view == true ) v++;
		}
		return v;
	}
	
	
	
	/*****************************************************************/
	/*****************************************************************/
	/*****画面系の処理*************************************************/
	/*****************************************************************/
	/*****************************************************************/
	/**
	 * 画面を作って出す
	 * @param w
	 * @param h
	 */
	public void createDisplay(int w , int h) {
		this.dispFrame = new Frame();
		//Windowを閉じたら終了
		this.dispFrame.addWindowListener(new WindowAdapter(){
            public void windowClosing(WindowEvent e){
            	System.exit(0);
            }
        });
		this.dispFrame.setSize(w+6, h+20);
		this.disp = new jongsDisplay(w , h);
		this.dispFrame.add(disp);
		this.dispThread = new Thread(disp);
		
		//this.dispFrame.setUndecorated(true);
		this.dispFrame.setVisible(true);
		
		//一次画面バッファを作成する
        disp_buf00 = new BufferedImage(w, h, BufferedImage.TYPE_INT_BGR);
        gdb00 = disp_buf00.getGraphics();
        disp_buf02 = new BufferedImage(w, h, BufferedImage.TYPE_INT_BGR);
        gdb02 = disp_buf02.getGraphics();
        this.dispThread.start();
	}
	
	/**
	 * 画面を破棄する
	 *
	 */
	public void dispose() {
		this.dispFrame.dispose();
	}
	
	
	/**
	 * 一次画面バッファを描画する
	 */
	public void drawImageBuf() {
		int n ;
		Graphics dbg = this.gdb00;
		jongsImage ji ;
		dbg.setColor(Color.black);
		dbg.fillRect(0, 0, disp_buf00.getWidth(), disp_buf00.getHeight());
		for ( n = 0 ; n < this.cellImages.length ; n++ ) {
			if ( this.cellImages[n] != null && this.cellImages[n].view ) {
				ji = this.cellImages[n];
				dbg.drawImage(ji.getImage(), ji.getX(), ji.getY(), ji.getWidth(), ji.getHeight(), disp);
				ji.drawText(dbg);
			}
		}
	}

	/**
	 * Shift押下時画面バッファを描画する
	 */
	public void drawShiftImageBuf() {
		int n ;
		Graphics dbg = this.gdb02;
		jongsImage ji ;
		dbg.setColor(Color.black);
		dbg.fillRect(0, 0, disp_buf02.getWidth(), disp_buf02.getHeight());
		for ( n = 0 ; n < this.cellImages.length ; n++ ) {
			if ( this.cellImages[n] != null && this.cellImages[n].view && this.cellImages[n].shiftview) {
				ji = this.cellImages[n];
				dbg.drawImage(ji.getImage(), ji.getX(), ji.getY(), ji.getWidth(), ji.getHeight(), disp);
				ji.drawText(dbg);
			} 
		}
	}

	
	public void setShiftView(int cell) {
		if (  this.cellImages[cell] != null  ) {
			this.cellImages[cell].shiftview = true ;
		}
	}
	
	public void setShiftHide(int cell) {
		if (  this.cellImages[cell] != null  ) {
			this.cellImages[cell].shiftview = false ;
			System.out.println("setShiftHide:hide_cell="+cell+":"+this.cellImages[cell].shiftview);
		}
	}
	
	/**
	 * 位置コードマップから位置コードを得る
	 * @param tr
	 * @return
	 */
	public int getPositionCode(String tr) {
		String tstr = tr.trim().toLowerCase();
		if ( positionMap.containsKey(tstr) ) {
			return positionMap.get(tstr)[0];
		}
		return 0 ;
	}

	
	/**
	 * 位置コードから計算し、位置座標を得る
	 * @param cell
	 * @param x
	 * @param y
	 * @return Point 計算した座標
	 */
	public Point changePosition(int cell , int x , int y ) {
		int w , h ;
		switch (x) {
		case POS_LEFT:
			x = 0 ;
			break ;
		case POS_RIGHT:
			w = this.cellImages[cell].getWidth();
			x = disp.width - w;
			break ;
		case POS_CENTER:
			w = this.cellImages[cell].getWidth();
			x = (disp.width - w) / 2 ;
			break ;
		}

		switch (y) {
		case POS_UPPER:
			y = 0 ;
			break ;
		case POS_LOWER:
			h = this.cellImages[cell].getHeight();
			y = disp.height - h ;
			break ;
		case POS_MIDDLE:
			h = this.cellImages[cell].getHeight();
			y = ( disp.height - h  ) / 2;
			break ;
		}

		Point pt = new Point(x,y);
		return pt;
	}
	
	/**
	 * 画面を構築し画面に描画する。
	 */
	public void viewDisplay() {
		this.drawImageBuf();
		this.drawShiftImageBuf();
		disp.viewDisplay(this.disp_buf00,this.disp_buf02);
	}
		

	/**
	 * セルの表示非表示をセット、
	 * 画面を構築し画面に描画する。
	 */
	public void viewDisplay(String flagStr) {
		this.setCellView(flagStr);
		this.drawImageBuf();
		this.drawShiftImageBuf();
		disp.viewDisplay(this.disp_buf00,this.disp_buf02);
	}
	
	public void setDisplaySize(int w , int h) {
		this.createDisplay(w, h);
	}

	
	/**
	 * 画面を構築しトランシジョンしつつ画面に描画する。
	 *
	 */
	public void transitionDisplay(String tstr ) {
		this.drawImageBuf();
		this.drawShiftImageBuf();
		disp.transitionDisplay(this.disp_buf00 , this.disp_buf02 , tstr );
	}
	
	/**
	 * セルの表示非表示をセット、
	 * 画面を構築しトランシジョンしつつ画面に描画する。
	 */
	public void transitionDisplay(String flagStr, String tstr ) {
		this.setCellView(flagStr);
		this.drawImageBuf();
		this.drawShiftImageBuf();
		disp.transitionDisplay(this.disp_buf00 , this.disp_buf02 , tstr );
	}
	
	
	/**
	 * 画面に登録してあるトランシジョン効果の予約語から
	 * トランシジョンコードを得る
	 * @param tr
	 * @return
	 */
	public int getTransitionCode( String tr ) {
		return disp.getTransitionCode(tr);
	}
	
	
	/*****************************************************************/
	/*****************************************************************/
	/*****セル系の処理*************************************************/
	/*****************************************************************/
	/*****************************************************************/

	/**
	 * 全てのセル数を決定する
	 * @param cn
	 */
	public void setCellNumber(int cn ){
		cellImages = new jongsImage[cn];
	}
	
	/**
	 * 指定セル番号に画像を読み込む
	 * @param cell
	 * @param filename
	 * @return
	 */
	public boolean setCellImage(int cell,String filename) {
		BufferedImage bimg ;
		if (this.cellImages[cell] == null) {
			this.cellImages[cell] = new jongsImage();
			this.cellImages[cell].setPoint(0, 0);
		}	
		bimg = this.loadImage(filename, disp);	
		if (bimg == null) return false ;
		cellImages[cell].setImage(bimg, disp);
		cellImages[cell].setFileName(filename);
		return true ;
	}
	
	/**
	 * 指定セル番号の透過色をRGBAで直接設定する１
	 * @param cell
	 * @param tc
	 */
	public void setTransColor(int cell , int tc ) {
		if (this.cellImages[cell] != null) {
			cellImages[cell].setTransColor(tc);
		}
	}
	
	/**
	 * 指定セル番号の透過色をRGBとA値で設定する
	 * @param cell
	 * @param col
	 * @param alpha
	 */
	public void setTransColor(int cell , int col , int alpha) {
		int tc ;
		if (this.cellImages[cell] != null) {
			tc = ( alpha << 24 ) | (col & 0x00ffffff);
			cellImages[cell].setTransColor(tc);
		}
	}

	
	/**
	 * 指定セル番号の透過色を座標指定する
	 * @param cell
	 * @param x
	 * @param y
	 * @param alpha
	 */
	public void setTransColor(int cell , int x , int y , int alpha) {
		if (this.cellImages[cell] != null) {
			cellImages[cell].setTransColor(x,y,alpha);
		}
	}

	/**
	 * 指定セル番号の座標を設定する
	 * @param cell
	 * @param x
	 * @param y
	 */
	public void setCellPosition(int cell , int x , int y ) {
		Point pt ;
		if (this.cellImages[cell] != null) {
			pt = this.changePosition(cell, x, y);
			cellImages[cell].setPoint(pt);
		}
	}

	/**
	 * 指定セル番号のサイズを設定する
	 * @param cell
	 * @param x
	 * @param y
	 */
	public void setCellSize(int cell , int w , int h ) {
		if (this.cellImages[cell] != null) {
			cellImages[cell].setSize(w, h);
		}
	}
	
	/**
	 * 
	 * @param cell
	 * @param w
	 * @param h
	 * @param color
	 */
	public void setCellCanvas( int cell , int w , int h , int color) {
		if (this.cellImages[cell] == null) {
			this.cellImages[cell] = new jongsImage();
			this.cellImages[cell].setPoint(0, 0);
		}	
		this.cellImages[cell] = new jongsImage();
		this.cellImages[cell].setCanvas(w, h, color);		
	}
	
	
	/**
	 * 
	 * @param cell
	 * @param fname
	 * @param stl
	 * @param size
	 * @param fcol
	 * @param fx
	 * @param fy
	 */
	public void setCellTextParameter( int cell , String fname , String stl , int size,  int fcol ) {
		int style ;
		if (this.cellImages[cell] != null) {
			if ( stl.length() == 0 ) {
				style = Font.PLAIN;
			} else {
				if (stl.equals("plain")) style = Font.PLAIN;
				else if (stl.equals("italic")) style = Font.ITALIC;
				else if (stl.equals("bold")) style = Font.BOLD;
				else style = Font.PLAIN;
			}
			
			this.cellImages[cell].setTextParam(fname, style, size, fcol );
		}		
	}
	
	
	/**
	 * 
	 * @param cell
	 * @param txt
	 */
	public void setCellText( int cell , String txt ) {
		if (this.cellImages[cell] != null) {
			this.cellImages[cell].setText(txt);
		}
	}
	

	
	/**
	 * フラグ設定文字列に従い、セルの表示フラグを設定する
	 * @param flagStr
	 */
	public void setCellView(String flagStr) {
		for ( int n = 0 ; n < flagStr.length() && n < this.cellImages.length ; n++ ){
			char c = flagStr.charAt(n);
			if (this.cellImages[n] != null) {
				if (c == '0') this.cellImages[n].setView(false) ;
				else if (c == '1') this.cellImages[n].setView(true) ;				
			}
		}
	}
	
	/**
	 * セル番号とフラグ設定指定にに従い、セルの表示フラグを設定する
	 * @param flag
	 */
	public void setCellView(int flag) {
		int flagHex = flag ;
		for ( int n = 0 ; n < 32 && n < this.cellImages.length ; n++ ){
			int c = flagHex & 0x00000001 ;	
			if (this.cellImages[n] != null) {
				if (c == 0) this.cellImages[n].setView(false) ;
				else if (c == 1 )  this.cellImages[n].setView(true) ;				
			}
			flagHex = flagHex >> 1;
		}
	}		
	public void setCellView(int cell , int val) {
		if ( cell < this.cellImages.length && this.cellImages[cell] != null) {
			if (val == 0) this.cellImages[cell].setView(false) ;
			else if (val == 1 )  this.cellImages[cell].setView(true) ;
		}
	}
	public void setCellView(int cell , String val) {
		if ( cell < this.cellImages.length &&  this.cellImages[cell] != null) {
			if (val.trim().toLowerCase().equals("false")) 
				this.cellImages[cell].setView(false) ;
			else if (val.trim().toLowerCase().equals("true") ) 
				this.cellImages[cell].setView(true) ;
		}
	}


	/*****************************************************************/
	/*****************************************************************/
	/*****テキスト系の処理*********************************************/
	/*****************************************************************/
	/*****************************************************************/
	
	/**
	 * 画面にテキストのフォント色を設定する
	 * @param fc1 文字色
	 * @param fc2 縁取り色
	 */
	public void setFontColor( int fc1 , int fc2 ) {
		disp.setFontColor(fc1, fc2);
	}
	
	/**
	 * 画面にテキストのフォントを設定する
	 * @param n
	 * @param s
	 * @param sz
	 */
	public void setTextFont(String n , String s , int sz ) {
		String name = n.trim() ;
		String stl = s.trim().toLowerCase() ;
		int style = 0 ;
		int size = sz ;

		if ( name.length() == 0 ) {
			name = disp.textFont.getFontName();
		}
		
		if ( stl.length() == 0 ) {
			style = disp.textFont.getStyle();
		} else {
			if (stl.equals("plain")) style = Font.PLAIN;
			else if (stl.equals("italic")) style = Font.ITALIC;
			else if (stl.equals("bold")) style = Font.BOLD;
			else style = Font.PLAIN;
		}
		
		if (size == 0 ) size= disp.textFont.getSize();
		
		disp.setTextFont(new Font(name,style,size));
	}
	
	
	/**
	 * 画面にテキストの表示領域をセットする
	 * @param x
	 * @param y
	 * @param w
	 * @param h
	 */
	public void setTextArea(int x , int y ,int w , int h) {
		disp.setTextArea(x, y, w, h);
	}

	/**
	 * 画面にテキストの文字間オフセットを設定する
	 * @param ls 文字間
	 * @param lh 行間
	 */
	public void setTextInterval(int ls, int lh) {
		disp.setTextInterval(ls, lh);
	}
	
	/**
	 * 画面に文字列を出しイベントを待つ
	 * @param txt
	 */
	public void drawText(String txt) {
		disp.drawText(txt);
	}
	
	/**
	 * 画面に文字列を出し、指定時間待つ 
	 * @param txt
	 */	
	public void drawWaitText(String txt,int time) {
		disp.drawWaitText(txt,time);
	}
	

	/*****************************************************************/
	/*****************************************************************/
	/*****選択系の処理*************************************************/
	/*****************************************************************/
	/*****************************************************************/	
	
	/**
	 * 選択ボタンの表示エリアを定義する
	 * @param xnum  Ｘ軸方向ボタン数
	 * @param ynum  Ｙ軸方向ボタン数
	 * @param xspan Ｘ軸方向ボタン間隔
	 * @param yspan Ｙ軸方向ボタン間隔
	 */
	public void calcSelectButton( Rectangle Area ,  int xnum , int ynum , int xspan , int yspan ) {
		int bx , by , bn , bpx , bpy ;
		int bnum = xnum * ynum ;
		int bwid =  ( Area.width - xspan * (xnum-1) ) / xnum ;
		int bhei =  ( Area.height - yspan * (ynum-1) ) / ynum ;
		if ( bwid < 0 ) {
			bwid = Area.width / xnum ;
			xspan = 0 ;
		}
		if ( bhei < 0 ) {
			bhei = Area.height / ynum ;
			yspan = 0 ;
		}
		
		Rectangle [] button = new Rectangle [bnum] ;
		bn = 0 ;
		for ( bpy = Area.y , by = 0 ; by < ynum ; by ++ , bpy += bhei + yspan) {
			for ( bpx = Area.x , bx = 0 ; bx < xnum ; bx ++ , bpx += bwid + xspan) {
				button[bn] = new Rectangle(bpx , bpy , bwid , bhei);
				bn++ ;
			}
		}
		disp.setSelectButton(button);
	}
	
	
	public void setSelectParam( Vector<String> vct  ) {
		int selnum = vct.size();
		disp.setSelectArea(selectArea00);
		disp.setSelectColor(selectColor00);
		disp.setSelectText(vct,0,selnum);
		if ( disp.selectbtn.length < selnum ) {
			if ( selectArea00.width > selectArea00.height )
				calcSelectButton( selectArea00 ,  selnum  , 1 , 0 , 0 ) ;
			else
				calcSelectButton( selectArea00 ,  1 , selnum  , 0 , 0 ) ;
		}
		disp.selectClick();	
	}

	
	/**
	 * 画面にセレクトボタンの表示領域をセットする
	 * @param x
	 * @param y
	 * @param w
	 * @param h
	 */
	public void setSelectArea(int x , int y ,int w , int h) {
		selectArea00 = new Rectangle(x, y, w, h) ;
		disp.setSelectArea(selectArea00);
		calcSelectButton( selectArea00 ,  1 , 1 , 0 , 0 ) ;		
	}

	/**
	 * 画面にセレクトボタンの表示領域をセットする
	 */
	public void setSelectArea(int x , int y ,int w , int h , int xbnum , int ybnum ) {
		selectArea00 = new Rectangle(x, y, w, h) ;
		disp.setSelectArea(selectArea00);
		calcSelectButton( selectArea00 ,  xbnum , ybnum , 0 , 0 ) ;		
	}

	/**
	 * 画面にセレクトボタンの表示領域をセットする
	 */
	public void setSelectArea(int x , int y ,int w , int h , int xbnum , int ybnum , int xspan , int yspan ) {
		selectArea00 = new Rectangle(x, y, w, h) ;
		disp.setSelectArea(selectArea00);
		calcSelectButton( selectArea00 ,  xbnum , ybnum , xspan , yspan ) ;		
	}

	
	
	/**
	 * 画面にセレクトボタンの色をセットする
	 * @param c1 通常色
	 * @param c2 選択色
	 * @param c3 押下色
	 * @param c4 文字色
	 */
	public void setSelectColor(int c1 , int c2 ,int c3 , int c4) {
		selectColor00 = new int [4] ;
		selectColor00[0] = c1 ;
		selectColor00[1] = c2 ;
		selectColor00[2] = c3 ;
		selectColor00[3] = c4 ;
		disp.setSelectColor(selectColor00);
	}
	
	
	
	/**
	 * ボタンセレクトの返却値を返す
	 * @return
	 */
	public int getSelectButton() {
		return disp.selectButton ;
	}

	public String getSelectText() {
		return disp.selectText ;
	}
	
	/**
	 * 画面にセルのクリックを要求するコマンドを送る
	 *
	 */
	public void cellClick() {
		disp.cellClick();
	}
	
	/**
	 * クリックされたセル番号を得る
	 * @return
	 */
	public int getSelectCell() {
		int cellvalue , press ;
		//全セルに対しクリック位置をチェックする
		cellvalue = this.checkCellClick(disp.releasex , disp.releasey );
		press = this.checkCellClick(disp.pressx , disp.pressy );
		//マウスのプレスとリリースイベントの箇所が同一セルでなければ
		//クリックされたとみなさない
		if ( cellvalue != press ) {
			disp.cellClick();
			cellvalue = -1;
		}
		return cellvalue ;
	}
	
}
