using System;
using System.Text;
using System.Threading;
using System.ServiceModel;

//サンプルコードでは、個々のクラスがどの名前空間・アセンブリに
//属しているのか表現するため、あえてusingは使用しない。
//using UnifiedPOS.Scanner;

namespace Console_ScannerConsumerBiDirectionSample
{
    /// <summary>
    /// Scannerの使用方法を説明する
    /// </summary>
    class ScannerTest
    {
        // このアプリケーションのためのコンシューマIDを定義する
        string consumerID = Guid.NewGuid().ToString();

        UnifiedPOS.Scanner.V1_4.Scanner scannerChannel;

        /// <summary>
        /// Scannerの使用方法を説明する
        /// </summary>
        public void Test()
        {
            // WS-POSを利用するための準備
            // チャネルの作成、イベント受信の準備、
            // WS-POSセッションの開始、KeepAliveの開始

            ScannerEventService.SetDataEventHandler(new ScannerEventService.DataEventHandler(ScannerEvents_OnDataEvent));

            // 自己ホストとして、イベント受信クラスを実行する。
            // イベントはScannerEventServiceクラスで受信される。
            var scannerEventServiceHost = new ServiceHost(typeof(ScannerEventService));
            scannerEventServiceHost.Open();

            // WCFを使用してWS-POSサービスプロバイダに接続するための準備
            var channelFactory = new ChannelFactory<UnifiedPOS.Scanner.V1_4.Scanner>("ScannerSimulator");

            // Scanner WS-POSサービスプロバイダへのチャネル
            scannerChannel = channelFactory.CreateChannel();

            // WS-POS プロバイダのセッションを開始する。双方向通信のため、Endpointにイベント受信用エンドポイントを渡す
            // （ロングポーリングでは、nullを渡す）
            var eventEndpoint = scannerEventServiceHost.BaseAddresses[0];
            scannerChannel.OpenSession(consumerID, eventEndpoint.AbsoluteUri);

            // Keep AliveのためにWS-POSサービスプロバイダのセッションタイムアウト時間(秒)を取得
            var providerSessionTimoutSec = scannerChannel.GetProviderSessionTimeout(consumerID);

            // Keep Aliveを開始する。タイマーのPeriodには、サービスプロバイダセッションタイムアウトの半分の指定を推奨
            var keepAliveTimer = new Timer(new TimerCallback(KeepAliveTimer_TimerCallback), scannerChannel, 0, providerSessionTimoutSec * 1000 / 2);

            // これ以降は、OPOS / POS for .NET でのシーケンスと
            // 非常に似ている

            Console.WriteLine("Press ENTER to OpenDevice, Claim, Enable, DataEventEnable.");
            Console.ReadLine();

            // WS-POS プロバイダのデバイスをOpen, Claim, Enable
            scannerChannel.OpenDevice(consumerID);
            scannerChannel.Claim(consumerID, 1000);
            scannerChannel.SetDeviceEnabled(consumerID, true);
            scannerChannel.SetDataEventEnabled(consumerID, true);
            //scannerChannel.SetEventRequestProperties(consumerID, new UnifiedPOS.Scanner.V1_4.PropertyNameList() { "ScanData", "ScanDataLabel" });

            var encriptedCLaimedConsumerID = scannerChannel.GetEncryptedClaimedConsumerID();

            Console.WriteLine("Press ENTER to Disable, Release, Close");
            Console.ReadLine();

            // UPOSの仕様では、デバイスをCloseすると、Release, Disableも行われる。
            // ネットワークトラフィックを軽減するため、連続して呼び出す場合は
            // CloseDeviceのみ呼び出すことを推奨
            // scannerChannel.SetDeviceEnabled(consumerID, false);
            // scannerChannel.Release(consumerID);
            scannerChannel.CloseDevice(consumerID);


            Console.WriteLine("Press ENTER to OpenDevice, Claim, Enable, DataEventEnable.");
            Console.ReadLine();

            // WS-POS プロバイダのデバイスをOpen, Claim, Enable
            scannerChannel.OpenDevice(consumerID);
            scannerChannel.Claim(consumerID, 1000);
            scannerChannel.SetDeviceEnabled(consumerID, true);
            scannerChannel.SetDataEventEnabled(consumerID, true);
            scannerChannel.SetEventRequestProperties(consumerID, new UnifiedPOS.Scanner.V1_4.PropertyNameList() {"ScanData", "ScanDataLabel"});

            Console.WriteLine("Press ENTER to Disable, Release, Close");
            Console.ReadLine();

            // UPOSの仕様では、デバイスをCloseすると、Release, Disableも行われる。
            // ネットワークトラフィックを軽減するため、連続して呼び出す場合は
            // CloseDeviceのみ呼び出すことを推奨
            // scannerChannel.SetDeviceEnabled(consumerID, false);
            // scannerChannel.Release(consumerID);
            scannerChannel.CloseDevice(consumerID);

            // WS-POSの後始末
            // KeepAliveの停止、
            // WS-POSセッションの終了、イベント受信クラスの終了
            keepAliveTimer.Dispose();
            scannerChannel.CloseSession(consumerID);
            scannerEventServiceHost.Close();

        }

        /// <summary>
        /// WS-POS KeepAliveを行う
        /// </summary>
        void KeepAliveTimer_TimerCallback(object o)
        {
            var posprinterChannel = o as UnifiedPOS.Scanner.V1_4.Scanner;

            try
            {
                posprinterChannel.KeepAlive(consumerID);
            }
            catch (TimeoutException)
            {
                // サービスプロバイダとの通信がオフラインとなった場合は、
                // 再度 OpenSessionからやりなおす。
                // ただし、サービスプロバイダ側がセッションタイムアウトする前は、
                // OpenSessionできない。
                // このサンプルプログラムでは、何も処理をしていない

                Console.WriteLine("WS-POS KeepAlive Timeout happend.");
            }
        }

        /// <summary>
        /// Scannerのデータイベントのハンドリング
        /// </summary>
        void ScannerEvents_OnDataEvent(string ConsumerID, string Source, int EventID, DateTime TimeStamp, int Status)
        {
            var scanDataBytes = scannerChannel.GetScanData(ConsumerID);
            Console.WriteLine("ScanData = {0}", Encoding.ASCII.GetString(scanDataBytes));
            scannerChannel.SetDataEventEnabled(ConsumerID, true);
        }
    }
}
