using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Cosmos.UserFrame;
using Cosmos.ServiceProvider;
using Cosmos.Common;
using Cosmos.CommonManager;
using System.Data;

using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.Net;
using System.Net.Cache;
using System.IO;

using System.Collections.Specialized;
using System.Management;
using System.Security.Cryptography;


//************************************************************************************************
// 최초 작성 : 2018.02.22, 미주 펀치 APP 와 해피포인트 기능 ; girak.kim
// Class : Model > PunchhPosClient(PunchhClientResponse) <--> PunchhServiceCaller (PunchhAPIResponse)
// 변경 이력 : 
//------------------------------------------------------------------------------------------------
// - 수정 이유 :   
// - 수정 내용 :
// - 수정 적용된 내용 :   
// - 기 타 : 
//************************************************************************************************


namespace Cosmos.Service
{
    //##############################[결제, 포인트 관련 인터페이스 구현 클래스]#############################################//
    class PunchhHappyPoint_US : PaymentBase, IPaymentUs
    {

        private IOCBDirect m_OCBDirect = null;

        public PunchhHappyPoint_US()
        {
            m_OCBDirect = (IOCBDirect)sManager.InitServiceInstance(ServiceLists.AGENT_OLEDEVICE.DLL, ServiceLists.AGENT_OLEDEVICE.OCB_DIRECT);
        }

        #region SearchPayment 결제 조회
        /// <summary>
        /// 결제 조회
        /// </summary>
        /// <param name="aParam"></param>
        /// <param name="aRet"></param>
        /// <returns></returns>
        public string SearchPayment(string[] aParam, ref string[] aRet)
        {
            string sRet = UserCom.RST_ERR;
            return sRet;
        }

        #endregion


        #region GetPayment 결제 정보 획득
        /// <summary>
        /// 결제 정보 획득
        /// </summary>
        /// <param name="aParam"></param>
        /// <returns></returns>
        public object GetPayment(string[] aParam)
        {
            return m_cPayItem;
        }
        #endregion

         #region SetPayment 결제 등록
        /// <summary>
        /// 결제 등록(제휴 포인트 사용)
        /// </summary>
        /// <param name="aParam"></param>
        /// <returns></returns>
        public string SetPayment(string[] aParam)
        {
            string sRet = UserCom.RST_ERR;
            return sRet;
        }
        #endregion

        #region CancelPayment 결제 취소
        /// <summary>
        /// 결제 취소
        /// </summary>
        /// <param name="aParam"></param>
        /// <returns></returns>
        public string CancelPayment(string[] aParam)
        {
            string sRet = UserCom.RST_ERR;
            return sRet;
        }
        #endregion

        #region RefundPayment 결제 반품
        /// <summary>
        /// 결제 반품
        /// </summary>
        /// <param name="aParam"></param>
        /// <returns></returns>
        public string RefundPayment(string[] aParam)
        {
            string sRet = UserCom.RST_ERR;
            return sRet;
        }
        #endregion

        #region SetMenualPayment
        public string SetMenualPayment(string[] aParam)
        {
            string sRet = UserCom.RST_ERR;
            return sRet;
        }
        #endregion

    }
    //##############################[결제, 포인트 관련 인터페이스 구현 클래스 끝]#############################################//



    #region Punchh 상수 
    public class PunchhConst
    {
        public const string LOCATION_KEY = "9079df560fad175e29c5dff19c5acbda";//"273b9a4039481b5063e1d1001cdaef4e";
        public const string USER_LOOKUP_API_URL = "/users/search";//사용자 정보 조회
        public static string POSSIBLE_REDEMPTION_API_URL = "/redemptions/possible"; //
        public static string CREATE_OR_VOID_REDEMPTION_API_URL = "/redemptions";
        public static string CHECKIN_API_URL = "/checkins";    
    }
    #endregion

    #region 각종 요청/응답 모델   Strat (API 연동 시에 아래 모델 Json 형식으로 파싱함

        #region 공통 모델 (Balance, Reward, MenuItem) Strat
        public class Balance
        {
            [JsonProperty(PropertyName = "banked_rewards")]
            public double BankedRewards { get; set; }

            [JsonProperty(PropertyName = "membership_level")]
            public string MembershipLevel { get; set; }

            [JsonProperty(PropertyName = "membership_level_id")]
            public int? MembershipLevelId { get; set; }

            [JsonProperty(PropertyName = "membership_program_id")]
            public int? MembershipProgramId { get; set; }

            [JsonProperty(PropertyName = "net_balance")]
            public double? NetBalance { get; set; }

            [JsonProperty(PropertyName = "net_debits")]
            public double? NetDebits { get; set; }

            [JsonProperty(PropertyName = "pending_points")]
            public double? PendingPoints { get; set; }

            [JsonProperty(PropertyName = "points_balance")]
            public double? PointsBalance { get; set; }

            [JsonProperty(PropertyName = "signup_anniversary_day")]
            public string SignupAnniversaryDay { get; set; }

            [JsonProperty(PropertyName = "total_credits")]
            public double? TotalCredits { get; set; }

            [JsonProperty(PropertyName = "total_debits")]
            public double? TotalDebits { get; set; }

            [JsonProperty(PropertyName = "total_point_credits")]
            public double? TotalPointCredits { get; set; }

            [JsonProperty(PropertyName = "total_redeemable_visits")]
            public int? TotalRedeemableVisits { get; set; }

            [JsonProperty(PropertyName = "expired_membership_level")]
            public string ExpiredMembershipLevel { get; set; }

            [JsonProperty(PropertyName = "total_visits")]
            public int? TotalVisits { get; set; }

            [JsonProperty(PropertyName = "initial_visits")]
            public int? InitialVisits { get; set; }

            [JsonProperty(PropertyName = "unredeemed_cards")]
            public int? UnredeemedCards { get; set; }


        }

        public class Reward
        {
            [JsonProperty(PropertyName = "id")]
            public int Id { get; set; }

            [JsonProperty(PropertyName = "name")]
            public string Name { get; set; }

            [JsonProperty(PropertyName = "description")]
            public string Description { get; set; }

            [JsonProperty(PropertyName = "discount_amount")]
            public double DiscountAmount { get; set; }

            [JsonProperty(PropertyName = "points")]
            public double Points { get; set; }

            [JsonProperty(PropertyName = "image")]
            public string Image { get; set; }

            [JsonProperty(PropertyName = "created_at")]
            public DateTime? CreatedAt { get; set; }

            [JsonProperty(PropertyName = "end_date_tz")]
            public DateTime? EndDateTz { get; set; }

            [JsonProperty(PropertyName = "start_date_tz")]
            public DateTime? StartDateTz { get; set; }

            [JsonProperty(PropertyName = "updated_at")]
            public DateTime? UpdatedAt { get; set; }

            [JsonProperty(PropertyName = "redeemable_properties")]
            public string RedeemableProperties { get; set; }

            [JsonProperty(PropertyName = "status")]
            public string Status { get; set; }
        }

        public class MenuItem
        {
            [JsonProperty(PropertyName = "item_name")]
            public string ItemName { get; set; }

            [JsonProperty(PropertyName = "item_qty")]
            public int? ItemQty { get; set; }

            [JsonProperty(PropertyName = "item_amount")]
            public double? ItemAmount { get; set; }

            [JsonProperty(PropertyName = "menu_item_type")]
            public string MenuItemType { get; set; }

            [JsonProperty(PropertyName = "menu_item_id")]
            public int? MenuItemId { get; set; }

            [JsonProperty(PropertyName = "menu_family")]
            public string MenuFamily { get; set; }

            [JsonProperty(PropertyName = "menu_major_group")]
            public string MenuMajorGroup { get; set; }

            [JsonProperty(PropertyName = "serial_number")]
            public int? SerialNumber { get; set; }

        }

        #endregion 공통 모델 (Balance, Reward, MenuItem) End
    
        #region UserLookup Model 사용자 정보, 포인트 조회 요청 파라메타

        public class UserLookupRequest
        {
            [JsonProperty(PropertyName = "location_key")]
            public string LocationKey { get; set; }

            [JsonProperty(PropertyName = "phone")]
            public string Phone { get; set; }

            [JsonProperty(PropertyName = "user_as_qrcode")]
            public string UserAsQRcode { get; set; }

            [JsonProperty(PropertyName = "redemption_code")]
            public string RedemptionCode { get; set; }

            [JsonProperty(PropertyName = "card_number")]
            public string CardNumber { get; set; }
        }

        public class UserLookupResponse
        {
            [JsonProperty(PropertyName = "id")]
            public int Id { get; set; }

            [JsonProperty(PropertyName = "created_at")]
            public DateTime? CreatedAt { get; set; }

            [JsonProperty(PropertyName = "updated_at")]
            public DateTime? UpdatedAt { get; set; }

            [JsonProperty(PropertyName = "first_name")]
            public string FirstName { get; set; }

            [JsonProperty(PropertyName = "last_name")]
            public string LastName { get; set; }

            [JsonProperty(PropertyName = "phone")]
            public string Phone { get; set; }

            [JsonProperty(PropertyName = "fb_uid")]
            public string FbUid { get; set; }

            [JsonProperty(PropertyName = "birthday")]
            public DateTime? Birthday { get; set; }

            [JsonProperty(PropertyName = "gender")]
            public string Gender { get; set; }

            [JsonProperty(PropertyName = "city")]
            public string City { get; set; }

            [JsonProperty(PropertyName = "state")]
            public string State { get; set; }

            [JsonProperty(PropertyName = "selected_card_number")]
            public string SelectedCardNumber { get; set; }

            [JsonProperty(PropertyName = "selected_reward_id")]
            public int? selected_reward_id { get; set; }



            [JsonProperty(PropertyName = "balance")]
            public Balance Balance { get; set; }

            [JsonProperty(PropertyName = "rewards")]
            public List<Reward> Rewards { get; set; }


        }

        #endregion UserLookup Model End

        #region Redemption Possible Model (결제 가능 여부 잔액 조회)
        
        public class RedemptionRequest
        {
            [JsonProperty(PropertyName = "location_key")]
            public string LocationKey { get; set; }

            [JsonProperty(PropertyName = "discount_type")]
            public string DiscountType { get; set; }

            [JsonProperty(PropertyName = "reward_id")]
            public int? RewardId { get; set; }

            [JsonProperty(PropertyName = "redemption_code")]
            public string RedemptionCode { get; set; }

            [JsonProperty(PropertyName = "user_as_qrcode")]
            public string UserAsQRcode { get; set; }

            [JsonProperty(PropertyName = "punchh_key")]
            public string PunchhKey { get; set; }

            [JsonProperty(PropertyName = "transaction_no")]
            public int? TransactionNo { get; set; }

            [JsonProperty(PropertyName = "receipt_datetime")]
            public DateTime? ReceiptDateTime { get; set; }

            [JsonProperty(PropertyName = "menu_items")]
            public List<MenuItem> MenuItems { get; set; }

            [JsonProperty(PropertyName = "subtotal_amount")]
            public double? SubTotalAmount { get; set; }

            [JsonProperty(PropertyName = "receipt_amount")]
            public double? ReceiptAmount { get; set; }


            #region
            /*

        [JsonProperty(PropertyName = "email")]
        public string Email { get; set; }

        [JsonProperty(PropertyName = "cc_last4")]
        public int? CCLast4 { get; set; }

        [JsonProperty(PropertyName = "employee_id")]
        public int? EmployeeId { get; set; }

        [JsonProperty(PropertyName = "employee_name")]
        public string EmployeeName { get; set; }

        [JsonProperty(PropertyName = "external_uid")]
        public string ExternalUid { get; set; }

        [JsonProperty(PropertyName = "pos_version")]
        public string POSVersion { get; set; }

      
        [JsonProperty(PropertyName = "pos_type")]
        public string POSType { get; set; }


        [JsonProperty(PropertyName = "process")]
        public bool? Process { get; set; }
         * */
            #endregion

        }
    
        public class RedemptionResponse
        {
            [JsonProperty(PropertyName = "redemption_id")]
            public int? RedemptionId { get; set; }

            [JsonProperty(PropertyName = "redemption_code")]
            public string RedemptionCode { get; set; }

            [JsonProperty(PropertyName = "redemption_amount")]
            public double? RedemptionAmount { get; set; }

            [JsonProperty(PropertyName = "status")]
            public string Status { get; set; }

            [JsonProperty(PropertyName = "category")]
            public string Category { get; set; }

            [JsonProperty(PropertyName = "previous_amount")]
            public string PreviousAmount { get; set; }
        }
    
        #endregion Redemption Possible Model End

        #region User Checkin Model , 해피포인트 적립
        public class CheckinRequest
        {
            [JsonProperty(PropertyName = "location_key")]
            public string LocationKey { get; set; }

            [JsonProperty(PropertyName = "punchh_key")]
            public string PunchhKey { get; set; }

            [JsonProperty(PropertyName = "receipt_datetime")]
            public DateTime? ReceiptDateTime { get; set; }

            [JsonProperty(PropertyName = "payable")]
            public double? Payable { get; set; }

            [JsonProperty(PropertyName = "subtotal_amount")]
            public double SubTotalAmount { get; set; }

            [JsonProperty(PropertyName = "receipt_amount")]
            public double ReceiptAmount { get; set; }

            [JsonProperty(PropertyName = "pos_version")]
            public string POSVersion { get; set; }

            [JsonProperty(PropertyName = "employee_id")]
            public int EmployeeId { get; set; }

            [JsonProperty(PropertyName = "sequence_no")]
            public int SequenceNo { get; set; }

            [JsonProperty(PropertyName = "pos_type")]
            public string POSType { get; set; }

            [JsonProperty(PropertyName = "cc_last4")]
            public string CCLast4 { get; set; }

            [JsonProperty(PropertyName = "amount")]
            public string Amount { get; set; }

            [JsonProperty(PropertyName = "employee_name")]
            public string EmployeeName { get; set; }

            [JsonProperty(PropertyName = "transaction_no")]
            public int TransactionNo { get; set; }

            [JsonProperty(PropertyName = "revenue_code")]
            public string RevenueCode { get; set; }

            [JsonProperty(PropertyName = "menu_items")]
            public List<MenuItem> MenuItems { get; set; }

            [JsonProperty(PropertyName = "phone")]
            public string Phone { get; set; }

            [JsonProperty(PropertyName = "email")]
            public string Email { get; set; }

            [JsonProperty(PropertyName = "user_as_qrcode")]
            public string UserAsQRcode { get; set; }

            [JsonProperty(PropertyName = "card_number")]
            public string CardNumber { get; set; }

            [JsonProperty(PropertyName = "single_scan_token")]
            public string SingleScanToken { get; set; }

            [JsonProperty(PropertyName = "redemption_code")]
            public string RedemptionCode { get; set; }

            [JsonProperty(PropertyName = "user_token")]
            public string UserToken { get; set; }

        }

        public class CheckinResponse
        {
            [JsonProperty(PropertyName = "id")]
            public string Id { get; set; }

            [JsonProperty(PropertyName = "first_name")]
            public string FirstName { get; set; }

            [JsonProperty(PropertyName = "last_name")]
            public string LastName { get; set; }

            [JsonProperty(PropertyName = "email")]
            public string Email { get; set; }

            [JsonProperty(PropertyName = "phone")]
            public string Phone { get; set; }

            [JsonProperty(PropertyName = "fb_uid")]
            public string FbUid { get; set; }

            [JsonProperty(PropertyName = "user_as_qrcode")]
            public string UserAsQRcode { get; set; }

            [JsonProperty(PropertyName = "birthday")]
            public string Birthday { get; set; }

            [JsonProperty(PropertyName = "gender")]
            public string Gender { get; set; }

            [JsonProperty(PropertyName = "anniversary")]
            public DateTime? Anniversary { get; set; }

            [JsonProperty(PropertyName = "created_at")]
            public DateTime? CreatedAt { get; set; }

            [JsonProperty(PropertyName = "rewards")]
            public List<Reward> Rewards { get; set; }

            [JsonProperty(PropertyName = "balance")]
            public Balance Balance { get; set; }

        }

        #endregion User Checkin Model End

        #region Void Redemption Model  결제 취소
        public class VoidRedemptionRequest
        {
            [JsonProperty(PropertyName = "location_key")]
            public string LocationKey { get; set; }

            [JsonProperty(PropertyName = "phone")]
            public string Phone { get; set; }

            [JsonProperty(PropertyName = "user_as_qrcode")]
            public string UserAsQRcode { get; set; }

            [JsonProperty(PropertyName = "card_number")]
            public string CardNumber { get; set; }

            [JsonProperty(PropertyName = "redemption_id")]
            public int? RedemptionId { get; set; }

            [JsonProperty(PropertyName = "redemption_code")]
            public string RedemptionCode { get; set; }

            [JsonProperty(PropertyName = "store_number")]
            public string StoreNumber { get; set; }

            [JsonProperty(PropertyName = "transaction_no")]
            public string TransactionNo { get; set; }

        }

        #endregion  Void Redemption Model End

    #endregion End
        
    #region Punchh API 연동 시에 필요한 Library 정의
        public static class PunchhLib
    {
        /// <summary>
        /// request json key-value
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="requestModel"></param>
        /// <returns></returns>
        public static NameValueCollection GetNameValue<T>(T requestModel)
        {
            NameValueCollection nameValue = new NameValueCollection();

            var lstRequest = requestModel.GetType().GetProperties().ToList();
            foreach (var item in lstRequest)
            {
                var attributes = item.GetCustomAttributes(typeof(JsonPropertyAttribute), false);
                foreach (var att in attributes)
                {
                    var atts = (JsonPropertyAttribute)att;
                    var attName = atts.PropertyName;//json property 
                    var attValue = Convert.ToString(item.GetValue(requestModel, null));

                    nameValue.Add(attName, attValue);
                }
            }

            return nameValue;
        }


        /// <summary>
        /// http 통신 시 에 userAgent 정보
        /// </summary>
        /// <param name="locationKey"></param>
        /// <returns></returns>
        public static string GetUserAgent(string locationKey)
        {
            var windowsVersion = GetOSFriendlyName();
            var dotNetVersion = System.Environment.Version;

            System.Reflection.Assembly assembly = System.Reflection.Assembly.GetExecutingAssembly();
            System.Diagnostics.FileVersionInfo fvi = System.Diagnostics.FileVersionInfo.GetVersionInfo(assembly.Location);
            var productVerArr = fvi.ProductVersion.Split('.');

            string assemblyVersion = productVerArr[0] + "." + productVerArr[1] + "." + productVerArr[2];
            StringBuilder sb = new StringBuilder();
            sb.AppendFormat("pchposlib/{0};{1};{2};lkey:{3} ", assemblyVersion, dotNetVersion, windowsVersion, locationKey);

            return sb.ToString();
        }


        /// <summary>
        /// OS 정보
        /// </summary>
        /// <returns></returns>
        public static string GetOSFriendlyName()
        {
            string result = string.Empty;
            result = "Windows";
            /* 큰 의미 없어 보임.
            try
            {
                //System.Managemet 참조 추가 필요
                ManagementObjectSearcher searcher = new ManagementObjectSearcher("SELECT * FROM Win32_OperatingSystem");
                foreach (ManagementObject os in searcher.Get())
                {
                    result = os["Caption"].ToString();
                    break;
                }
                var resultArr = result.Split(' ');
                result = resultArr[1] + " " + resultArr[2];
            }
            catch (Exception ex)
            {

            }
             * */
            return result;
        }

        public static string GenerateXPchDigest(string message, string secretKey)
        {
            var encoding = new System.Text.ASCIIEncoding();
            byte[] keyBytes = encoding.GetBytes(secretKey);
            byte[] messageBytes = encoding.GetBytes(message);
            using (var hmacSHA256 = new HMACSHA1(keyBytes))
            {
                byte[] hashMessage = hmacSHA256.ComputeHash(messageBytes);
                return BitConverter.ToString(hashMessage).Replace("-", "").ToLower();
            }
        }

        public static T ToEnum<T>(this string value)
        {
            return (T)Enum.Parse(typeof(T), value, true);
        }

        public static int CreateTransactionNo()
        {
            TimeSpan ts = DateTime.UtcNow - new DateTime(2018, 1, 1, 0, 0, 0, 0);
            //double dTime = ts.TotalSeconds;
            int nTime = (int)Math.Truncate(ts.TotalSeconds);
            Random ran = new Random();
            return nTime + ran.Next(0, 9999);
        }

    }

    #endregion
    
    //##############################[POS Client 기능 구현]#############################################//

    #region Punchh POS Config

        public class PunchhConfiguration
        {
            /// <summary>
            /// The Location Key.
            /// </summary>
            public string LocationKey { get; set; }

            /// <summary>
            /// Punchh Environment.
            /// </summary>
            public PunchhEnvironment Environment { get; set; }

            /// <summary>
            /// Client Secret.
            /// </summary>
            public string ClientSecret { get; set; }

            /// <summary>
            /// Is Retry Enabled.
            /// </summary>
            public bool IsRetryCalls { get; set; }

            /// <summary>
            /// The Region.
            /// </summary>
            public string Region { get; set; }
        }

        public enum PunchhEnvironment
        {
            SANDBOX,
            PRODUCTION
        }

    #endregion

    #region POS Client 기능 구현

        public class PunchhPosClient
        {
            private string _locationKey { get; set; }
            private string _region { get; set; } /// The Base Url.

            //생성자
            public PunchhPosClient(PunchhConfiguration configuration)
            {
                //클라이언트 부분 설정
                _locationKey = configuration.LocationKey;
                _region = configuration.Region;

                SetAPIUrls(configuration);

                //서버 연동 부분 설정
                if (!string.IsNullOrEmpty(configuration.ClientSecret))
                {
                    PunchhServiceCaller.ClientSecret = configuration.ClientSecret;
                    PunchhServiceCaller.IsXPchEnabled = true;
                }

                PunchhServiceCaller.UserAgent = PunchhLib.GetUserAgent(configuration.LocationKey);
                PunchhServiceCaller.IsRetryCalls = configuration.IsRetryCalls;
            }

            public void SetAPIUrls(PunchhConfiguration configuration)
            {
                string baseUrl = string.Empty;
                string dataSinkUrl = string.Empty;

                switch (configuration.Region)
                {
                    case "asia":
                        baseUrl = "https://posasia.punchh.com";
                        dataSinkUrl = "https://islasia.punchh.com";
                        break;
                    case "au":
                        baseUrl = "https://posau.punchh.com";
                        dataSinkUrl = "https://islau.punchh.com";
                        break;
                    case "eu":
                        baseUrl = "https://poseu.punchh.com";
                        dataSinkUrl = "https://isleu.punchh.com";
                        break;
                    case "uk":
                        baseUrl = "https://posuk.punchh.com";
                        dataSinkUrl = "https://isluk.punchh.com";
                        break;
                    default:
                        baseUrl = "https://pos.punchh.com";
                        dataSinkUrl = "https://isl.punchh.com";
                        break;
                }

                if (configuration.Environment == PunchhEnvironment.SANDBOX)
                {
                    baseUrl = "https://sandbox.punchh.com";
                    dataSinkUrl = "https://isl-sandbox.punchh.com";
                }

                PunchhServiceCaller.BaseUrl = baseUrl + "/api/pos";
                PunchhServiceCaller.DataSinkBaseUrl = dataSinkUrl;
            }

            //사용자 조회
            public PunchhClientResponse<UserLookupResponse> UserLookup(UserLookupRequest requestModel)
            {
                try
                {
                    NameValueCollection headers = new NameValueCollection();
                    headers.Add("Authorization", string.Format("Token token={0}", _locationKey));
                    NameValueCollection content = PunchhLib.GetNameValue<UserLookupRequest>(requestModel);//요청값 json key, value 로 변경

                    PunchhAPIResponse apiResponse = PunchhServiceCaller.PunchhAPIRequest(PunchhConst.USER_LOOKUP_API_URL, headers, content);

                    if (apiResponse.HttpStatusCode == System.Net.HttpStatusCode.OK)
                    {
                        var resp = JsonConvert.DeserializeObject<UserLookupResponse>(apiResponse.Message);
                        return new PunchhClientResponse<UserLookupResponse>
                        {
                            Data = resp,
                            Message = apiResponse.Message,
                            Status = apiResponse.HttpStatusCode.ToString().ToEnum<PunchhResponseStatus>()
                        };

                    }
                    else
                    {
                        return new PunchhClientResponse<UserLookupResponse>
                        {
                            Data = null,
                            Message = apiResponse.Message,
                            Status = apiResponse.HttpStatusCode.ToString().ToEnum<PunchhResponseStatus>()
                        };
                    }
                }
                catch (WebException ex)
                {
                    return new PunchhClientResponse<UserLookupResponse>
                    {
                        Message = ex.Message,
                        Status = ex.Status.ToString().ToEnum<PunchhResponseStatus>()
                    };
                }
                catch (Exception ex)
                {
                    return new PunchhClientResponse<UserLookupResponse>
                    {
                        Data = null,
                        Message = "Exception.",
                        Status = PunchhResponseStatus.EXCEPTION
                    };
                }

                //return new PunchhResponse<UserLookupResponse>
                //{
                //    Data = null,
                //    Status = PunchhResponseStatus.EXCEPTION,
                //    Message = "Message"

                //};                
            }

            //해피 포인트 잔액, 결제 가능 금액 조회
            public PunchhClientResponse<RedemptionResponse> RedemptionPossible(RedemptionRequest requestModel)
            {
                try
                {
                    NameValueCollection headers = new NameValueCollection();
                    headers.Add("Authorization", string.Format("Token token={0}", _locationKey));

                    JObject jsonObj = JObject.FromObject(requestModel);
                    string content = jsonObj.ToString();

                    PunchhAPIResponse apiResponse = PunchhServiceCaller.PunchhAPIPostRequest(PunchhConst.POSSIBLE_REDEMPTION_API_URL, headers, content);

                    if (apiResponse.HttpStatusCode == System.Net.HttpStatusCode.OK)
                    {
                        var resp = JsonConvert.DeserializeObject<RedemptionResponse>(apiResponse.Message);

                        return new PunchhClientResponse<RedemptionResponse>
                        {
                            Data = resp,
                            Message = apiResponse.Message,
                            Status = apiResponse.HttpStatusCode.ToString().ToEnum<PunchhResponseStatus>()
                        };
                    }
                    else
                    {
                        return new PunchhClientResponse<RedemptionResponse>
                        {
                            Data = null,
                            Message = apiResponse.Message,
                            Status = apiResponse.HttpStatusCode.ToString().ToEnum<PunchhResponseStatus>()
                        };
                    }
                }
                catch (WebException ex)
                {
                    return new PunchhClientResponse<RedemptionResponse>
                    {
                        Message = ex.Message,
                        Status = ex.Status.ToString().ToEnum<PunchhResponseStatus>()
                    };
                }
                catch (Exception ex)
                {
                    return new PunchhClientResponse<RedemptionResponse>
                    {
                        Data = null,
                        Message = "Exception.",
                        Status = PunchhResponseStatus.EXCEPTION
                    };
                }

            }

            //해피 포인트 결제
            public PunchhClientResponse<RedemptionResponse> RedemptionCreate(RedemptionRequest requestModel)
            {
                try
                {
                    NameValueCollection headers = new NameValueCollection();
                    headers.Add("Authorization", string.Format("Token token={0}", _locationKey));

                    JObject jsonObj = JObject.FromObject(requestModel);
                    string content = jsonObj.ToString();

                    PunchhAPIResponse apiResponse = PunchhServiceCaller.PunchhAPIPostRequest(PunchhConst.CREATE_OR_VOID_REDEMPTION_API_URL, headers, content);

                    if (apiResponse.HttpStatusCode == System.Net.HttpStatusCode.OK)
                    {
                        var resp = JsonConvert.DeserializeObject<RedemptionResponse>(apiResponse.Message);

                        return new PunchhClientResponse<RedemptionResponse>
                        {
                            Data = resp,
                            Message = apiResponse.Message,
                            Status = apiResponse.HttpStatusCode.ToString().ToEnum<PunchhResponseStatus>()
                        };
                    }
                    else
                    {
                        return new PunchhClientResponse<RedemptionResponse>
                        {
                            Data = null,
                            Message = apiResponse.Message,
                            Status = apiResponse.HttpStatusCode.ToString().ToEnum<PunchhResponseStatus>()
                        };
                    }
                }
                catch (WebException ex)
                {
                    return new PunchhClientResponse<RedemptionResponse>
                    {
                        Message = ex.Message,
                        Status = ex.Status.ToString().ToEnum<PunchhResponseStatus>()
                    };
                }
                catch (Exception ex)
                {
                    return new PunchhClientResponse<RedemptionResponse>
                    {
                        Data = null,
                        Message = "Exception.",
                        Status = PunchhResponseStatus.EXCEPTION
                    };
                }
            }

            // 해피 포인트 적립    
            public PunchhClientResponse<CheckinResponse> UserCheckin(CheckinRequest requestModel)
            {

                try
                {
                    NameValueCollection headers = new NameValueCollection();
                    headers.Add("Authorization", string.Format("Token token={0}", _locationKey));

                    JObject jsonObj = JObject.FromObject(requestModel);
                    string content = jsonObj.ToString();

                    PunchhAPIResponse apiResponse = PunchhServiceCaller.PunchhAPIPostRequest(PunchhConst.CHECKIN_API_URL, headers, content);

                    if (apiResponse.HttpStatusCode == System.Net.HttpStatusCode.OK)
                    {
                        var resp = JsonConvert.DeserializeObject<CheckinResponse>(apiResponse.Message);

                        return new PunchhClientResponse<CheckinResponse>
                        {
                            Data = resp,
                            Message = apiResponse.Message,
                            Status = apiResponse.HttpStatusCode.ToString().ToEnum<PunchhResponseStatus>()
                        };
                    }
                    else
                    {
                        return new PunchhClientResponse<CheckinResponse>
                        {
                            Data = null,
                            Message = apiResponse.Message,
                            Status = apiResponse.HttpStatusCode.ToString().ToEnum<PunchhResponseStatus>()
                        };
                    }
                }
                catch (WebException ex)
                {
                    return new PunchhClientResponse<CheckinResponse>
                    {
                        Message = ex.Message,
                        Status = ex.Status.ToString().ToEnum<PunchhResponseStatus>()
                    };
                }
                catch (Exception ex)
                {
                    return new PunchhClientResponse<CheckinResponse>
                    {
                        Data = null,
                        Message = "Exception.",
                        Status = PunchhResponseStatus.EXCEPTION
                    };
                }
            }

            // 해피 포인트 결제 취소
            public PunchhClientResponse<string> VoidRedemption(VoidRedemptionRequest requestModel)
            {
                try
                {
                    NameValueCollection headers = new NameValueCollection();
                    headers.Add("Authorization", string.Format("Token token={0}", _locationKey));

                    NameValueCollection content = PunchhLib.GetNameValue<VoidRedemptionRequest>(requestModel);
                    PunchhAPIResponse apiResponse = PunchhServiceCaller.PunchhAPIDeleteRequest(PunchhConst.CREATE_OR_VOID_REDEMPTION_API_URL, headers, content);

                    return new PunchhClientResponse<string>
                    {
                        Data = string.Empty,
                        Message = apiResponse.Message,
                        Status = apiResponse.HttpStatusCode.ToString().ToEnum<PunchhResponseStatus>()
                    };
                }
                catch (WebException ex)
                {
                    return new PunchhClientResponse<string>
                    {
                        Message = ex.Message,
                        Status = ex.Status.ToString().ToEnum<PunchhResponseStatus>()
                    };
                }
                catch (Exception e)
                {
                    return new PunchhClientResponse<string>
                    {
                        Data = null,
                        Message = "Exception : ",
                        Status = PunchhResponseStatus.EXCEPTION
                    };
                }
            }

        }    

    #endregion

    #region Punchh Server API 에서 응답 후 POS Client 형식으로 변경
    //각 기능 응답 제네릭 클래스
    //API 에서 리턴 되어 오는 형식 그대로 에서 각 요청 메뉴 별로 형태 변경
    public class PunchhClientResponse<T>
    {
        public T Data { get; set; }
        public PunchhResponseStatus Status { get; set; }
        public string Message { get; set; }
    }


    public enum PunchhResponseStatus
    {
        OK = 200,
        CREATED = 201,
        ACCEPTED = 202,
        NotModified = 304,
        NOTFOUND = 404,
        BADREQUEST = 400,
        UNAUTHORIZED = 401,
        PRECONDITIONFAILED = 412,
        UNPROCESSABLE_ENTITY = 422,
        INTERNAL_SERVER_ERROR = 500,
        EXCEPTION = -1,
        PARAMETERS_NOT_SET = -2,
        TIMEOUT = -3,
        NAMERESOLUTIONFAILURE = -4,
        CONNECTFAILURE = -5
    }

    #endregion
    
    //##############################[POS Client 기능 구현 끝]##########################################//


    //###############################[Punchh Server API 호출]##########################################//
    #region Punchh Server API 호출

        public static class PunchhServiceCaller
    {
        #region
        //속성의 값들은 PunchhPosClient 에서 호출 시 설정함.
        public static string BaseUrl { get; set; }
        public static string DataSinkBaseUrl { get; set; }
        public static bool IsXPchEnabled { get; set; }
        public static string ClientSecret { get; set; }//현재는 사용 안 되는 함.
        public static string UserAgent { get; set; }
        public static bool IsRetryCalls { get; set; } // 서버 오류 발생시 재요청 여부        
        public static bool IsDataSinkCall { get; set; } // Is Receipt Datasink Call.

        #endregion


        #region Request API
        public static PunchhAPIResponse PunchhAPIRequest(string apiURL, NameValueCollection headers = null, NameValueCollection content = null)
        {
            int retryCall = 3;
            while (true)
            {
                try
                {
                    //요청 
                    #region
                    string queryString = string.Empty;
                    if (content != null)
                    {
                        queryString = string.Join("&", content.AllKeys.Where(key => !string.IsNullOrWhiteSpace(content[key])).Select(key => string.Format("{0}={1}", System.Web.HttpUtility.UrlEncode(key)
                                                                                                                                                                    , System.Web.HttpUtility.UrlEncode(content[key])
                                                                                                                                                           )));
                        queryString = "?" + queryString;
                    }

                    string punchhAPI_URL = BaseUrl + apiURL + queryString;
                    HttpWebRequest.DefaultCachePolicy = new HttpRequestCachePolicy(HttpRequestCacheLevel.Revalidate);

                    HttpWebRequest request = (HttpWebRequest)WebRequest.Create(punchhAPI_URL);
                    request.ContentType = "application/x-www-form-urlencoded";
                    request.Method = "GET";
                    request.Timeout = 30000;
                    request.UserAgent = PunchhLib.GetUserAgent(UserAgent);
                    request.CachePolicy = new HttpRequestCachePolicy(HttpRequestCacheLevel.Revalidate);

                    if (IsXPchEnabled && string.IsNullOrEmpty(ClientSecret))//ClientSecret 가 사용 되지 않음므로 해당 사항 없음
                    {
                        Uri uri = new Uri(punchhAPI_URL);
                        string sPath = uri.AbsolutePath;
                        string sBody = queryString;
                        string sValue = sPath + sBody;

                        string xPchDigest = PunchhLib.GenerateXPchDigest(sValue, ClientSecret);
                        headers.Add("x-pch-digest", xPchDigest);

                    }

                    if (headers != null)
                    {
                        foreach (var key in headers.AllKeys)
                        {
                            request.Headers[key] = headers[key];//웹통신에 header 추가
                        }
                    }
                    #endregion

                    //응답
                    #region
                    string responseMessage = string.Empty;
                    HttpWebResponse response = null;

                    response = (HttpWebResponse)request.GetResponse();

                    using (Stream stream = response.GetResponseStream())
                    {
                        using (StreamReader reader = new StreamReader(stream))
                        {
                            responseMessage = reader.ReadToEnd();
                        }
                    }
                    #endregion

                    retryCall = 0;

                    return new PunchhAPIResponse
                    {
                        Message = responseMessage,
                        HttpStatusCode = response.StatusCode,
                        Headers = response.Headers
                    };

                }
                catch (WebException ex)
                {
                    retryCall--;
                    if (ex.Response != null)
                    {
                        #region 재시도 후 결과 전송
                        var webResp = (HttpWebResponse)ex.Response;
                        var respMsg = new StreamReader(webResp.GetResponseStream()).ReadToEnd();

                        if (IsRetryCalls && retryCall > 0 && webResp.StatusCode == HttpStatusCode.InternalServerError)
                            continue;

                        retryCall = 0;

                        string filteredRespMsg = string.Empty;

                        try
                        {
                            filteredRespMsg = (respMsg.Contains("error")) ? JsonConvert.DeserializeObject<PunchhErrorResponse>(respMsg).Error.Message :
                                                                           JsonConvert.DeserializeObject<Newtonsoft.Json.Linq.JArray>(respMsg)[0].ToString();
                        }
                        catch (Exception e)
                        {
                            //e.Message;		                    
                        }

                        return new PunchhAPIResponse
                        {
                            Message = string.IsNullOrEmpty(filteredRespMsg) ? ex.Message : filteredRespMsg,
                            HttpStatusCode = webResp.StatusCode,
                            Headers = webResp.Headers
                        };
                        #endregion

                    }
                    else
                    {
                        if (IsRetryCalls && retryCall > 0)
                            continue;
                        else
                            throw ex;
                    }
                }
                catch (Exception ex)
                {
                    throw ex;
                }
            }//while end

        }

        public static PunchhAPIResponse PunchhAPIPostRequest(string apiURL, NameValueCollection headers, string content)
        {
            int retryCall = 3;
            while (true)
            {
                try
                {
                    #region
                    string punchhAPI_URL = BaseUrl + apiURL;
                    HttpWebRequest request = (HttpWebRequest)WebRequest.Create(punchhAPI_URL);
                    request.ContentType = "application/json";
                    request.Method = "POST";
                    request.Timeout = 30000;
                    request.UserAgent = PunchhLib.GetUserAgent(UserAgent);

                    if (IsXPchEnabled && !string.IsNullOrEmpty(ClientSecret) && !IsDataSinkCall)
                    {
                        Uri uri = new Uri(punchhAPI_URL);
                        string sPath = uri.AbsolutePath;
                        string sBody = content;
                        string sValue = sPath + sBody;

                        string xPchDigest = PunchhLib.GenerateXPchDigest(sValue, ClientSecret);
                        headers.Add("x-pch-digest", xPchDigest);
                    }
                    if (headers != null)
                    {
                        foreach (var key in headers.AllKeys)
                        {
                            request.Headers[key] = headers[key];
                        }
                    }


                    // Convert post data into bytes.
                    byte[] contentBytes = Encoding.ASCII.GetBytes(content);
                    // Set post data to request stream.
                    using (Stream requestStream = request.GetRequestStream())
                    {
                        requestStream.Write(contentBytes, 0, contentBytes.Length);
                        requestStream.Close();
                    }

                    string responseMessage = string.Empty;
                    HttpWebResponse response = null;

                    response = (HttpWebResponse)request.GetResponse();

                    using (Stream stream = response.GetResponseStream())
                    using (StreamReader reader = new StreamReader(stream))
                    {
                        responseMessage = reader.ReadToEnd();
                        // Logger.Log("Response Body: " + responseMessage, LogType.INFO);
                    }

                    retryCall = 0;

                    return new PunchhAPIResponse
                    {
                        Message = responseMessage,
                        HttpStatusCode = response.StatusCode,
                        Headers = response.Headers
                    };

                    #endregion
                }
                catch (WebException ex)
                {
                    #region
                    retryCall--;
                    if (ex.Response != null)
                    {
                        #region 재시도 후 결과 전송
                        var webResp = (HttpWebResponse)ex.Response;
                        var respMsg = new StreamReader(webResp.GetResponseStream()).ReadToEnd();

                        if (IsRetryCalls && retryCall > 0 && webResp.StatusCode == HttpStatusCode.InternalServerError)
                            continue;

                        retryCall = 0;

                        string filteredRespMsg = string.Empty;

                        try
                        {
                            filteredRespMsg = (respMsg.Contains("error")) ? JsonConvert.DeserializeObject<PunchhErrorResponse>(respMsg).Error.Message :
                                                                           JsonConvert.DeserializeObject<Newtonsoft.Json.Linq.JArray>(respMsg)[0].ToString();
                        }
                        catch (Exception e)
                        {
                            //e.Message;		                    
                        }

                        return new PunchhAPIResponse
                        {
                            Message = string.IsNullOrEmpty(filteredRespMsg) ? ex.Message : filteredRespMsg,
                            HttpStatusCode = webResp.StatusCode,
                            Headers = webResp.Headers
                        };
                        #endregion

                    }
                    else
                    {
                        if (IsRetryCalls && retryCall > 0)
                            continue;
                        else
                            throw ex;
                    }
                    #endregion


                }// try end
            }//while end
        }

        public static PunchhAPIResponse PunchhAPIDeleteRequest(string apiURL, NameValueCollection headers, NameValueCollection content)
        {
            int retryCall = 3;
            while (true)
            {
                try
                {
                    #region
                    string queryString = string.Empty;
                    if (content != null)
                    {
                        queryString = string.Join("&", content.AllKeys.Where(key => !string.IsNullOrWhiteSpace(content[key])).Select(key => string.Format("{0}={1}", System.Web.HttpUtility.UrlEncode(key, Encoding.UTF8), System.Web.HttpUtility.UrlEncode(content[key], Encoding.UTF8))));
                        queryString = "?" + queryString;
                    }

                    string punchhAPI_URL = BaseUrl + apiURL + queryString;
                    HttpWebRequest request = (HttpWebRequest)WebRequest.Create(punchhAPI_URL);
                    request.ContentType = "application/x-www-form-urlencoded";
                    request.Method = "DELETE";
                    request.Timeout = 30000;
                    request.UserAgent = PunchhLib.GetUserAgent(UserAgent);
                    if (IsXPchEnabled)
                    {
                        Uri uri = new Uri(punchhAPI_URL);
                        string sPath = uri.AbsolutePath;
                        string sBody = queryString;
                        string sValue = sPath + sBody;

                        string xPchDigest = PunchhLib.GenerateXPchDigest(sValue, ClientSecret);
                        headers.Add("x-pch-digest", xPchDigest);
                    }

                    if (headers != null)
                    {
                        foreach (var key in headers.AllKeys)
                        {
                            request.Headers[key] = headers[key];//웹통신에 header 추가
                        }
                    }
                    #endregion

                    //응답
                    #region
                    string responseMessage = string.Empty;
                    HttpWebResponse response = null;
                    response = (HttpWebResponse)request.GetResponse();
                    using (Stream stream = response.GetResponseStream())
                    using (StreamReader reader = new StreamReader(stream))
                    {
                        responseMessage = reader.ReadToEnd();
                    }
                    #endregion

                    retryCall = 0;

                    return new PunchhAPIResponse
                    {
                        Message = responseMessage,
                        HttpStatusCode = response.StatusCode,
                        Headers = response.Headers
                    };

                }//end try
                catch (WebException ex)
                {
                    retryCall--;
                    if (ex.Response != null)
                    {
                        #region 재시도 후 결과 전송
                        var webResp = (HttpWebResponse)ex.Response;
                        var respMsg = new StreamReader(webResp.GetResponseStream()).ReadToEnd();

                        if (IsRetryCalls && retryCall > 0 && webResp.StatusCode == HttpStatusCode.InternalServerError)
                            continue;

                        retryCall = 0;

                        string filteredRespMsg = string.Empty;

                        try
                        {
                            filteredRespMsg = (respMsg.Contains("error")) ? JsonConvert.DeserializeObject<PunchhErrorResponse>(respMsg).Error.Message :
                                                                           JsonConvert.DeserializeObject<Newtonsoft.Json.Linq.JArray>(respMsg)[0].ToString();
                        }
                        catch (Exception e)
                        {
                            //e.Message;		                    
                        }

                        return new PunchhAPIResponse
                        {
                            Message = string.IsNullOrEmpty(filteredRespMsg) ? ex.Message : filteredRespMsg,
                            HttpStatusCode = webResp.StatusCode,
                            Headers = webResp.Headers
                        };
                        #endregion
                    }
                    else
                    {
                        if (IsRetryCalls && retryCall > 0)
                            continue;
                        else
                            throw ex;
                    }
                }
                catch (Exception ex)
                {

                    throw ex;
                }//end catch
            }//while end
        }
        #endregion
    }

    #endregion

    #region Punchh Server API Response 형식 정의 (Punchh API 에서 아래 형식으로 리턴함)
        public class PunchhAPIResponse
        {
            public System.Net.WebHeaderCollection Headers { get; set; }
            public HttpStatusCode HttpStatusCode { get; set; }
            public string Message { get; set; }
        }

        public class PunchhErrorResponse
        {
            public Error Error { get; set; }
        }

        public class Error
        {
            public string Message { get; set; }
        }

    #endregion

    //###############################[Punchh Server API 호출 끝]#######################################//
}
