/*
 * Created on 2003/05/10
 *
 */
 
 package org.j69.eewiki.wiki;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.nio.charset.CharacterCodingException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;

import org.apache.oro.text.perl.Perl5Util;
import org.j69.eewiki.util.CacheConfig;
import org.j69.eewiki.util.ExtensionFilter;
import org.j69.eewiki.util.StringUtil;
import org.j69.eewiki.util.WebUtil;

/**
 * Wikiデータ1ページ分の情報を格納、操作するクラス
 * @author J.r0ck
 *
 */
public class WikiFile {

   // フィールド
	private String filename;                     // ファイル名（物理ファイル名）
	private String pageindex;                 // ページタイトル先頭1文字
	private String titlename;                    // ページタイトル
	private String linkname;                    // リンク名
	private String subtitlename;              // サブタイトル
	private Date lastmodifiedAsDate;     // 最終更新日時（Date）
	private String lastmodifiedAsString;  // 最終更新日時（String）
	private String passage;                     // 経過時間 
	private boolean locked = false;     // 排他フラグ
	private boolean frozen = false;     // 凍結フラグ
	private boolean usepasswd = false; // パスワードフラグ

	private String pageContent;              // ページコンテンツ

	private String upfilePath;                    //添付ファイルデータ格納ディレクトリ
	private String diffPath;                    //WikiDiffデータ格納ディレクトリ
	private String basePath;                    //Wikiデータ格納ディレクトリ
	private String rootPath;                    //Wiki-rootディレクトリ

	// コンフィグ
	private final static CacheConfig config_ = CacheConfig.getInstance( );
	
	 /**
	  * コンストラクタ　(WikiList作成時に使用される)
	  * @param dirName　これは、Wikiデータの基盤のディレクトリ
	  * @param phyFileName（物理ファイル名）
	  */
	public WikiFile(String phyFileName) {
		filename = phyFileName;
		rootPath = config_.getConfig("define.wiki.basedir");		
		basePath = rootPath + config_.getConfig("define.dir.data");
		diffPath = rootPath + config_.getConfig("define.dir.diff");
		upfilePath = rootPath + config_.getConfig("define.dir.upload");
	}

	/**
	 * コンストラクタ　（Wikiページデータ取得時に使用される）
	 * @param dirName　これは、Wikiのrootディレクトリ
	 * @param pageName ページ名
	 * @param dummy
	 */
   public WikiFile(String pageName, boolean dummy) throws CharacterCodingException {
	 	filename = WebUtil.getEncodeFileName(pageName) + "." + WikiConstants.WIKI_DATA_EXTANSION;
		titlename = pageName;
	    rootPath = config_.getConfig("define.wiki.basedir");		
	  	basePath = rootPath + config_.getConfig("define.dir.data");
		diffPath = rootPath + config_.getConfig("define.dir.diff");
		upfilePath = rootPath + config_.getConfig("define.dir.upload");
   }

	/**
	 * 物理ファイル名の取得
	 * @return
	 */
	public String getFilename() {
		return filename;
	}

	/**
	 * 凍結状態かどうかを取得
	 * TODO　毎回ファイル読むのはどうかと思うが...WIKIの性質上
	 * 誰かが他方で変更する可能性があるので、現状はこのまま
	 * @return
	 */
	public boolean isFrozen() throws IOException {
		frozen = false;
		File file = getContentsFile();
		String str = "";
		BufferedReader in = null;
		
		if (!file.exists())
			return false;
			
		try {
			in = new BufferedReader(new InputStreamReader(
						new FileInputStream(file), WikiConstants.DEFAULT_FILE_ENCODING));

			StringBuffer buffer = new StringBuffer();
			
			if(in.ready()) {
				while ((str = in.readLine()) != null) {
					String diffstr = str.replaceAll("#Frozen", ""); 
					if (!str.equals(diffstr)) {
						frozen = true;
						break;
					}
				}
			}
		} finally {
			if(in != null) {
				in.close();
			}
		}
		return frozen;
	}

	/**
	 * ファイルサイズ（KB）取得※添付ファイル用
	 * 
	 * @return ファイルサイズ（KB）
	 */
	public String getFileSize() {
		return Integer.toString((int)getAttFile().length()/1024 + 1) + "KB";
	}

	/**
	 * ページファイル最終更新時間の取得
	 * 
	 * @return 最終更新時間
	 */
	public String getLastModified() {
		return String.valueOf(getContentsFile().lastModified());
	}

	/**
	 * 最終更新日時（Date）
	 * @return
	 */
	public Date getLastmodifiedAsDate() {
		lastmodifiedAsDate =  new Date(getContentsFile().lastModified());
		return lastmodifiedAsDate;
	}

	/**
	 * 最終更新日時（文字列）
	 * @return
	 */
	public String getLastmodifiedAsString(Locale locale) {
		lastmodifiedAsString = 
			new SimpleDateFormat("yyyy-MM-dd (EEE) HH:mm:ss", locale).format(getLastmodifiedAsDate());
		return lastmodifiedAsString;
	}

	/**
	 * 最終更新日（文字列:日付のみ）
	 * @return
	 */
	public String getLastmodDateAsString(Locale locale) {
		lastmodifiedAsString = 
			new SimpleDateFormat("yyyy-MM-dd", locale).format(getLastmodifiedAsDate());
		return lastmodifiedAsString;
	}

	/**
	 * リンク名
	 * @return
	 */
	public String getLinkname() throws CharacterCodingException {
		linkname = WebUtil.getEncodeFileName(getTitlename());
		return linkname;
	}

	/**
	 * 排他制御かかっているか
	 * @return
	 */
	public boolean isLocked() {
		// TODO Auto-generated catch block
		return locked;
	}

	/**
	 * ページコンテンツの取得
	 * @return
	 */
	public String getPageContent() throws IOException {
		File file = getContentsFile();
		if (file.exists()) 
			return readPageData(file);
		return "";
	}

	/**
	 * Diffページコンテンツの取得
	 * @return
	 */
	public String getDiffPageContent() throws IOException {
		File file = getDiffContentsFile();
		if (file.exists()) 
			return readPageData(file);
		return "";
	}

	/**
	 * ファイル読込み
	 * @return
	 */
	private String readPageData(File file) throws IOException {
		BufferedReader in = null;

		try {
			in = new BufferedReader(new InputStreamReader(
				  new FileInputStream(file), WikiConstants.DEFAULT_FILE_ENCODING));
	
			StringBuffer buffer = new StringBuffer();
			String str = null;
	
			if(in.ready()) {
				buffer.append(in.readLine());
			}
			while ((str = in.readLine()) != null) {
				buffer.append("\n" + str);
			}
	
			pageContent = buffer.toString();
		} finally {
			if(in != null) {
				in.close();
			}
		}		
		return pageContent;
	}
	
	/**
	 * ページインデックスの取得（ページタイトル頭1文字）
	 * @return
	 */
	public String getPageindex() {
		char[] buf = getTitlename().toCharArray();
		StringBuffer sb = new StringBuffer();
		sb.append(buf[0]);
		pageindex = sb.toString();
		return pageindex;
	}

	/**
	 * 変更経過時間
	 * @return
	 */
	public String getPassage() {

		Date now = new Date();
		long passageAsLong = (now.getTime()  - getLastmodifiedAsDate().getTime()) / 1000;
		
		if (Math.ceil(passageAsLong / 60) < 60) {
			passage =  (int)Math.ceil(passageAsLong / 60) + "m";
		}
		else if (Math.ceil(passageAsLong / 60 / 60) < 24) {
			passage = (int)Math.ceil(passageAsLong / 60 / 60) + "h";
		}
		else {
			passage = (int)Math.ceil(passageAsLong / 60 / 60 / 24) + "d";
		}
		return passage;
	}

	/**
	 * サブタイトルの取得
	 * ファイルの先頭1行目をサブタイトルとし、頭に「-」を付ける
	 * ただし、#から始まる行はエスケープする
	 * @return
	 */
	public String getSubtitlename() throws IOException {
		File file = getContentsFile();
		String subTitle = "";
		BufferedReader in = null;
		
		try {
			in = new BufferedReader(new InputStreamReader(
						new FileInputStream(file), WikiConstants.DEFAULT_FILE_ENCODING));

			StringBuffer buffer = new StringBuffer();
			String firstLine = "";
			
			if(in.ready()) {
				while ((firstLine = in.readLine()) != null) {
					String diffstr = firstLine.replaceAll("#Frozen", ""); 
					if (firstLine.equals(diffstr)) {
						if(firstLine.trim().length() > 0) {
							subtitlename = " - " + firstLine;
							break;
						}
					}
				}
			}
		} finally {
			if(in != null) {
				in.close();
			}
		}
		return subtitlename.substring(1, Math.min(subtitlename.length(), 20));
	}

	/**
	 * ページタイトルの取得
	 * @return
	 */
	public String getTitlename() {
		// 拡張子を削除
		titlename = WebUtil.getDecodeFileName(ExtensionFilter.removeExtention(filename));
		return titlename;
	}

	/**
	 * 修正にパスワードが必要なページかどうかを取得
	 * @return
	 */
	public boolean isUsepasswd() {
		//コンフィグから配列で取得
		String[] lockedPages = config_.getConfigArray ("define.admin.lockedpage");

		for (int i = 0; i < lockedPages.length; i++) {
			if(lockedPages[i].equals(getTitlename())) return true;
			if(lockedPages[i].equals("*")) return true;
		}
		return false;
	}

	/**
	 * 編集可能なページかどうか？
	 *
	 * @param pageName 判定対象のページ
	 * @return true:編集可能なページ、false:編集できないページ
	 **/
	public boolean isFixedPage() {
		//コンフィグから配列で取得
		String[] fixedPages = config_.getConfigArray ("define.admin.fixedpage");

		for (int i = 0; i < fixedPages.length; i++) {
			if(fixedPages[i].equals(getTitlename())) return true;
			if(fixedPages[i].equals("*")) return true;
		}
		return false;
	}
	
	/**
	 * 凍結設定
	 */
	public void setFrozen() throws IOException {
		String contens = getPageContent();
		contens = "#Frozen\n" + contens;
		setPageContent(contens, true);
	}

	/**
	 * 解凍設定
	 */
	public void setMelts() throws IOException {
		String contens = getPageContent();
		contens = contens.replaceAll("#Frozen\n", "");
		setPageContent(contens, true);
	}

	/**
	 * ページコンテンツを保存する
	 * @param contens
	 * @param updateTime 更新時間を更新するかどうか
	 */
	public void setPageContent(String contens, boolean updateTime) throws IOException {
		File file = getContentsFile();
		File diffFile = getDiffContentsFile();
		
		//diffデータ取得
		boolean IsMakeDiff = true;
		if (diffFile.exists()) {
			String diffContent = getDiffPageContent();
			//変更あるかどうかを調べる
			if (diffContent.equals(contens)) {
				IsMakeDiff = false;				
			}
		}
		
		//新規作成される場合
		if(!file.exists()) {
			file.getParentFile().mkdirs(); 
			file.createNewFile();
		}
		if ((!diffFile.exists()) || (IsMakeDiff)) {
			diffFile.getParentFile().mkdirs(); 
			diffFile.createNewFile();
		}
		
		long lastModified = file.lastModified(); 
		
		//もし、変更があればDiffファイル作成
		if (IsMakeDiff) 
			writePageData(diffFile, readPageData(file));
		
		//ページ保存
		writePageData(file, contens);
		
		//更新日時を更新
		if(!updateTime) {
			file.setLastModified(lastModified);
		}
	}

	/**
	 * ファイル保存
	 * @param file
	 */
	private void writePageData(File file, String data) throws IOException {
		data.replaceAll("\r", "\n");
		BufferedWriter out = null;
		try {
			out = new BufferedWriter(new OutputStreamWriter(
					new FileOutputStream(file), WikiConstants.DEFAULT_FILE_ENCODING));
			out.write(data);
			out.flush();
		} finally {
			if(out != null) {
				out.close();
			}
		}
	}
	
	/**
	 * ページのファイルオブジェクトの取得
	 * 
	 * @return ページファイル
	 */
	private File getContentsFile() {
		return new File(getContentsPath());
	}

	/**
	 * ページファイルのフルパス取得
	 * 
	 * @return ページファイルのフルパス
	 */
	public String getContentsPath() {
		// basePath/filenameの１文字目/filenameの2文字目/filename
		char[] buf = filename.toCharArray();
		return basePath + "/" + buf[0] + "/" + buf[1] + "/" + filename;
	}

	/**
	 * Diff用ページのファイルオブジェクトの取得
	 * 
	 * @return ページファイル
	 */
	private File getDiffContentsFile() {
		return new File(getContentsDiffPath());
	}

	/**
	 * ページファイルのフルパス取得(Diffディレクトリ用)
	 * 
	 * @return ページファイルのフルパス
	 */
	public String getContentsDiffPath() {
		// diffPath/filenameの１文字目/filenameの2文字目/filename
		char[] buf = filename.toCharArray();
		return diffPath + "/" + buf[0] + "/" + buf[1] + "/" + filename;
	}

	/**
	 * ページのファイルオブジェクトの取得
	 * 
	 * @return ページファイル
	 */
	private File getAttFile() {
		return new File(getAttFilePath());
	}

	/**
	 * ページファイルのフルパス取得(Diffディレクトリ用)
	 * 
	 * @return ページファイルのフルパス
	 */
	public String getAttFilePath() {
		// upfilePath/filenameの１文字目/filenameの2文字目/filename
		char[] buf = filename.toCharArray();
		return upfilePath +  "/" + filename;
	}

	/**
	 * ページファイルの存在チェック
	 * 
	 * @return 存在しているかどうか
	 */
	public boolean isExists() {
		return getContentsFile().exists();
	}

	/**
	 * ファイルの削除
	 */	
	public void deleteFile() {
		if (isExists())
			getContentsFile().delete();
	}

	/**
	 * 差分のデータの取得
	 * @return　ページデータ
	 */
	public String diffWiki() throws IOException  {
		char style = config_.getConfig("define.diff.style").toCharArray()[0];
		String diffResult = StringUtil.diff(getDiffPageContent(), getPageContent(), style);
		return diffResult;
	}

	/**
	 * ページスタイル文字列取得
	 */
	public String getStylePath() throws IOException {
		Perl5Util perl = new Perl5Util();
		String filename = "";
		//#SetCss(スタイルシートファイル名)
		//ページ内に存在した場合、UploadディレクトリのCSSを摘要する
		//最悪スタイルシート無くても問題は大きくない
		String data = getPageContent();
		final String[] datas = data.split( "\n");
		for (int i = 0; i < datas.length; i++) {
			if (perl.match("m!^#SetCss\\(.+\\)$!", datas[i])) {
				filename = perl.substitute("s!^#SetCss\\((.+)\\)\\?*!$1!", datas[i]);
				break;
			}
		}
		
		if (filename.equals( "")) {
			return getDefaultStylePath();
		}
		else {
			String dirName = config_.getConfig("define.dir.upload");
			String fileName = WebUtil.getEncodeFileName(filename);
			
			//ファイルが無ければデフォルトのCSSを摘要
			String fullPath = config_.getConfig("define.wiki.basedir") + dirName + "/" + fileName + ".css";
			File file = new File(fullPath);
			if (file.exists()) {
				return dirName + "/" + fileName + ".css";	
			}
			else {
				return getDefaultStylePath();
			}
		}
	}

	/**
	 * ページスタイル文字列取得
	 */
	public String getDefaultStylePath() {
		String dirName = config_.getConfig("define.dir.css");
		String fileName = config_.getConfig("define.page.style");
		return dirName + "/" + fileName + ".css";
	}
}
