//#-hidden-code
//
//  See LICENSE folder for this template’s licensing information.
//
//  Abstract:
//  The Swift file containing the source code edited by the user of this playground book.
//


import PlaygroundSupport
import AudioToolbox
import Foundation
import Network
let page = PlaygroundPage.current

private var connection : NWConnection!
page.needsIndefiniteExecution = true

func vprint(_ str: String) {
   if let proxy1 = page.liveView as? PlaygroundRemoteLiveViewProxy {
       let message: PlaygroundValue = .string(str)
       proxy1.send(message)
   }
}
func s_print(_ str: String) {
   if let proxy1 = page.liveView as? PlaygroundRemoteLiveViewProxy {
       let message: PlaygroundValue = .string("SEND " + str)
       proxy1.send(message)
   }
}
func r_print(_ str: String) {
   if let proxy1 = page.liveView as? PlaygroundRemoteLiveViewProxy {
       let message: PlaygroundValue = .string("RECIVE " + str)
       proxy1.send(message)
   }
}
func ip_print(_ str: String) {
   if let proxy1 = page.liveView as? PlaygroundRemoteLiveViewProxy {
       let message: PlaygroundValue = .string("IP " + str)
       proxy1.send(message)
   }
}
//#-end-hidden-code
/*: some text
##  💬チャットプログラム
このプログラムには、送信側と受信側両方の役割が記述されています。\
互いのデバイスが対等な関係で１対１で通信する仕組みはピアツーピアと呼ばれています。

 * Note:
 このプログラムは通信相手のIPアドレスを変更するだけで動作します。\
 メッセージの送信はライブビューの下にあるテキストボックスに文字を入力し、［送信］ボタンを押します。\
 相手のメッセージがライブビューの左側に、自分のメッセージは右側に表示されます。
 ---
 */
//#-code-completion(everything, hide)
//相手のIPアドレス
let host = "192.168.0.62"
let port = "50000"//ポート番号
//設定したIPアドレスを右画面に表示
ip_print(host)
// 受信側リスナー開始
let myQueue  = DispatchQueue.global()

do {
    let listener = try NWListener(using: .tcp, on: 50000)
    listener.newConnectionHandler = { (newConnection) in
        // Handle inbound connections
        var remoteHost = ""
        switch(newConnection.endpoint) {
            case .hostPort(let host, _):
                remoteHost = "\(host)"
                vprint("\(remoteHost)からの接続がありました")
            default:
                break
        }
        newConnection.start(queue: myQueue)
        nwRecv(connection: newConnection)
        if(connection == nil) {
            //相手からの接続に対してこちらからの送信用の接続をする。
            connection = connect(host: remoteHost, port: port)
        }
    }
    vprint("リスナーを開始しました。")
    listener.start(queue: myQueue)
}
catch {
    vprint("\(#function): \(error.localizedDescription)")
}

func nwRecv(connection: NWConnection) {
    //let semaphore = DispatchSemaphore(value: 0)
    // データ受信
    
    connection.receive(minimumIncompleteLength: 1,
                       maximumLength: 65536,
                       completion:{(data, context, flag, error) in
        if let error = error {
            vprint("\(#function): \(error.localizedDescription)")
            connection.cancel()
        } else {
            if let data = data, !data.isEmpty {
                // データをテキストに
                let text = String(data: data, encoding: .utf8)!
                r_print(text)
                let soundIdRing:SystemSoundID = 1003
                AudioServicesPlaySystemSound(soundIdRing)
                //次の受信待ちのための再帰呼び出し
                if(flag == false) {
                    nwRecv(connection: connection)
                }
                //semaphore.signal()
            }
            else {
                vprint("receiveMessage data nil")
            }
        }
    })
    // 受信完了待ち
    //semaphore.wait()
}

func nwSend(connection: NWConnection, message: String) {
    // 送信データ生成
    let data = message.data(using: .utf8)!
    //let semaphore = DispatchSemaphore(value: 0)
    // データ送信
    connection.send(content: data, completion: .contentProcessed { error in
        if let error = error {
            vprint("\(#function): \(error.localizedDescription)")
            connection.cancel()
        } else {
            let soundIdRing:SystemSoundID = 1004
            AudioServicesPlaySystemSound(soundIdRing)
            //semaphore.signal()
        }
    })
    // 送信完了待ち
    //semaphore.wait()
}

func disconnect(connection: NWConnection)
{
    // コネクション切断
    connection.cancel()
}


func connect(host: String, port: String) -> NWConnection
{
    let t_host = NWEndpoint.Host(host)
    let t_port = NWEndpoint.Port(port)
    let connection : NWConnection
    //let semaphore = DispatchSemaphore(value: 0)
    /* コネクションの初期化 */
    connection = NWConnection(host: t_host, port: t_port!, using: .tcp)
    
    /* コネクションのStateハンドラ設定 */
    connection.stateUpdateHandler = { (newState) in
        switch newState {
        case .ready:
            vprint("接続要求して送信準備ができました")
            //semaphore.signal()
        case .waiting(let error):
            vprint("\(#function): \(error.localizedDescription)")
        case .failed(let error):
            vprint("\(#function): \(error.localizedDescription)")
        case .setup: break
        case .cancelled: break
        case .preparing: break
        @unknown default:
            vprint("Illegal state")
        }
    }
    // コネクション開始
    let queue = DispatchQueue(label: "example")
    connection.start(queue:queue)
    
    // コネクション完了待ち
    //semaphore.wait()
    return connection
}


//ボタンの処理
let proxy2 = page.liveView as? PlaygroundRemoteLiveViewProxy
class pgListener: PlaygroundRemoteLiveViewProxyDelegate {
    func remoteLiveViewProxy(_ remoteLiveViewProxy: PlaygroundRemoteLiveViewProxy,received message: PlaygroundValue) {
        if case let .string(text) = message {
            var str = text
            if(str.hasPrefix("SEND")) {
                str.removeFirst(5)
                if(connection != nil) {
                    s_print(str)
                    nwSend(connection: connection, message: str)
                }
            } else if(str.hasPrefix("CONNECT")) {
                connection = connect(host: host, port: port)
            }
        }
    }
    func remoteLiveViewProxyConnectionClosed(_ remoteLiveViewProxy: PlaygroundRemoteLiveViewProxy) {
        //コネクション切断時の処理
    }
}

let pglistener = pgListener()
proxy2?.delegate = pglistener
