/*
 * Copyright (c) eHealth
 */

package be.fgov.ehealth.technicalconnector.tests.beid.tlv;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.text.SimpleDateFormat;
import java.util.Locale;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import be.ehealth.technicalconnector.enumeration.Charset;
import be.ehealth.technicalconnector.utils.ConnectorIOUtils;
import be.ehealth.technicalconnector.utils.MarshallerHelper;
import be.fedict.commons.eid.client.FileType;
import be.fgov.ehealth.technicalconnector.tests.beid.domain.testcard.DoctypeType;
import be.fgov.ehealth.technicalconnector.tests.beid.domain.testcard.Eid;
import be.fgov.ehealth.technicalconnector.tests.beid.domain.testcard.GenderType;
import be.fgov.ehealth.technicalconnector.tests.beid.domain.testcard.SpecType;

/**
 * @author EHP
 */
public class XmlEidViewerTLVFileGenerator implements TLVFileGenerator {

    private static final Logger LOG = LoggerFactory.getLogger(XmlEidViewerTLVFileGenerator.class);

    public static final Locale DUTCH = new Locale("nl", "BE");

    private static MarshallerHelper<Eid, Eid> helper = new MarshallerHelper<Eid, Eid>(Eid.class, Eid.class);

    public byte[] generate(FileType fileType, String location) {
        try {
            Eid eid = helper.toObject(ConnectorIOUtils.getResourceAsStream(location));
            switch (fileType) {
                case Identity:
                    return toIdentity(eid);
                case Address:
                    return toAddress(eid);
                case Photo:
                    return eid.getIdentity()
                              .getPhoto();
                default:
                    throw new IllegalArgumentException("Unsupported fileType [" + fileType + "]");
            }
        } catch (Exception e) {
            LOG.error("Unable to generate content {}", e.getMessage());
        }
        return null;
    }

    public static byte[] toAddress(Eid eid) {
        ByteArrayOutputStream baos = null;
        try {
            baos = new ByteArrayOutputStream();
            writeTLV(1, eid.getAddress()
                           .getStreetandnumber()
                           .getBytes(Charset.UTF_8.getName()), baos);
            writeTLV(2, eid.getAddress()
                           .getZip()
                           .getBytes(Charset.UTF_8.getName()), baos);
            writeTLV(3, eid.getAddress()
                           .getMunicipality()
                           .getBytes(Charset.UTF_8.getName()), baos);
            return baos.toByteArray();
        } catch (Exception e) {
            throw new IllegalArgumentException(e);
        } finally {
            ConnectorIOUtils.closeQuietly(baos);
        }
    }

    public static byte[] toIdentity(Eid eid) {
        ByteArrayOutputStream baos = null;
        try {
            baos = new ByteArrayOutputStream();
            writeTLV(1, eid.getCard()
                           .getCardnumber()
                           .getBytes(Charset.UTF_8.getName()), baos);
            writeTLV(2, eid.getCard()
                           .getChipnumber(), baos);
            writeTLV(3, convert(eid.getCard()
                                   .getValiditydatebegin(), "dd.MM.yyyy"), baos);
            writeTLV(4, convert(eid.getCard()
                                   .getValiditydateend(), "dd.MM.yyyy"), baos);
            writeTLV(5, eid.getCard()
                           .getDeliverymunicipality()
                           .toString()
                           .getBytes(Charset.UTF_8.getName()), baos);
            writeTLV(6, eid.getIdentity()
                           .getNationalnumber()
                           .getBytes(Charset.UTF_8.getName()), baos);
            writeTLV(7, eid.getIdentity()
                           .getName()
                           .getBytes(Charset.UTF_8.getName()), baos);
            writeTLV(8, eid.getIdentity()
                           .getFirstname()
                           .getBytes(Charset.UTF_8.getName()), baos);
            writeTLV(9, eid.getIdentity()
                           .getMiddlenames()
                           .getBytes(Charset.UTF_8.getName()), baos);
            writeTLV(10, eid.getIdentity()
                            .getNationality()
                            .getBytes(Charset.UTF_8.getName()), baos);
            writeTLV(11, eid.getIdentity()
                            .getPlaceofbirth()
                            .getBytes(Charset.UTF_8.getName()), baos);
            writeTLV(12, convert(eid.getIdentity()
                                    .getDateofbirth(), "dd.MMM.yyyy"), baos);
            writeTLV(13, convert(eid.getIdentity()
                                    .getGender()), baos);
            if (StringUtils.isNotBlank(eid.getIdentity()
                                          .getNoblecondition())) {
                writeTLV(14, eid.getIdentity()
                                .getNoblecondition()
                                .getBytes(Charset.UTF_8.getName()), baos);
            }
            writeTLV(15, convertDocumentType(eid.getCard()
                                                .getDocumenttype()), baos);
            writeTLV(16, convertSpecialStatus(eid.getIdentity()
                                                 .getSpecialstatus()), baos);
            return baos.toByteArray();
        } catch (Exception e) {
            throw new IllegalArgumentException(e);
        } finally {
            ConnectorIOUtils.closeQuietly(baos);
        }
    }

    private static byte[] convertSpecialStatus(SpecType specialstatus) {
        switch (specialstatus) {
            case NO_STATUS:
                return "0".getBytes();
            case WHITE_CANE:
                return "1".getBytes();
            case EXTENDED_MINORITY:
                return "2".getBytes();
            case WHITE_CANE_EXTENDED_MINORITY:
                return "3".getBytes();
            case YELLOW_CANE:
                return "4".getBytes();
            case YELLOW_CANE_EXTENDED_MINORITY:
                return "5".getBytes();
            default:
                throw new IllegalArgumentException("Unsupported SpecialStatus [" + specialstatus + "]");
        }
    }

    private static void writeTLV(int idx, byte[] content, OutputStream os) throws IOException {
        os.write(idx);
        os.write(content.length);
        os.write(content);
    }

    private static byte[] convert(String cal, String targetFormat) throws Exception {
        SimpleDateFormat source = new SimpleDateFormat("yyyyMMdd");
        SimpleDateFormat target = new SimpleDateFormat(targetFormat, DUTCH);
        return target.format(source.parse(cal))
                     .toUpperCase()
                     .getBytes(Charset.UTF_8.getName());
    }

    private static byte[] convert(GenderType gender) {
        switch (gender) {
            case MALE:
                return "M".getBytes();
            case FEMALE:
                return "F".getBytes();
            default:
                throw new IllegalArgumentException("Unsupported gender [" + gender + "]");
        }
    }

    private static byte[] convertDocumentType(DoctypeType docType) {
        switch (docType) {
            case BELGIAN_CITIZEN:
                return "1".getBytes();
            case KIDS_CARD:
                return "6".getBytes();
            case BOOTSTRAP_CARD:
                return "7".getBytes();
            case HABILITATION_CARD:
                return "8".getBytes();
            case FOREIGNER_A:
                return "11".getBytes();
            case FOREIGNER_B:
                return "12".getBytes();
            case FOREIGNER_C:
                return "13".getBytes();
            case FOREIGNER_D:
                return "14".getBytes();
            case FOREIGNER_E:
                return "15".getBytes();
            case FOREIGNER_E_PLUS:
                return "16".getBytes();
            case FOREIGNER_F:
                return "17".getBytes();
            case FOREIGNER_F_PLUS:
                return "18".getBytes();
            case EUROPEAN_BLUE_CARD_H:
                return "19".getBytes();
            default:
                throw new IllegalArgumentException("Unsupported gender [" + docType + "]");
        }
    }


}
