/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package action;

import com.viettel.mobileca.signature.apservice.APWs;
import com.viettel.mobileca.signature.apservice.TransactionInfo;
import com.viettel.mobileca.signature.utils.APGetPort;
import com.viettel.mobileca.signature.utils.APSignature;
import com.viettel.mobileca.signature.utils.Constants;
import com.viettel.mobileca.signature.utils.MacUtils;
import com.viettel.signature.pdf.TimestampConfig;
import com.viettel.signature.plugin.SignOOXmlFile;
import com.viettel.signature.plugin.SignPdfFile;
import com.viettel.signature.plugin.SignXmlFile;
import form.SignMobileCAForm;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.security.PrivateKey;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Random;
import java.util.regex.Pattern;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.log4j.Logger;

import org.apache.struts.action.Action;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.bouncycastle.util.encoders.Base64;
import utils.HashFilePDF;
import utils.X509ExtensionUtil;

/**
 *
 * @author khanhdn1
 */
public class SignMobileCAAction extends Action {

    private Logger logger = Logger.getLogger(SignMobileCAAction.class);
    SignPdfFile pdfSig;
    SignXmlFile xmlSig;
    SignOOXmlFile ooxmlSig;

    private final String AP_ID = "AP2";
    private final String PROCESS_CODE_SIGN = "000001";
    private final String PROCESS_CODE_QUERYCER = "000006";
    private final String MODE_SYNC = "SYNC";
    private final String URL_WS = utils.ResourceBundleUtils.getApplicationResource("url.ws.mobile.ca");
//    private final String URL_WS = "http://mobilepki.viettel-ca.vn:8080/apws.asmx";
//    private final String URL_WS = "http://10.60.108.20:8084/apws.asmx";
//    private final String PRIVATE_KEY = "MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAI1ZGghrDQryVQcDDe1gpJ1TQvRWZz0P59/04LlMOdcf0G7XGHy7tzmWffdKgVmlkTbw0tMGa37lg7Suxrm9JpmhcEQmTMo6L06SEmBYSiZfPWMyDnjdWhE8mPMP3ju3xx24UVXcpSclykQflTBr42yZgqsgM19ndsDFkwtz2uWDAgMBAAECgYAJzOPBMar111eN5OhSTSEcx2kdB+Cgmzm4jYIHVwGrqMkK5l8MRvetRoH1Y3UUgiZPaOM1Pny1j7RSEsw0lKjYY4Jawm5n7js13VkIs9tO8HhK00Oo/7a6ZRxAbczpfvGHmMdwaUQgHSGngzE7T3D8Eh4xx3Qu6fmTAIeKPNSMAQJBANfPb9gDkWIsQ/16siOQaTEfacASx/2MvucfrQ2WYGWbG1xNVfA1hkC2tmRRu3SRJp/1lhlERTvOSac4m9IBMasCQQCnq75nAlQTU+/1GvH8nLyEPrCudn40jMCKSEkMWJKKVuiKCrF2GJCZQipNs1DfMSyPggux3Z3hQ62JBuZfNvOJAkEAxIYCM5QMMHpe79Vrozc+k50nj+GKfTpOHeqajGUEI4K7x7IlMDmNqCC6t2A2dFA5/DCIHzosUeno6H6EZxjvQQJAY+IStgiUD0OEge4AU+0G/HzgAb5C5okmtfnj0j/9Y/3r3zgJiYGOuk3JJ6p3tc30brUYxGdyAtyvRx7eI8B3iQJBAJpa4qW6sJ36AKZFLq4D6EwaL2G3kc1bVFSwgRB0TFMB3Vak4O4mu1HWfgCWo20RvJCfcYCrIEdguvd3IunQ9Mc=";
    private final String PRIVATE_KEY = utils.ResourceBundleUtils.getApplicationResource("private.key.ap");
    private final String MODE_ASYNC_SERVER_SERVER = "SS";
    private final String MODE_ASYNC_CLIENT_SERVER = "CS";
    private final String FORMAT_DATE = "{0:yyyyMMddHHmmss}";
    private final String MSSP_ID = "Viettel";
    private final int FORCE_PIN = 1;
    private final int RUN_IMM = 0; // ko yeu cau ky ngay
    private final int SIG_TYPE = 0; // ky van ban
    private final boolean FORCE_PIN_SPECIFIED = true;
    private final boolean SIG_TYPE_SPECIFIED = true;
    private final boolean REQUEST_ID_SPECIFIED = true;

    @Override
    public ActionForward execute(ActionMapping mapping, ActionForm form,
            HttpServletRequest request, HttpServletResponse response)
            throws Exception {
        SignMobileCAForm frm = (SignMobileCAForm) form;
        request.setAttribute("statusResultSignMobileCA", "error");
        request.setAttribute("descErrorSignMobileCA", "");
        request.setAttribute("signedFileName", "");

        String fileName = frm.getFileName();
        String phoneNumber = frm.getPhoneNumber();

        if (fileName == null) {
            logger.error("FileName is empty");
            request.setAttribute("descErrorSignMobileCA", "FileName is empty");
            return mapping.findForward("resultSignMobileCA");
        }

//        Pattern pattern = Pattern.compile("[^a-zA-Z0-9.-_]");
//        Pattern pattern = Pattern.compile("[^/./\\:*?\"<>|]");
        Pattern pattern = Pattern.compile("[^-_.A-Za-z0-9]");
        if (pattern.matcher(fileName).find()) {
            logger.error("FileName chứa ký tự đặc biệt");
            request.setAttribute("descErrorSignMobileCA", "FileName chứa ký tự đặc biệt");
            return mapping.findForward("resultSignMobileCA");
        }

        //Get the servers upload directory real path name
        String folderPath = getServlet().getServletContext().getRealPath("/") + "upload";
        String fileFullPath = folderPath + "/" + fileName;
        //create the upload folder if not exists
        logger.info("fileFullPath: " + fileFullPath);
        File fileDownload = new File(fileFullPath);
        if (!fileDownload.exists()) {
            logger.error("File không tồn tại");
            request.setAttribute("descErrorSignMobileCA", "File không tồn tại");
            return mapping.findForward("resultSignMobileCA");
        }

//        boolean hasSelectCert = true;
        String certSerial = null;

        String dataDisplay = "Thu nghiem ky Mobile-CA";

        //X509Certificate[] chain = mobileCA.GetCertificate(apID, msisdn, certSerial, fileP12, passFileP12);
        List<String> certList = getAllCertificateInSim(AP_ID, phoneNumber, certSerial);
        if (certList == null || certList.size() == 0) {
            request.setAttribute("descErrorSignMobileCA", "Không lấy được thông tin CTS trên SIM");
            return mapping.findForward("resultSignMobileCA");
        }
        String realPath = getServlet().getServletContext().getRealPath("/");
        String folderRootCA = realPath + "RootCA";
        X509Certificate[] certChain = X509ExtensionUtil.getCertChainOfCert(certList.get(0), folderRootCA);

        String base64Hash;

//        String src = realPath + "example.pdf";
        String src = folderPath + "/" + fileName;
        String fontPath = realPath + "share/font/times.ttf";
        pdfSig = new SignPdfFile();
//        String fieldName = "sig2";
//        base64Hash = HashFilePDF.getHashTypeNoDisplay_ExistedSignatureField(pdfSig, src, certChain, fontPath, fieldName);
//        base64Hash = HashFilePDF.getHashTypeRectangleTextDefault(pdfSig, src, certChain, fontPath);
//        base64Hash = HashFilePDF.getHashTypeRectangleTextDefault_ExistedSignatureField(pdfSig, src, certChain, fontPath, fieldName);

        base64Hash = HashFilePDF.getHashTypeRectangleText(pdfSig, src, certChain, fontPath);

//        String displayText = "123";
//        base64Hash = HashFilePDF.getHashTypeRectangleText2_ExistedSignatureField(pdfSig, src, certChain, fontPath, displayText, fieldName);
//        String imageFile = realPath + "attpStamp.png";
//        base64Hash = HashFilePDF.getHashTypeImageDefault(pdfSig, src, certChain, imageFile, fontPath);
//        base64Hash = HashFilePDF.getHashTypeImageDefault_ExistedSignatureField(pdfSig, src, certChain, imageFile, fontPath, fieldName);
//        base64Hash = HashFilePDF.getHashTypeImage(pdfSig, src, certChain, imageFile, fontPath);
//        base64Hash = HashFilePDF.getHashTypeTableDefault(pdfSig, src, certChain, fontPath);
//        base64Hash = HashFilePDF.getHashTypeTable(pdfSig, src, certChain, fontPath);
//        base64Hash = getHashXML(certChain);
//        base64Hash = getHashOOXML(certChain);
        if (base64Hash == null) {
            request.setAttribute("descErrorSignMobileCA", "Tạo Hash không thành công");
            return mapping.findForward("resultSignMobileCA");
        }

//        if (!hasSelectCert) {
//            certSerial = ((X509Certificate) certChain[0]).getSerialNumber().toString(16);
//        }
        String signature = signSynchronous(base64Hash, AP_ID, phoneNumber, dataDisplay, certSerial);

        if (signature == null || signature.isEmpty()) {
            request.setAttribute("descErrorSignMobileCA", "Ký Hash không thành công");
            return mapping.findForward("resultSignMobileCA");
        }

        try {
            Base64.decode(signature);
        } catch (Exception e) {
            logger.error(e);
            request.setAttribute("descErrorSignMobileCA", "Định dạng chữ ký không hợp lệ");
            return mapping.findForward("resultInsertSignature");
        }

        insertSignaturePdfFile(pdfSig, signature, fileName, request);

        request.setAttribute("statusResultSignMobileCA", "success");
        return mapping.findForward("resultSignMobileCA");
    }

    private void insertSignaturePdfFile(SignPdfFile pdfSig, String signature, String fileName, HttpServletRequest request) {
        String name = fileName.split(".pdf")[0] + "_signed";
        String ext = "pdf";
        String filePath = getServlet().getServletContext().getRealPath("/") + "upload";

        File fileDes = new File(filePath + "/" + name + "." + ext);
        if (fileDes.exists()) {
            int index = 1;
            String name_2 = name + " (" + index + ")";
            String path = filePath + "/" + name_2 + "." + ext;
            fileDes = new File(path);
            while (fileDes.exists()) {
                index++;
                name_2 = name + " (" + index + ")";
                path = filePath + "/" + name_2 + "." + ext;
                fileDes = new File(path);
            }
            name = name_2;
        }

        TimestampConfig timestampConfig = new TimestampConfig();
        timestampConfig.setUseTimestamp(false);
        if (!pdfSig.insertSignature(signature, filePath + "/" + name + "." + ext, timestampConfig)) {
            request.setAttribute("descErrorSignMobileCA", "Insert signature into file fail.");
        } else {
            request.setAttribute("statusResultSignMobileCA", "success");
            request.setAttribute("signedFileName", name + "." + ext);
        }
    }

    public void signPDFSynsWithMobileCASyns(String phone) {

    }

    public static int GetRandomNumber(int min, int max) {
        Random rand = new Random();
        int randomNum = rand.nextInt((max - min) + 1) + min;
        return randomNum;
    }

    public long generateRequestId() {
        int seq = GetRandomNumber(1, 999999);
        String strTrace = String.valueOf(seq);
        while (strTrace.length() < 6) {
            strTrace = "0" + strTrace;
        }
        DateFormat df;
        df = new SimpleDateFormat("yyMMdd");
        return Long.valueOf(df.format(new Date()) + strTrace);
    }

    public List<String> getAllCertificateInSim(String apId, String msisdn, String certSerial) {
        try {
            APWs service = APGetPort.getPort(URL_WS);
            TransactionInfo transInfo = new TransactionInfo();

            transInfo.setApId(apId);
            transInfo.setMsspId(MSSP_ID);
            transInfo.setProcessCode(PROCESS_CODE_QUERYCER);
            transInfo.setMsisdn(msisdn);
            if (certSerial != null) {
                transInfo.setCertSerial(certSerial);
            }

            transInfo.setRequestId(generateRequestId());

//            transInfo.requestIdSpecified = REQUEST_ID_SPECIFIED;
            DateFormat df = new SimpleDateFormat("yyyyMMddHHmmss");
            transInfo.setReqDate(df.format(new Date()));

            PrivateKey key = MacUtils.getPrivateKeyFromString(PRIVATE_KEY);

            String strCombine = transInfo.getApId() + transInfo.getMsspId()
                    + transInfo.getProcessCode() + transInfo.getRequestId().toString() + transInfo.getMsisdn();
            String mac = MacUtils.creMsgSig(strCombine, key);
            transInfo.setMac(mac);

            transInfo = service.certificateQuery(transInfo);
            if (transInfo == null || transInfo.getCertList() == null || transInfo.getCertList().size() == 0) {
                return null;
            }

            return transInfo.getCertList();
        } catch (Exception e) {
            logger.error(e);
            return null;
        }
    }

    public String signSynchronous(String hash, String apId, String msisdn, String dataDisplay, String certSerial) {
        try {
            APWs service = APGetPort.getPort(URL_WS);
            TransactionInfo transInfo = new TransactionInfo();

            transInfo.setApId(apId);
            transInfo.setMsspId(MSSP_ID);
            if (certSerial != null && !certSerial.trim().isEmpty()) {
                transInfo.setCertSerial(certSerial);
            }
            transInfo.setProcessCode(PROCESS_CODE_SIGN);
            transInfo.setMsisdn(msisdn);
            transInfo.setMsgMode(MODE_SYNC); // co che dong bo
            transInfo.setForcePIN(FORCE_PIN); // bat buoc nhap PIN
//            transInfo.setforcePINSpecified = FORCE_PIN_SPECIFIED;
            transInfo.setRunImm(RUN_IMM); // ko yeu cau ky ngay
            transInfo.setSigType(SIG_TYPE); // ky van ban
//            transInfo.setsigTypeSpecified = SIG_TYPE_SPECIFIED;
            transInfo.setDataDisplay(dataDisplay);
//            transInfo.setrequestIdSpecified = REQUEST_ID_SPECIFIED;

            DateFormat df = new SimpleDateFormat("yyyyMMddHHmmss");
            transInfo.setReqDate(df.format(new Date()));

            transInfo.setRequestId(generateRequestId());
            logger.info("Thong tin requestID: " + transInfo.getRequestId());

            transInfo.setDataSign(hash);

            PrivateKey key = MacUtils.getPrivateKeyFromString(PRIVATE_KEY);
            String strCombine = transInfo.getApId() + transInfo.getMsspId() + transInfo.getProcessCode() + transInfo.getRequestId().toString()
                    + transInfo.getMsisdn() + transInfo.getDataSign() + transInfo.getDataDisplay() + transInfo.getSigType().toString();
            String mac = MacUtils.creMsgSig(strCombine, key);
            transInfo.setMac(mac);
            logger.info("Thong tin MAC : " + transInfo.getMac());

            if (transInfo.getMac() == null) {
                logger.info("Error get MAC");
                return null;
            }

            transInfo = service.signature(transInfo);

            if (transInfo == null || transInfo.getErrorCode() == null || !"00".equals(transInfo.getErrorCode())) {
                logger.info("Error get signature");
                return null;
            }

            return transInfo.getSignature();
        } catch (Exception e) {
            logger.error(e);
            return null;
        }
    }

}
