package pl.qumak.push;

import org.springframework.boot.builder.SpringApplicationBuilder;
import pl.qumak.Client;
import pl.qumak.xsd.datex2v3.*;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.transform.stream.StreamSource;
import java.io.BufferedWriter;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.Writer;
import java.time.LocalDateTime;

public class PushService {

    private String companyName;

    public void setCompanyName(String companyName) {
        this.companyName = companyName;
    }

    public void callWebService(SpringApplicationBuilder builder) {
        Client wsClient = builder.context().getBean(Client.class);
        MessageContainer request = createD2();
        MessageContainerRequestToXml(request);
        MessageContainer response = wsClient.pushRequest(request);
        MessageContainerResponseToXML(response);
    }

    private InternationalIdentifier createInternationalIdentifier(ObjectFactory objectFactory) {
        InternationalIdentifier internationalIdentifier = objectFactory.createInternationalIdentifier();
        internationalIdentifier.setCountry("PL");
        internationalIdentifier.setNationalIdentifier(companyName);
        return internationalIdentifier;
    }

    private ExchangeInformation createExchangeInformation() {
        ObjectFactory objectFactory = new ObjectFactory();
        ExchangeInformation exchange = objectFactory.createExchangeInformation();
        exchange.setModelBaseVersion("3");
        InternationalIdentifier internationalIdentifier = createInternationalIdentifier(objectFactory);
        ExchangeContext exchangeContext = objectFactory.createExchangeContext();
        Agent agent = objectFactory.createAgent();
        agent.setInternationalIdentifier(internationalIdentifier);
        exchangeContext.setSupplierOrCisRequester(agent);
        exchangeContext.setExchangeSpecificationVersion("Exchange2020");
        _ProtocolTypeEnum protocolTypeEnum = objectFactory.create_ProtocolTypeEnum();
        protocolTypeEnum.setValue(ProtocolTypeEnum.SNAPSHOT_PULL);
        exchangeContext.setCodedExchangeProtocol(protocolTypeEnum);
        exchange.setExchangeContext(exchangeContext);
        DynamicInformation dynamicInformation = objectFactory.createDynamicInformation();
        _ExchangeStatusEnum exchangeStatusEnum = objectFactory.create_ExchangeStatusEnum();
        exchangeStatusEnum.setValue(ExchangeStatusEnum.UNDEFINED);
        dynamicInformation.setExchangeStatus(exchangeStatusEnum);
        dynamicInformation.setMessageGenerationTimestamp(LocalDateTime.now());
        exchange.setDynamicInformation(dynamicInformation);
        return exchange;
    }

    private MessageContainer createD2() {
        ObjectFactory objectFactory = new ObjectFactory();
        MessageContainer messageContainer = objectFactory.createMessageContainer();
        messageContainer.setModelBaseVersion("3");
        ExchangeInformation exchange = createExchangeInformation();
        messageContainer.setExchangeInformation(exchange);

        SituationPublication situationPublication = objectFactory.createSituationPublication();
        situationPublication.setPublicationTime(LocalDateTime.now());
        situationPublication.setPublicationCreator(createInternationalIdentifier(objectFactory));
        situationPublication.setLang("pl");
        situationPublication.setModelBaseVersion("3");

        HeaderInformation headerInformation = objectFactory.createHeaderInformation();
        _InformationStatusEnum informationStatusEnum = objectFactory.create_InformationStatusEnum();
        informationStatusEnum.setValue(InformationStatusEnum.REAL);
        headerInformation.setInformationStatus(informationStatusEnum);

        SituationRecord situationRecord = createSituationRecord();
        situationRecord.setId("sr1245");
        situationRecord.setVersion("1");

        situationRecord.setSituationRecordCreationTime(LocalDateTime.now());
        situationRecord.setSituationRecordVersionTime(LocalDateTime.now());

        _ProbabilityOfOccurrenceEnum probabilityOfOccurrenceEnum = objectFactory.create_ProbabilityOfOccurrenceEnum();
        probabilityOfOccurrenceEnum.setValue(ProbabilityOfOccurrenceEnum.CERTAIN);
        situationRecord.setProbabilityOfOccurrence(probabilityOfOccurrenceEnum);

        Validity validity = objectFactory.createValidity();
        _ValidityStatusEnum validityStatusEnum = objectFactory.create_ValidityStatusEnum();
        validityStatusEnum.setValue(ValidityStatusEnum.ACTIVE);
        validity.setValidityStatus(validityStatusEnum);
        OverallPeriod overallPeriod = objectFactory.createOverallPeriod();
        overallPeriod.setOverallStartTime(LocalDateTime.now());
        validity.setValidityTimeSpecification(overallPeriod);
        situationRecord.setValidity(validity);

        Comment generalPublicComment = objectFactory.createComment();
        _CommentTypeEnum commentTypeEnum = objectFactory.create_CommentTypeEnum();
        commentTypeEnum.setValue(CommentTypeEnum.DESCRIPTION);
        generalPublicComment.setCommentType(commentTypeEnum);
        StringHelper stringHelper = new StringHelper(objectFactory, "PL");
        MultilingualString comment = stringHelper
                .createFrom("Zablokowany pas lewy w kierunku Lodzi. Ruch odbywa się pasem prawym w kierunku Lodzi.\n");
        generalPublicComment.setComment(comment);
        situationRecord.getGeneralPublicComment().add(generalPublicComment);

        PointLocation pointLocation = objectFactory.createPointLocation();
        situationRecord.setLocationReference(pointLocation);

        PointByCoordinates pointByCoordinates = objectFactory.createPointByCoordinates();
        PointCoordinates pointCoordinates = objectFactory.createPointCoordinates();
        final float longitude = (float) 20.729871;
        final float latitude = (float) 52.163258;
        pointCoordinates.setLatitude(latitude);
        pointCoordinates.setLongitude(longitude);
        pointByCoordinates.setPointCoordinates(pointCoordinates);
        pointLocation.setPointByCoordinates(pointByCoordinates);

        Situation situation = objectFactory.createSituation();
        situation.setId("s123");
        situation.setSituationVersionTime(LocalDateTime.now());
        situation.getSituationRecord().add(situationRecord);
        situation.setHeaderInformation(headerInformation);
        situationPublication.getSituation().add(situation);

        messageContainer.setExchangeInformation(exchange);
        messageContainer.getPayload().add(situationPublication);
        return messageContainer;
    }

    private SituationRecord createSituationRecord() {
        ObjectFactory objectFactory = new ObjectFactory();
        String datexClass = "<weatherRelatedRoadConditionType>blackIce</weatherRelatedRoadConditionType>";
        String datexType = getSituationRecordType("<weatherRelatedRoadConditionType>blackIce</weatherRelatedRoadConditionType>");
        final WeatherRelatedRoadConditions weatherRelatedRoadConditions = objectFactory.createWeatherRelatedRoadConditions();
        _WeatherRelatedRoadConditionTypeEnum weatherRelatedRoadConditionTypeEnum = objectFactory.create_WeatherRelatedRoadConditionTypeEnum();
        weatherRelatedRoadConditionTypeEnum.setValue(WeatherRelatedRoadConditionTypeEnum.fromValue(datexType));
        weatherRelatedRoadConditions.getWeatherRelatedRoadConditionType().add(weatherRelatedRoadConditionTypeEnum);
        _TrafficConstrictionTypeEnum trafficConstrictionTypeEnum = new _TrafficConstrictionTypeEnum();
        trafficConstrictionTypeEnum.setValue(TrafficConstrictionTypeEnum.ROAD_BLOCKED);
        weatherRelatedRoadConditions.setTrafficConstrictionType(trafficConstrictionTypeEnum);
        return weatherRelatedRoadConditions;
    }

    private String getSituationRecordType(String datexElement) {
        JAXBElement<String> type = null;
        try {
            JAXBContext jaxbContext = JAXBContext.newInstance(String.class);
            Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
            StreamSource xmlSource = new StreamSource(new StringReader(datexElement));
            type = unmarshaller.unmarshal(xmlSource, String.class);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return type.getValue();
    }

    public void MessageContainerRequestToXml(MessageContainer request) {
        Writer writer = null;
        System.out.println("Przygotowano komunikat do wysłania do serwera KPD. Jego treść została zapisana do pliku pushRequest.xml.");
        String pushRequest = marshalXmlToString(request);
        try {
            writer = new BufferedWriter(new OutputStreamWriter(
                    new FileOutputStream("pushRequest.xml"), "utf-8"));
            writer.write(pushRequest);
        } catch (IOException ex) {
            ex.printStackTrace();
        } finally {
            try {
                writer.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    private void MessageContainerResponseToXML(MessageContainer response) {
        Writer writer = null;
        System.out.println("Odebrano komunikat od serwera KPD. Jego treść została zapisana do pliku pushResponse.xml");
        String pushResponse = marshalXmlToString(response);
        try {
            writer = new BufferedWriter(new OutputStreamWriter(
                    new FileOutputStream("pushResponse.xml"), "utf-8"));
            writer.write(pushResponse);
        } catch (IOException ex) {
            ex.printStackTrace();
        } finally {
            try {
                writer.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public String marshalXmlToString(Object messageContainer) {
        String xmlString = null;
        try {
            JAXBContext jaxbContext = JAXBContext.newInstance(MessageContainer.class);
            Marshaller marshaller = jaxbContext.createMarshaller();
            marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
            StringWriter stringWriter = new StringWriter();
            marshaller.marshal(messageContainer, stringWriter);
            xmlString = stringWriter.toString();
        } catch (JAXBException e) {
            e.printStackTrace();
        }
        return xmlString;
    }
}
