/*
 * WakeOnLan.java
 * 
 * Author: http://www.hrtdotnet.jp/
 * 
 */
package jp.hrtdotnet.java.net;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;


/**
 * WakeOnLANを実装したクラスです。
 * 
 * @version 2.0
 * @author hrtdotnet.jp
 * <br>http://www.hrtdotnet.jp/
 * @since 2.0
 */
public class WakeOnLan {

    /**
     * WakeOnLANのパケットを送信します。
     * 
     * @param ipAddress 送信IPアドレス
     * @param port 送信ポート
     * @param macAddress 対象MACアドレス(区切り文字は「:」もしくは「-」)
     * @throws UnknownHostException
     *          hostからIPアドレスを取得できない、もしくはIPv6のアドレスが指定された場合
     * @throws SecurityException
     *          hostへのソケット接続が許可されない場合
     * @throws SocketException
     *          ソケットを開くことができない場合
     * @throws IOException
     *          入出力エラーが発生した場合
     */
    public static void execute(String ipAddress, int port, String macAddress)
            throws UnknownHostException, SocketException, IOException {
        byte[] magicPacket = createMagicPacket(macAddress);
        executeMagicPacket(ipAddress, port, magicPacket);
    }

    /**
     * WakeOnLANのパケットを送信します。
     * 
     * @param ipAddress 送信IPアドレス
     * @param port 送信ポート
     * @param macAddress 対象MACアドレス
     * @param delim MACアドレスの区切り文字
     * @throws UnknownHostException
     *          hostからIPアドレスを取得できない、もしくはIPv6のアドレスが指定された場合
     * @throws SecurityException
     *          hostへのソケット接続が許可されない場合
     * @throws SocketException
     *          ソケットを開くことができない場合
     * @throws IOException
     *          入出力エラーが発生した場合
     */
    public static void execute(String ipAddress, int port, String macAddress, String delim)
            throws UnknownHostException, SocketException, IOException {
        byte[] magicPacket = createMagicPacket(macAddress, delim);
        executeMagicPacket(ipAddress, port, magicPacket);
    }  

    /**
     * WakeOnLANのパケットを送信します。
     * 
     * @param host 送信IPアドレス
     * @param port 送信ポート
     * @param macAddress 対象MACアドレス(オクテット単位)
     * @throws UnknownHostException
     *          hostからIPアドレスを取得できない、もしくはIPv6のアドレスが指定された場合
     * @throws SecurityException
     *          hostへのソケット接続が許可されない場合
     * @throws SocketException
     *          ソケットを開くことができない場合
     * @throws IOException
     *          入出力エラーが発生した場合
     */
    public static void execute(String host, int port, byte[] macAddress)
            throws UnknownHostException, SocketException, IOException {
        byte[] magicPacket = createMagicPacket(macAddress);
        executeMagicPacket(host, port, magicPacket);
    }    

    /**
     * WakeOnLANのパケットを送信します。
     * 
     * @param host 送信ホスト、IPアドレス
     * @param port 送信ポート
     * @param macAddress MagicPacket(オクテット単位)
     * @throws UnknownHostException
     *          hostからIPアドレスを取得できない、もしくはIPv6のアドレスが指定された場合
     * @throws SecurityException
     *          hostへのソケット接続が許可されない場合
     * @throws SocketException
     *          ソケットを開くことができない場合
     * @throws IOException
     *          入出力エラーが発生した場合
     */
    protected static void executeMagicPacket(String host, int port, byte[] magicPacket)
            throws UnknownHostException, SecurityException, SocketException, IOException {
        
        if (magicPacket == null) {
            throw new NullPointerException("MagicPacket is null.");
        }
        
        InetAddress inetAddress = null;
        DatagramPacket packet = null;
        DatagramSocket socket = null;
        try {
            inetAddress = InetAddress.getByName(host);
            packet = new DatagramPacket(magicPacket, magicPacket.length, inetAddress, port);
            socket = new DatagramSocket();
            socket.send(packet);
        } finally {
            if (socket != null && socket.isClosed() == false) {
                socket.close();                            
            }
        }
    }    

    
    /**
     * MagicPacketを生成します。
     * 
     * @param macAddress MACアドレス(区切り文字は「:」もしくは「-」)
     * @return MagicPacket
     */
    protected static byte[] createMagicPacket(String macAddress) {
        //MACアドレス分割
        byte[] macAddressBytes = NetUtil.octetMacAddress(macAddress);
        //MaggicPacket生成
        return createMagicPacket(macAddressBytes);
    }

    /**
     * MagicPacketを生成します。
     * 
     * @param macAddress MACアドレス
     * @param delim 区切り文字
     * @return MagicPacket
     */
    protected static byte[] createMagicPacket(String macAddress, String delim) {
        //MACアドレス分割
        byte[] macAddressBytes = NetUtil.octetMacAddress(macAddress, delim);
        //MaggicPacket生成
        return createMagicPacket(macAddressBytes);
    }

    /**
     * MagicPacketを生成します。
     * 
     * @param macAddress MACアドレス(オクテット単位)
     * @return MagicPacket
     */
    protected static byte[] createMagicPacket(byte[] macAddress) {

        int macAddressLength = NetUtil.MAC_ADDRESS_LENGTH;
        
        //不正チェック
        if (macAddress == null) {
            throw new NullPointerException("Mac-Address is null.");
        } else if (macAddress.length != macAddressLength) {
            throw new IllegalArgumentException("Mac-Address-length is invalid.");
        }

        //ブロードキャストアドレス+MACアドレス×16
        byte[] magicPacket = new byte[macAddressLength + macAddressLength * 16];
        
        //ブロードキャストアドレス
        for (int i = 0; i < macAddressLength; i++) {
            magicPacket[i] = (byte)0xFF;
        }

        //MACアドレス×16
        for (int i = 1; i <= 16; i++) {
            System.arraycopy(macAddress, 0, magicPacket, i * macAddressLength, macAddressLength);
        }

        return magicPacket;
    }
}
