//------------------------------------------------------------------------------
// The contents of this file are subject to the nopCommerce Public License Version 1.0 ("License"); you may not use this file except in compliance with the License.
// You may obtain a copy of the License at  http://www.nopCommerce.com/License.aspx. 
// 
// Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. 
// See the License for the specific language governing rights and limitations under the License.
// 
// The Original Code is nopCommerce.
// The Initial Developer of the Original Code is NopSolutions.
// All Rights Reserved.
// 
// Contributor(s): _______. 
//------------------------------------------------------------------------------

using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Globalization;
using System.IO;
using System.Net;
using System.Text;
using System.Web;
using NopSolutions.NopCommerce.Common;
using NopSolutions.NopCommerce.Common.Configuration.Settings;
using NopSolutions.NopCommerce.Common.CustomerManagement;
using NopSolutions.NopCommerce.Common.Orders;
using NopSolutions.NopCommerce.Common.Payment;
using System.Xml;

namespace NopSolutions.NopCommerce.Payment.Methods.PSIGate
{
    /// <summary>
    /// PSI Gate payment processor
    /// </summary>
    public class PSIGatePaymentProcessor : IPaymentMethod
    {
        #region Fields
        private bool useSandBox = true;
        private string storeID;
        private string passphrase;
        #endregion

        #region Ctor
        /// <summary>
        /// Creates a new instance of the PSIGatePaymentProcessor class
        /// </summary>
        public PSIGatePaymentProcessor()
        {

        }
        #endregion

        #region Methods

        /// <summary>
        /// Initializes the Authorize.NET payment processor
        /// </summary>
        private void InitSettings()
        {
            useSandBox = SettingManager.GetSettingValueBoolean("PaymentMethod.PSIGate.UseSandbox");
            storeID = SettingManager.GetSettingValue("PaymentMethod.PSIGate.StoreID");
            passphrase = SettingManager.GetSettingValue("PaymentMethod.PSIGate.Passphrase");
        }

        /// <summary>
        /// Gets PSI Gate URL
        /// </summary>
        /// <returns></returns>
        private string GetPSIGateUrl()
        {
            return useSandBox ? "https://dev.psigate.com:7989/Messenger/XMLMessenger" :
                "https://secure.psigate.com:7934/Messenger/XMLMessenger";
        }

        /// <summary>
        /// Process payment
        /// </summary>
        /// <param name="paymentInfo">Payment info required for an order processing</param>
        /// <param name="customer">Customer</param>
        /// <param name="OrderGuid">Unique order identifier</param>
        /// <param name="processPaymentResult">Process payment result</param>
        public void ProcessPayment(PaymentInfo paymentInfo, Customer customer, Guid OrderGuid, ref ProcessPaymentResult processPaymentResult)
        {
            InitSettings();

            StringBuilder builder = new StringBuilder();
            builder.Append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
            builder.Append("<Order>");
            builder.Append("<StoreID>" + storeID + "</StoreID>");
            builder.Append("<Passphrase>" + passphrase + "</Passphrase>");
            builder.Append("<Bname>" + paymentInfo.BillingAddress.FirstName + "</Bname>");
            builder.Append("<Bcompany>" + paymentInfo.BillingAddress.Company + "</Bcompany>");
            builder.Append("<Baddress1>" + paymentInfo.BillingAddress.Address1 + "</Baddress1>");
            builder.Append("<Baddress2>" + paymentInfo.BillingAddress.Address2 + "</Baddress2>");
            builder.Append("<Bcity>" + paymentInfo.BillingAddress.City + "</Bcity>");
            if (paymentInfo.BillingAddress.StateProvince != null)
                builder.Append("<Bprovince>" + paymentInfo.BillingAddress.StateProvince.Abbreviation + "</Bprovince>");
            builder.Append("<Bpostalcode>" + paymentInfo.BillingAddress.ZipPostalCode + "</Bpostalcode>");
            if (paymentInfo.BillingAddress.Country != null)
                builder.Append("<Bcountry>" + paymentInfo.BillingAddress.Country.TwoLetterISOCode + "</Bcountry>");
            builder.Append("<Phone>" + paymentInfo.BillingAddress.PhoneNumber + "</Phone>");
            builder.Append("<Fax>" + paymentInfo.BillingAddress.FaxNumber + "</Fax>");
            builder.Append("<Email>" + paymentInfo.BillingAddress.Email + "</Email>");
            builder.Append("<Comments> </Comments>");
            builder.Append("<CustomerIP>" + HttpContext.Current.Request.UserHostAddress + "</CustomerIP>");
            builder.Append("<Subtotal>" + paymentInfo.OrderTotal.ToString("N2", new CultureInfo("en-US", false).NumberFormat) + "</Subtotal>");
            builder.Append("<PaymentType>CC</PaymentType>");
            builder.Append("<CardAction>0</CardAction>");
            builder.Append("<CardNumber>" + paymentInfo.CreditCardNumber + "</CardNumber>");
            string cardExpMonth = string.Empty;
            if (paymentInfo.CreditCardExpireMonth < 10)
            {
                cardExpMonth = "0" + paymentInfo.CreditCardExpireMonth.ToString();
            }
            else
            {
                cardExpMonth = paymentInfo.CreditCardExpireMonth.ToString();
            }
            builder.Append("<CardExpMonth>" + cardExpMonth + "</CardExpMonth>");
            builder.Append("<CardExpYear>" + paymentInfo.CreditCardExpireYear.ToString().Substring(2, 2) + "</CardExpYear>");
            builder.Append("<CardIDNumber>" + paymentInfo.CreditCardCVV2 + "</CardIDNumber>");
            builder.Append("</Order>");
            string orderInfo = builder.ToString();

            byte[] bytes = Encoding.ASCII.GetBytes(orderInfo);
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(GetPSIGateUrl());
            request.Method = "POST";
            request.ContentLength = bytes.Length;
            request.ContentType = "application/x-www-form-urlencoded";
            request.Accept = "application/xml; charset=UTF-8";
            request.KeepAlive = false;

            using (Stream str0 = request.GetRequestStream())
                str0.Write(bytes, 0, bytes.Length);

            string xmlResponse = string.Empty;
            try
            {
                using (HttpWebResponse resp1 = (HttpWebResponse)request.GetResponse())
                using (Stream str1 = resp1.GetResponseStream())
                using (StreamReader reader = new StreamReader(str1))
                    xmlResponse = reader.ReadToEnd();
            }
            catch (WebException exc)
            {
                if (exc.Response != null)
                    using (HttpWebResponse resp2 = (HttpWebResponse)exc.Response)
                    using (StreamReader rdr2 = new StreamReader(resp2.GetResponseStream()))
                        xmlResponse = rdr2.ReadToEnd();
            }
            StringReader input = new StringReader(xmlResponse);
            XmlTextReader rdr3 = new XmlTextReader(input);

        Label_1:
            while (rdr3.Read())
            {
                if (!(rdr3.IsStartElement() & (rdr3.Name == "Result")))
                {
                    continue;
                }
                while (rdr3.Read())
                {
                    if ((rdr3.NodeType == XmlNodeType.Element) && (rdr3.Name == "OrderID"))
                    {
                        processPaymentResult.AuthorizationTransactionID = rdr3.ReadElementString("OrderID");
                    }
                    else
                    {
                        if ((rdr3.NodeType == XmlNodeType.Element) && (rdr3.Name == "Approved"))
                        {
                            string approvedString = rdr3.ReadElementString("Approved");
                            if (approvedString == "APPROVED")
                                processPaymentResult.PaymentStatus = PaymentStatusEnum.Paid;

                            continue;
                        }
                        if ((rdr3.NodeType == XmlNodeType.Element) && (rdr3.Name == "ReturnCode"))
                        {
                            processPaymentResult.AuthorizationTransactionCode = rdr3.ReadElementString("ReturnCode");
                            continue;
                        }
                        if ((rdr3.NodeType == XmlNodeType.Element) && (rdr3.Name == "ErrMsg"))
                        {
                            processPaymentResult.Error = rdr3.ReadElementString("ErrMsg");
                            continue;
                        }
                        if ((rdr3.Name == "Result") & (rdr3.NodeType == XmlNodeType.EndElement))
                        {
                            goto Label_1;
                        }
                    }
                }
            }

            if (processPaymentResult.PaymentStatus != PaymentStatusEnum.Paid && string.IsNullOrEmpty(processPaymentResult.Error))
                processPaymentResult.Error = "Unknown PSI Gate error";
        }

        /// <summary>
        /// Post process payment (payment gateways that require redirecting)
        /// </summary>
        /// <param name="order">Order</param>
        /// <returns>The error status, or String.Empty if no errors</returns>
        public string PostProcessPayment(Order order)
        {
            return string.Empty;
        }

        /// <summary>
        /// Gets additional handling fee
        /// </summary>
        /// <returns>Additional handling fee</returns>
        public decimal GetAdditionalHandlingFee()
        {
            //override if payment method requires additional handling fee
            return decimal.Zero;
        }

        /// <summary>
        /// Captures payment (from admin panel)
        /// </summary>
        /// <param name="order">Order</param>
        /// <param name="processPaymentResult">Process payment result</param>
        public void Capture(Order order, ref ProcessPaymentResult processPaymentResult)
        {
            throw new NopException("Capture method not supported");
        }
        #endregion

        #region Properties
        /// <summary>
        /// Gets a value indicating whether capture is allowed from admin panel
        /// </summary>
        public bool CanCapture
        {
            get 
            { 
                return false;
            }
        }
        #endregion
    }
}
