/******************************************************************************
 * 文件名称: CardReader.cs
 * 文件说明: 读卡的封装，包括社保卡，身份证，电子凭证等等
 * 当前版本: V1.0
 * 创建日期: 2022-06-20
 *
 * * 2020-06-20: 增加 CardReader 类
 * *****  2020-06-20: 增加 CardReader 方法，获取URL地址,USER信息，地方版SSCard.dll使用
 * *****  2020-06-20: 增加 CardReader 方法重载，国家版电子凭证使用
 * *****  2020-06-20: 增加 Init 方法，SSCard环境初始化
 * *****  2020-06-20: 增加 ReadCardBas 方法，读社保卡
 * *****  2020-06-20: 增加 VerifyPIN 方法，验证密码
 * *****  2020-06-20: 增加 ChangePIN 方法，修改密码
 * *****  2020-06-20: 增加 ReadSFZ 方法，读身份证
 * *****  2020-06-20: 增加 GetQRBase 方法，读二维码
 * *****  2020-06-20: 增加 NationEcTrans 方法，读电子凭证（国家版）


******************************************************************************/
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using PTMedicalInsurance.Common.SMLib;
using PTMedicalInsurance.Entity;
using PTMedicalInsurance.Helper;
using PTMedicalInsurance.Variables;
using System;
using System.Runtime.InteropServices;
using System.Security.Policy;
using System.Text;

namespace PTMedicalInsurance.CardReaders
{
    class CardReader
    {
        [DllImport("SSCard.dll", EntryPoint = "Init", CharSet = CharSet.Ansi, ExactSpelling = false, CallingConvention = CallingConvention.StdCall)]
        static extern int Init(IntPtr pURL, IntPtr pUser);

        //[DllImport(DllPath, EntryPoint = "Init", CharSet = CharSet.Ansi, ExactSpelling = false, CallingConvention = CallingConvention.StdCall)]
        //static extern int Init(StringBuilder pInitInfo, StringBuilder pErrMsg);

        [DllImport("SSCard.dll", EntryPoint = "ReadCardBas", CharSet = CharSet.Ansi, ExactSpelling = false, CallingConvention = CallingConvention.StdCall)]
        private static extern int ReadCardBas(IntPtr pOutBuff, int nOutBuffLen, IntPtr pSignBuff, int nSignBuffLen);


        const string DllPath = @"SSCard.dll";
        private static bool initilaized = false;

  

        private static string URL;
        private static string User;

        public CardReader()
        {
        }

        public CardReader(string URL, string User)
        {

        }

        public static int Init(string _URL, string _User)
        {
            string errMsg = "";
            int result = -1;
            if (string.IsNullOrEmpty(_URL))
            {
                errMsg = "读卡地址未维护，请联系管理员！";
                return result;
            }
            URL = _URL;
            User = _User;
            if (initilaized)
            {
                Global.writeLog("Init", "检测到已初始化", "");
                return 0;
            }
            
            //Global.writeLog_debug(string.Format("读卡初始化:{0},{1}", URL, User));

            // 将托管字符串转换为 ANSI 编码的字节数组
            byte[] urlBytes = Encoding.ASCII.GetBytes(URL + '\0'); // 添加终止符
            byte[] userBytes = Encoding.ASCII.GetBytes(User + '\0'); // 添加终止符
            Global.writeLog("Init", URL, User);
            // 分配全局非托管内存并复制字符串数据
            IntPtr pUrl = Marshal.AllocHGlobal(urlBytes.Length);
            IntPtr pUser = Marshal.AllocHGlobal(userBytes.Length);
            try
            {
                // 将托管字节数组复制到非托管内存
                Marshal.Copy(urlBytes, 0, pUrl, urlBytes.Length);
                Marshal.Copy(userBytes, 0, pUser, userBytes.Length);

                result = Init(pUrl, pUser);
                if (result == 0)
                {
                    initilaized = true;
                }
                
                return result;
            }
            catch (Exception ex)
            {
                errMsg = ex.Message;
                return result;

            }
            finally
            {
                // 显式释放全局非托管内存
                if (pUrl != IntPtr.Zero) Marshal.FreeHGlobal(pUrl);
                if (pUser != IntPtr.Zero) Marshal.FreeHGlobal(pUser);
                
                Global.writeLog($"Init:{_URL};{_User}:", result.ToString(), errMsg);
            }
        }

        //public  int Init()
        //{
        //    string errMsg = "";
        //    int result = -1;
        //    try
        //    {
        //        StringBuilder sbInitInfo = new StringBuilder();

        //        StringBuilder sbUSER = new StringBuilder(4096);

        //        result = Init(sbInitInfo, sbUSER);
        //        return result;
        //    }
        //    catch (Exception ex)
        //    {
        //        errMsg = ex.Message;
        //        return result;

        //    }
        //    finally
        //    {
        //        Global.writeLog("初始化", "", result.ToString() + ":" + errMsg);
        //    }
        //}

        /// <summary>
        /// 读社保卡
        /// </summary>
        /// <param name="basInfo"></param>
        /// <returns></returns>
        public int ReadCardBas(out string basInfo)
        {
            basInfo = "";
            int rtn = -1;
            // 分配非托管内存
            //IntPtr pOutBuff = Marshal.AllocCoTaskMem(1024);
            //IntPtr pSignBuff = Marshal.AllocCoTaskMem(8192);

            // 分配全局非托管内存
            IntPtr pOutBuff = Marshal.AllocHGlobal(1024);
            IntPtr pSignBuff = Marshal.AllocHGlobal(8192);
            try
            {
                rtn = ReadCardBas(pOutBuff, 1024, pSignBuff, 8192);
                if (rtn == 0)
                {
                    // 解析返回读卡信息
                    string outString = Marshal.PtrToStringAnsi(pOutBuff);
                    string signString = Marshal.PtrToStringAnsi(pSignBuff);
                    basInfo = outString;
                }
                else
                {
                    string outString = Marshal.PtrToStringAnsi(pOutBuff);
                    basInfo = outString;
                }
                return rtn;
            }
            catch (Exception ex)
            {
                basInfo = "ReadCardBas异常:" + ex.Message;
                return rtn;
            }
            finally
            {
                Global.writeLog("读社保卡", "", basInfo);
                //// 显式释放非托管内存
                //Marshal.FreeCoTaskMem(pOutBuff);
                //Marshal.FreeCoTaskMem(pSignBuff);

                // 显式释放非托管内存
                Marshal.FreeHGlobal(pOutBuff);
                Marshal.FreeHGlobal(pSignBuff);
            }
        }

        public int ReadCardBas(out string basInfo, out string signInfo)
        {
            basInfo = ""; signInfo = "";
            int rtn = -1;
            // 分配全局非托管内存
            IntPtr pOutBuff = Marshal.AllocHGlobal(1024);
            IntPtr pSignBuff = Marshal.AllocHGlobal(8192);
            try
            {
                rtn = ReadCardBas(pOutBuff, 1024, pSignBuff, 8192);
                if (rtn == 0)
                {
                    // 解析返回读卡信息
                    string outString = Marshal.PtrToStringAnsi(pOutBuff);
                    string signString = Marshal.PtrToStringAnsi(pSignBuff);
                    basInfo = outString;
                    signInfo = signString;
                }
                else
                {
                    string outString = Marshal.PtrToStringAnsi(pOutBuff);
                    string signString = Marshal.PtrToStringAnsi(pSignBuff);
                    basInfo = outString;
                    signInfo = signString;
                }
                return rtn;
            }
            catch (Exception ex)
            {
                basInfo = "ReadCardBas异常:" + ex.Message;
                return rtn;
            }
            finally
            {
                // 显式释放非托管内存
                Marshal.FreeHGlobal(pOutBuff);
                Marshal.FreeHGlobal(pSignBuff);
                Global.writeLog("ReadCardBas", basInfo, signInfo);
            }
        }

        

        /// <summary>
        /// 读身份证
        /// </summary>
        /// <param name="OutBuff"></param>
        /// <returns></returns>
        public int ReadSFZ(out string OutBuff)
        {
            OutBuff = "";
            try
            {
                SFZReader reader = new SFZReader();
                return reader.ReadSFZ(out OutBuff);
            }
            catch (Exception ex)
            {
                OutBuff = "ReadSFZ 异常:" + ex.Message;
                return -1;
            }
        }

        /// <summary>
        /// 6101
        /// </summary>
        /// <param name="OutBuff"></param>
        /// <returns></returns>
        public int ReadEC(out string OutBuff)
        {
            OutBuff = "";
            try
            {
                ECTokenData eCToken = new ECTokenData();
                eCToken.orgId = Global.inf.hospitalNO;
                eCToken.operatorId = Global.user.ID;
                eCToken.operatorName = Global.user.name;
                eCToken.officeId = Global.user.officeID;
                eCToken.officeName = Global.user.officeName;
                eCToken.businessType = Global.pat.card.ecBizType;
                //eCToken.outBizNo = bizNo;
                //eCToken.extData = "";

                string tmp = JsonConvert.SerializeObject(eCToken);
                var inData = new { InData = JObject.Parse(tmp) };
                JObject joInData = new JObject(
                        new JProperty("InData", JObject.Parse(tmp))
                    );

                ECReader reader = new ECReader();
                return reader.Read(joInData,out OutBuff);
            }
            catch (Exception ex)
            {
                OutBuff = "ReadEC 异常:" + ex.Message;
                return -1;
            }
        }

        /// <summary>
        /// 6101
        /// </summary>
        /// <param name="OutBuff"></param>
        /// <returns></returns>
        public int ECQuery(out string OutBuff)
        {
            OutBuff = "";
            try
            {
                EcQueryData ecqueryData = new EcQueryData();
                ecqueryData.orgId = Global.inf.hospitalNO;
                ecqueryData.operatorId = Global.user.ID;
                ecqueryData.operatorName = Global.user.name;
                ecqueryData.officeId = Global.user.officeID;
                ecqueryData.officeName = Global.user.officeName;
                ecqueryData.businessType = Global.pat.card.ecBizType;
                //eCToken.outBizNo = bizNo;
                //eCToken.extData = "";
                NationECInput nationECInput = new NationECInput();
                nationECInput.data = ecqueryData;
                nationECInput.orgId = Global.inf.hospitalNO;
                nationECInput.transType = "ec.query";
                nationECInput.extra = "";


                NationECCodeAPI nationECCodeAPI = new NationECCodeAPI(Global.inf.ecURL);
                return nationECCodeAPI.NationEcTrans(nationECInput, out OutBuff);
            }
            catch (Exception ex)
            {
                OutBuff = "ECQuery 异常:" + ex.Message;
                return -1;
            }
        }

        /// <summary>
        /// 人脸识别
        /// </summary>
        /// <param name="OutBuff"></param>
        /// <returns></returns>
        public int FaceRecognition(out string OutBuff)
        {
            Random rd = new Random();
            int iNum = rd.Next();
            Global.pat.card.outBizNo = DateTime.Now.ToString("YYYYMMDDHHNNSS") + Global.inf.hospitalNO + iNum.ToString().Substring(0, 5);


            EcAuthData ecAuthData = new EcAuthData();
            ecAuthData.orgId = Global.inf.hospitalNO;
            ecAuthData.operatorId = Global.user.ID;
            ecAuthData.operatorName = Global.user.name;
            ecAuthData.officeId = Global.user.officeID;
            ecAuthData.officeName = Global.user.officeName;
            ecAuthData.businessType = Global.pat.card.ecBizType;
            ecAuthData.outBizNo = DateTime.Now.ToString("YYYYMMDDHHNNSS") + Global.inf.hospitalNO + iNum.ToString().Substring(0, 5);

            NationECInput nationECInput = new NationECInput();
            nationECInput.data = ecAuthData;
            nationECInput.orgId = Global.inf.hospitalNO;
            nationECInput.transType = "cn.nhsa.ec.auth";
            nationECInput.extra = "";
            //调用刷脸认证接口cn.nhsa.ec.auth返回授权信息authNo
            NationECCodeAPI nationECCodeAPI = new NationECCodeAPI(Global.inf.ecURL);
            nationECCodeAPI.NationEcTrans(nationECInput, out OutBuff);
            if (JsonHelper.getDestValue(JObject.Parse(OutBuff), "code") != "0")
            {
                OutBuff = $"调用刷脸认证接口{nationECInput.transType}失败：" + OutBuff;
                return -1;
            }
            else
            {

                //成功返回授权信息
                string auth = JsonHelper.getDestValue(JObject.Parse(OutBuff), "data.authNo");

                AuthCheckData authCheckData = new AuthCheckData();
                authCheckData.orgId = Global.inf.hospitalNO;
                authCheckData.operatorId = Global.user.ID;
                authCheckData.operatorName = Global.user.name;
                authCheckData.officeId = Global.user.officeID;
                authCheckData.officeName = Global.user.officeName;
                authCheckData.businessType = Global.pat.card.ecBizType ?? "01102";
                Global.pat.card.outBizNo = DateTime.Now.ToString("YYYYMMDDHHNNSS") + Global.inf.hospitalNO + iNum.ToString().Substring(0, 5);
                authCheckData.outBizNo = Global.pat.card.outBizNo;
                authCheckData.authNo = auth;

                nationECInput = new NationECInput();
                nationECInput.data = authCheckData;
                nationECInput.orgId = Global.inf.hospitalNO;
                nationECInput.transType = "cn.nhsa.auth.check";

                //2.调用刷脸认证接口cn.nhsa.ec.auth返回授权信息authNo
                nationECCodeAPI.NationEcTrans(nationECInput, out OutBuff);
                if (JsonHelper.getDestValue(JObject.Parse(OutBuff), "code") != "0")
                {
                    OutBuff = $"调用刷脸认证接口{nationECInput.transType}失败：" + OutBuff;
                    return -1;
                }
                else
                {
                    Global.pat.ecToken = JsonHelper.getDestValue(JObject.Parse(OutBuff), "data.ecToken");
                    Global.pat.name = JsonHelper.getDestValue(JObject.Parse(OutBuff), "data.userName");
                    Global.pat.IDType = JsonHelper.getDestValue(JObject.Parse(OutBuff), "data.idType");
                    Global.pat.IDNO = JsonHelper.getDestValue(JObject.Parse(OutBuff), "data.idNo");
                    Global.pat.insuplc_admdvs = JsonHelper.getDestValue(JObject.Parse(OutBuff), "data.insuOrg");
                    Global.pat.mdtrtcertNO = Global.pat.ecToken;
                    Global.pat.certType = Global.pat.IDType;
                    Global.pat.certNO = Global.pat.IDNO;
                    return 0;
                }
            }
        }

        /// <summary>
        /// 结算完成后，传入人脸识别时的outBizNo，以及结算信息
        /// </summary>
        /// <param name="sLSH"></param>
        /// <param name="joIn"></param>
        /// <returns></returns>
        public SettleNotifyData GetSettleNotifyData(JObject joIn,JObject joSettlementRtn)
        {
            SettleNotifyData data = new SettleNotifyData();
            data.outBizNo = JsonHelper.getDestValue(joIn, "outBizNo"); // 核身或者刷脸的医疗机构业务流水号（必填）
            data.medicalSettleState = JsonHelper.getDestValue(joIn, "medicalSettleState"); // 医保/自费结算状态（必填）
            data.authNo = JsonHelper.getDestValue(joIn, "authNo"); // 实人认证业务流水号（可空）
            data.totalFee = JsonHelper.getDestValue(joIn, "medfee_sumamt"); // 总费用（必填）字符型（金额建议为分或保留小数）
            data.bizType = JsonHelper.getDestValue(joIn, "bizType"); // 业务场景（必填）
            data.idNo = JsonHelper.getDestValue(joIn, "idNo"); // 身份证号码（可空）
            data.userName = JsonHelper.getDestValue(joIn, "userName"); // 姓名（可空）
            data.setlTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") ; // 结算时间（必填）格式如：yyyy-MM-dd HH:mm:ss
            data.hospitalName = Global.inf.hospitalNO; // 定点机构名称（必填）
            data.doctorName = JsonHelper.getDestValue(joIn, "doctorName"); // 医生姓名（可空）
            data.failReason = JsonHelper.getDestValue(joIn, "failReason"); // 失败原因（可空）

            // ========== 以下字段在 medicalSettleState=SUCCESS 或 REFUND_SUCCESS 时必填 ==========
            if (data.medicalSettleState.Contains("SUCCESS"))
            {
                data.medicalSettleNo = JsonHelper.getDestValue(joSettlementRtn, "setl_id"); // 医保单据流水号（成功时必填）
                //data.ownAmt = JsonHelper.getDestValue(joSettlementRtn, "fulamt_ownpay_amt"); // 自费费用（成功时必填）
                //data.hifAmt = JsonHelper.getDestValue(joSettlementRtn, "hifp_pay"); // 医保报销费用（成功时必填）  ??各项相加？
                data.ownAmt = Global.Set.psnCashAmt.ToString(); // 自费费用（成功时必填）
                data.hifAmt = (Global.Set.psnAcctAmt + Global.Set.ybAmt + Global.Set.hospAmt).ToString(); // 三笔费用相加
                data.acctAmt = JsonHelper.getDestValue(joSettlementRtn, "acct_pay"); // 个人帐户支出（成功时必填）
                data.hifpAmt = JsonHelper.getDestValue(joSettlementRtn, "hifp_pay"); // 统筹基金支出（成功时必填）
                data.hifmiAmt = JsonHelper.getDestValue(joSettlementRtn, "hifob_pay"); // 大额医疗保险支出（可空）
                data.cvlservAmt = JsonHelper.getDestValue(joSettlementRtn, "cvlserv_pay"); // 公务员补助（可空）
                data.maAmt = JsonHelper.getDestValue(joSettlementRtn, "maf_pay"); // 医疗救助（可空）
                data.hosPreAmt = JsonHelper.getDestValue(joSettlementRtn, "hosp_part_amt"); // 单病种定点医疗机构垫支（可空）
                data.medOverLmtAmt = JsonHelper.getDestValue(joSettlementRtn, "overlmt_selfpay"); // 药品超标扣款金额（可空）
                data.mafAmt = ""; // 扶贫救助（可空）
                data.cvlservDedcAmt = ""; // 历史起付公务员返还（可空）
                data.balance = JsonHelper.getDestValue(joSettlementRtn, "balc"); // 帐户余额（可空）
            }
            

            return data;
        }

        public int SettleNotify(SettleNotifyData settleNotifyData, out string OutBuff)
        {
            settleNotifyData.orgId = Global.inf.hospitalNO;
            settleNotifyData.operatorId = Global.user.ID;
            settleNotifyData.operatorName = Global.user.name;
            settleNotifyData.officeId = Global.user.officeID;
            settleNotifyData.officeName = Global.user.officeName;
            settleNotifyData.businessType = Global.pat.card.ecBizType;

            NationECInput nationECInput = new NationECInput();
            nationECInput.data = settleNotifyData;
            nationECInput.orgId = Global.inf.hospitalNO;
            nationECInput.transType = "cn.nhsa.settle.notify";
            nationECInput.extra = "";
            //调用刷脸认证接口cn.nhsa.ec.auth返回授权信息authNo
            NationECCodeAPI nationECCodeAPI = new NationECCodeAPI(Global.inf.ecURL);
            nationECCodeAPI.NationEcTrans(nationECInput, out OutBuff);
            if (JsonHelper.getDestValue(JObject.Parse(OutBuff), "code") != "0")
            {
                OutBuff = $"调用刷脸认证接口{nationECInput.transType}失败：" + OutBuff;
                return -1;
            }
            else
            {
                return 0;
            }
        }


        /// <summary>
        /// 【1002】费用结算支付密码校验
        /// </summary>
        /// <param name="password">明文密码</param>
        /// <param name="errMsg">错误信息</param>
        /// <returns>0成功，其他失败</returns>
        public int VerifyPin(string password, out string errMsg)
        {
            errMsg = "";
            int result = -1;
            string input = password;
            try
            {
                // 使用全局变量获取参数
                string mdtrtCertNo = Global.pat.mdtrtcertNO;      // 就诊凭证编号
                string mdtrtCertType = Global.pat.mdtrtcertType;  // 就诊凭证类型
                string certNo = Global.pat.certNO;                 // 证件号码
                string psnCertType = Global.pat.certType;          // 人员证件类型
                string psnNo = Global.pat.psn_no;                  // 人员编号

                // 获取医药机构秘钥（需要先初始化）
                Global.inf.secretKey = "DDF0EBD542B19072";//银海控件的密钥
                string secretKey = VerifyPasswordEncryptUtils.encrypt(Global.inf.secretKey, Global.inf.secretKey);


                string cipherText = VerifyPasswordEncryptUtils.encrypt(password, Global.inf.secretKey);
                // SM4加密密码
                //string cipherText = PTMedicalInsurance.Common.SignUtils.sm4Encrypt(
                //    Global.inf.appId, secretKey, password);

                // 构建输入参数
                JObject joData = new JObject();
                joData["mdtrt_cert_no"] = mdtrtCertNo;
                joData["mdtrt_cert_type"] = mdtrtCertType;
                joData["certno"] = certNo;
                joData["psn_cert_type"] = psnCertType;
                joData["psn_no"] = psnNo;
                joData["secret_key"] = secretKey;
                joData["ciphertext"] = cipherText;

                input = joData.ToString();

                // 调用COM组件
                InvokeComCenter2026 carder = new InvokeComCenter2026();
                result = carder.Business("1002", ref input, ref errMsg);
                if (result != 0) return result;

                JObject joRtn = JObject.Parse(errMsg);
                result = JsonHelper.parseComRtnValue(joRtn, out errMsg);
                return result;
            }
            catch (Exception ex)
            {
                errMsg = "VerifyPin异常:" + ex.Message;
                return result;
            }
            finally
            {
                Global.writeLog($"VerifyPin:{result}", input, errMsg);
            }
        }
    }

   

}
