import java.io.BufferedReader;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;public class ChgAnGo {
 // log4jのプロパティフィアル名
 private static String LOG_PROPERTIES = "log4j.properties"; // パスワードファイルパス
 private static String PASSWORD_FILEPATH = "output/password.txt"; // 反転用文字入力ファイルパス
 private static String CONVERT_FILEPATH = "input/convert.txt"; // ログメッセージ:暗号化処理開始
 private static String LOG_ENCRYPT_START = "文字列の暗号化開始"; // ログメッセージ:暗号化処理完了
 private static String LOG_ENCRYPT_END = "文字列の暗号化完了"; // ログメッセージ:暗号化処理前
 private static String LOG_ENCRYPT_BEFORE = "暗号化処理前:"; // ログメッセージ:暗号化処理後
 private static String LOG_ENCRYPT_AFTER = "暗号化処理後:"; //反転用文字を取得できない場合
 private static String LOG_CONVERT_KEY_READ_ERROR = "暗号化のキーワードの取得に失敗しました。"; //ファイルに出力失敗場合
 private static String LOG_FILE_WRITE_ERROR = "暗号化したパスワードのファイル出力に失敗しました。"; // コンソールメッセージ:入力メッセージ
 private static String CONSOLE_INPUT_MESSAGE = "暗号化したいパスワードを入力してください。"; // コンソールメッセージ:入力した文字列バイト数>40場合
 private static String CONSOLE_INPUT_COUNT_ERROR = "エラー:40バイト以内文字列を入力してください。"; // コンソールメッセージ:半角英数字、半角記号以外が入力された場合
 private static String CONSOLE_INPUT_CHAR_ERROR = "エラー:半角英数字、半角記号を入力してください。"; // コンソールメッセージ:ファイルに出力失敗場合
 private static String CONSOLE_FILE_WRITE_ERROR = "エラー:暗号化したパスワードをファイルに出力するのは失敗しました。"; // コンソールメッセージ:反転用文字入力ファイルを取得できない場合
 private static String CONSOLE_CONVERT_KEY_READ_ERROR = "エラー:暗号化のキーワードの取得に失敗しました。"; // コンソールメッセージ:暗号化成功場合
 private static String CONSOLE_SUCCESS_END = "暗号化したパスワードはoutput/password.txtに出力しました。"; // コンソールメッセージ:プログラム終了
 private static String CONSOLE_END = "処理終了しました。"; // ログを出力するために、loggerを取得
 private static Logger logger = Logger.getLogger(ChgAnGo.class.getName()); 
 public static void main(String[] args) {  ChgAnGo chgAnGo = new ChgAnGo();
  //log4jプロパティファイルを取得する
  PropertyConfigurator.configure(LOG_PROPERTIES);  //処理開始ログ出力
  (LOG_ENCRYPT_START);  try {
   //暗号化したい文字列
   String inStr = "";   //暗号化したい文字列の取得
   //コマンドラインから入力した引数が存在(args.length==1)場合
   if (args.length == 1) {
    inStr = args[0];
   } else {    //コマンドラインから入力した引数が不存在(args.length<>1)場合
    //    byte[] input = new byte[41];    //スクリーンへメッセージを出力する。
    System.out.println(CONSOLE_INPUT_MESSAGE);    //スクリーンから暗号化したい文字列を取得する。
    BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    inStr = br.readLine();   }
   // 文字列のチェック
   //入力した文字列バイト数>40場合
   if (inStr.getBytes().length > 40) {
    System.out.println(CONSOLE_INPUT_COUNT_ERROR);
   } else if (inStr.getBytes().length > inStr.length()) {    //    半角英数字、半角記号以外が入力された場合
    System.out.println(CONSOLE_INPUT_CHAR_ERROR);
   } else if ("".equals(inStr)) {    //何も入力しない場合
    System.out.println(CONSOLE_INPUT_CHAR_ERROR);
   } else {    //上記以外場合、文字列に暗号化処理
    //暗号化のキーワードの取得
    String cstrKey = chgAnGo.getConvertKey(CONVERT_FILEPATH);    if (cstrKey != null && !"".equals(cstrKey)) {
     //暗号化処理メソッドを呼び出す
     String ango = chgAnGo.setEncrypt(inStr, cstrKey);     //暗号化した文字列をファイルに出力する。
     chgAnGo.writeToFile(PASSWORD_FILEPATH, ango.getBytes());     //正常終了時、ログを出力する。
     (LOG_ENCRYPT_BEFORE + inStr);
     (LOG_ENCRYPT_AFTER + ango);     //スクリーンへメッセージを出力する。
     System.out.println(CONSOLE_SUCCESS_END);
     
     System.out.println(chgAnGo.getEncrypt(ango, cstrKey));    } else {
     //反転用文字を取得できない場合、ログを出力する。
     (LOG_CONVERT_KEY_READ_ERROR);     //スクリーンへメッセージを出力する。
     System.out.println(CONSOLE_CONVERT_KEY_READ_ERROR);
    }
   }  } catch (IOException e) {
   //例外エラー発生場合、詳細エラーを出力する。
   logger.fatal(e.getMessage());  } finally {
   //処理完了ログ出力
   //スクリーンへメッセージを出力する
   System.out.println(CONSOLE_END);   //「文字列の暗号化完了」ログを出力する。
   (LOG_ENCRYPT_END);
  } }
 
 protected String setEncrypt(String password, String cstrKey) {  //int[] snNum = new int[password.length()];
  char[] snNum = new char[password.length()];
  String result = "";  //ビット反転操作を繰り回す
  for (int i = 0, j = 0; i < password.length(); i++, j++) {   //反転用文字列」<「暗号化したい文字列」場合、
   //反転用文字列を繰り回して利用する
   if (j == cstrKey.length())
    j = 0;   //暗号化したバイト=(暗号化したい文字)^(反転用文字)^(1/(当該文字の位置+1))
   //snNum[i] = password.charAt(i) ^ cstrKey.charAt(j) ^ (1 / (i + 1));
   snNum[i] = (char) (password.charAt(i) ^ cstrKey.charAt(j) ^ (1 / (i + 1)));
   result += (char) snNum[i];
  }  //暗号化した文字列を返す。
  return result;
 } 
 protected String getEncrypt(String password, String cstrKey) {  char[] snNum = new char[password.length()];
  String result = "";  //ビット反転操作を繰り回す
  for (int i = 0, j = 0; i < password.length(); i++, j++) {   //反転用文字列」<「復号化したい文字列」場合、
   //反転用文字列を繰り回して利用する   
   if (j == cstrKey.length())
    j = 0;
   char n = password.charAt(i);   //復号化したバイト=(復号化したい文字)^(反転用文字)^(1/(当該文字の位置+1))
   snNum[i] = (char) (n ^ cstrKey.charAt(j) ^ (1 / (i + 1)));
   result += snNum[i];
  }  //復号化した文字列を返す。
  return result;
 } 
 protected void writeToFile(String filePath, byte[] data) throws IOException {  FileOutputStream outfile = null;
  
  try {
   //ファイルパスより、出力ファイルを開く
   outfile = new FileOutputStream(filePath);   //データをファイルに出力する
   outfile.write(data, 0, data.length);   //ファイルを保存する
   outfile.flush();  } catch (IOException e) {
   //ファイル出力失敗場合、ログを出力する。
   (LOG_FILE_WRITE_ERROR);   //スクリーンへメッセージを出力する。
   System.out.println(CONSOLE_FILE_WRITE_ERROR);
   
   throw e;  } finally {
   //ファイルをクローズする
   if (outfile != null) {
    outfile.close();
   }
  } }
 
 protected String getConvertKey(String filePath) throws IOException {  String convertKey = null;
  BufferedReader br = null;  try {
   //ファイルパスより、入力ファイルを開く
   br = new BufferedReader(new FileReader(filePath));   //反転用文字を取得する
   convertKey = br.readLine();   //反転用文字を返す
   return convertKey;  } catch (IOException e) {
   //反転用文字を取得できない場合、ログを出力する。
   (LOG_CONVERT_KEY_READ_ERROR);   //スクリーンへメッセージを出力する。
   System.out.println(CONSOLE_CONVERT_KEY_READ_ERROR);   throw e;
  } finally {
   //ファイルをクローズする
   if (br != null) {
    br.close();
   }  }
 }
}