//haBull.java -- by tsaiwn@csie.nctu.edu.tw
//javac haBull.java CowBull.java
//java haBull
//then you can use "telnet hostName 3388" to connect this program
import java.io.*;
import java.net.*;
import java.util.*;  // Date is in this package
import java.lang.*;  // NOT necessary
class haBull  {  // should be same as the File name, 建議該用大寫開頭 haBull
    ServerSocket ssk;   //  用來等待連線
    Socket ccc;  // 有client連線進來可生出 Socket 小洞
    static final int DEFAULT_PORT = 3388;  // final 表示後面不可改它 
    static final int TIME_OUT = 300;  // 300 seconds
    int portNumber;   // Listening TCP port
    PrintStream cout = System.out;    // 取個與 C++ 同名的物件變數
    PrintStream cerr = System.err;
 static int getPortNumber(String s) {
    int kk=0;
    try {
       kk = Integer.parseInt(s);
    }catch(Exception e) { kk = 0; }
    return kk;
 } // getPortNumber if any, for example:   java haBull 5566
  public static void main(String[ ]  xxx) throws Exception {
                                          // ^^^ 宣稱若出問題會報告馬政府 !
        int pp=0;
        if(xxx.length > 0) pp =getPortNumber(xxx[0]);
        if(pp==0) pp = DEFAULT_PORT;  // 有打就用 user 打的, 否則用原來的
        new haBull(pp);  // 要求升出 haBull 物件並立刻執行 haBull( ) 建構子
  }// main
///
  haBull(int yy) {  // 這就是 class haBull 的 constructor 建構子
      portNumber = yy;
      cout.println("Server started ... at port "+ portNumber);
      try {
         ssk = new ServerSocket( portNumber );
         ssk.setSoTimeout(TIME_OUT * 1000);   //  time out = 300 sec ?
         while(38 == 38) { // true
            try {
               ccc = ssk.accept( );  // 等待連線後取得連線暫稱 ccc
            }catch(Exception e) {
               cerr.println(" .. " + TIME_OUT/60.0  // to minutes
                        + " minutes past "+new Date( ));
               continue; // to Listen again
            }
            Brother b = new Brother(ccc); // 請一個小弟, 並把連線交給小弟 
            b.start( );  // 叫小弟開始處理該連線
         }
      } catch(Exception e) {
         cerr.println("Cannot startup ChatServer at port "+ portNumber+"!");
         cerr.println("Maybe port "+portNumber+" has been used?\nByebye.");
      }
      cerr.println("...Server stop.");
  } // haBull( )
} // class haBull
////////
class Brother extends Thread {   // 表示這 class Brother 是一個 Thread 執行緒 
    Socket mySock;    // 用來記住"老大"傳來的資料, 方便這class內其他 function 用 
    Date now;
    String name="???"; // remember user name
    private String remoteAddr;   // 連著我的對方 IP address
    private BufferedReader in;
    private PrintStream out;
    private PrintStream cout = System.out;   // cout 只是簡寫而已
    Brother(Socket x) { 
        mySock = x; 
        now = new Date( );
        try {
            remoteAddr = mySock.getInetAddress().getHostAddress();
              // 然後取得InputStream並包成 BufferedReader 方便 readLine()
            in = new BufferedReader(
                    new InputStreamReader(mySock.getInputStream()) );
              // 再取得 OutputStream 並包成 PrintWriter
            out = new PrintStream(
                    (mySock.getOutputStream()), true );
            cout.println("Friend from " + remoteAddr + " at " + now);
        } catch(Exception e) {
          // say something
        }
    }
    public void run( ) {   // 規定 Thread 內一定要這樣寫才有用
        name = "???"; // 接著, 可以 與 連線者對話
        try { 
            out.print("Welcome to the BullCow Land.\n\r"); out.flush( );
            while(name.length( ) < 7) {  // 不可能短於 7 :-)
               out.print("Give me your 學號與姓名: ");
               name = in.readLine( );
               if(name.startsWith("   ") || name.length( )< 6 ) { 
                   name = "???";
                   out.print(" ! 不可亂打, ");
               }
            }
            out.println("歡迎 " + name + " 開始玩, 公牛是位置對數字也對!\n\r");
            cout.println("  " + name + " from " + remoteAddr + 
                  " play on " + new Date( ) +"..");
            log(1, name + " @" + remoteAddr+" " + new Date( ) ); // 1=start
            CowBull cb = new CowBull(in, out, this);  // this 指到我 !
               /// cb.play( );  // auto play in constructor 
            out.println("\n\rBye bye " + name + " at " + new Date( )); 
            log(0, name + " leaving " + new Date( ) ); // 0= leaving
            try { mySock.close( ); }catch(Exception e) {  } 
        }catch(Exception e) {  }
        cout.println("  " + name + " leaving at " +
                     new Date( ) +"..."+ remoteAddr);
    } // run( )
    void log(int k, String s) {
        FileWriter f;
        PrintWriter logFile;
        try {
           f = new FileWriter(logFileName, true);  // true to Append
           logFile = new PrintWriter(f);
        }catch(Exception e) { return; }  // No log if fail
        if(k == 0) logFile.append(" ..");
        logFile.append(s +"\r\n");
        logFile.close( );
        return;
    } // log
    String logFileName = "bullLog.txt";
} // class Brother
