/*
 * Copyright (c) 1998, 2020 Oracle and/or its affiliates. All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v. 2.0 which is available at
 * http://www.eclipse.org/legal/epl-2.0,
 * or the Eclipse Distribution License v. 1.0 which is available at
 * http://www.eclipse.org/org/documents/edl-v10.php.
 *
 * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
 */

// Contributors:
//     Oracle - initial API and implementation from Oracle TopLink
package org.eclipse.persistence.testing.sdo.helper.xmlhelper.loadandsave;

import commonj.sdo.Type;
import commonj.sdo.helper.XMLDocument;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.sax.SAXSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import org.eclipse.persistence.testing.sdo.helper.xmlhelper.SDOXMLHelperTestCases;
import org.eclipse.persistence.sdo.SDOConstants;
import org.eclipse.persistence.sdo.SDOType;
import org.eclipse.persistence.sdo.helper.SDOXMLHelper;
import org.eclipse.persistence.sdo.helper.extension.SDOUtil;
import org.w3c.dom.Document;
import org.xml.sax.InputSource;

public abstract class LoadAndSaveWithOptionsTestCases extends SDOXMLHelperTestCases {
    public LoadAndSaveWithOptionsTestCases(String name) {
        super(name);
    }

    protected String getNoSchemaControlFileName() {
        return getControlFileName();
    }

    protected String getNoSchemaControlWriteFileName() {
        return getNoSchemaControlFileName();
    }

    protected String getControlDataObjectFileName() {
        return getControlWriteFileName();
    }

    protected String getUnrelatedSchemaName() {
        return "./org/eclipse/persistence/testing/sdo/helper/xmlhelper/PurchaseOrder.xsd";
    }

    abstract protected String getSchemaName();

    abstract protected String getControlFileName();

    abstract protected void registerTypes();


    abstract protected String getControlRootName();

    protected String getControlWriteFileName() {
        return getControlFileName();
    }

    protected String getControlRootURI() {
        return NON_DEFAULT_URI;
    }

    public String getFullClassPackageName(Type aType) {
        StringBuffer className = new StringBuffer(SDOUtil.getPackageNameFromURI(aType.getURI()));
        className.append(SDOConstants.JAVA_PACKAGE_NAME_SEPARATOR);
        className.append(aType.getName().substring(0,1).toUpperCase());
        className.append(aType.getName().substring(1));
        return className.toString();
    }

    public void verifyPackageNameGeneratedFromURI(String uri, String typeName) {
        Type aType = typeHelper.getType(uri, typeName);
        String className = ((SDOType)aType).getInstanceClassName();
        String mangledClassName = getFullClassPackageName(aType);
        assertEquals(mangledClassName, className);
    }

    // Override package generation based on the JAXB 2.0 algorithm in SDOUtil.java
    protected List<String> getPackages() {
        List<String> packages = new ArrayList<String>();
        packages.add(NON_DEFAULT_JAVA_PACKAGE_DIR);
        return packages;
    }

    public void testLoadFromInputStreamSaveDocumentToOutputStream() throws Exception {
        defineTypes();

        FileInputStream inputStream = new FileInputStream(getControlFileName());
        XMLDocument document = xmlHelper.load(inputStream, null, getOptions());
        verifyAfterLoad(document);
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        xmlHelper.save(document, outputStream, null);

        compareXML(getControlWriteFileName(), outputStream.toString());

    }

    //
    public void testNoSchemaLoadFromInputStreamSaveDataObjectToString() throws Exception {
        registerTypes();
        FileInputStream inputStream = new FileInputStream(getNoSchemaControlFileName());
        XMLDocument document = xmlHelper.load(inputStream, null, getOptions());
        verifyAfterLoad(document);
        StringWriter writer = new StringWriter();
        xmlHelper.save(document, writer, null);
        // Nodes will not be the same but XML output is
        compareXML(getNoSchemaControlWriteFileName(), writer.toString());//, false);
    }

    //
    public void testLoadFromInputStreamWithURIAndOptionsSaveDataObjectToOutputStream() throws Exception {
        defineTypes();
        FileInputStream inputStream = new FileInputStream(getControlFileName());
        XMLDocument document = xmlHelper.load(inputStream, null, getOptions());// xsi:type will be written out
        verifyAfterLoad(document);

        ByteArrayOutputStream outstream = new ByteArrayOutputStream();
        xmlHelper.save(document.getRootObject(), getControlRootURI(), getControlRootName(), outstream);
        compareXML(getControlDataObjectFileName(), outstream.toString());

    }

    //
    // xpaths/doms null
    public void testLoadFromFileReaderWithURIAndOptionsStreamSaveDataObjectToWriter() throws Exception {
        defineTypes();
        FileReader reader = new FileReader(getControlFileName());
        XMLDocument document = xmlHelper.load(reader, null, getOptions());
        verifyAfterLoad(document);

        String s = xmlHelper.save(document.getRootObject(), getControlRootURI(), getControlRootName());
        compareXML(getControlDataObjectFileName(), s);
    }

    // okay except will set isDirty=true which will clear marshalledObject in xmlMarshaller
    public void testLoadFromAndSaveAfterDefineMultipleSchemas() throws Exception {
        defineTypes();
        xsdHelper.define(getSchema(getUnrelatedSchemaName()));
        FileInputStream inputStream = new FileInputStream(getControlFileName());
        XMLDocument document = xmlHelper.load(inputStream, null, getOptions());// xsi:type will be written out
        verifyAfterLoad(document);
        String s = xmlHelper.save(document.getRootObject(), getControlRootURI(), getControlRootName());
        compareXML(getControlDataObjectFileName(), s);

    }

    public void testLoadFromInputStreamWithURIAndOptionsSaveDataObjectToStreamResult() throws Exception {
        DocumentBuilderFactory factory;
        DocumentBuilder builder;
        Document document1 = null;

        //DOMSource source = null;
        defineTypes();
        factory = DocumentBuilderFactory.newInstance();
        factory.setNamespaceAware(true);
        factory.setValidating(false);
        builder = factory.newDocumentBuilder();

        document1 = builder.parse(new File(getControlFileName()));
        document1.toString();
        //source = new DOMSource(document1);
        XMLDocument document = ((SDOXMLHelper)xmlHelper).load(new FileInputStream(getControlFileName()), null, getOptions());
        verifyAfterLoad(document);

        ByteArrayOutputStream outstream = new ByteArrayOutputStream();

        StreamResult result = new StreamResult(outstream);
        ((SDOXMLHelper)xmlHelper).save(document, result, null);
        compareXML(getControlWriteFileName(), result.getOutputStream().toString());

    }

    public void testLoadFromDomSourceWithURIAndOptionsSaveDataObjectToStreamResult() throws Exception {
        DocumentBuilderFactory factory;
        DocumentBuilder builder;
        Document document1 = null;
        DOMSource source = null;

        defineTypes();

        factory = DocumentBuilderFactory.newInstance();
        factory.setNamespaceAware(true);
        factory.setValidating(false);
        builder = factory.newDocumentBuilder();

        document1 = builder.parse(new File(getControlFileName()));

        source = new DOMSource(document1);

        XMLDocument document = ((SDOXMLHelper)xmlHelper).load(source, null, getOptions());
        verifyAfterLoad(document);

        ByteArrayOutputStream outstream = new ByteArrayOutputStream();

        StreamResult result = new StreamResult(outstream);

        ((SDOXMLHelper)xmlHelper).save(document, result, null);
        compareXML(getControlWriteFileName(), result.getOutputStream().toString());

    }

    public void testLoadFromSAXSourceWithURIAndOptionsSaveDataObjectToStreamResult() throws Exception {
        SAXSource source = null;

        defineTypes();
        FileInputStream inputStream = new FileInputStream(getControlFileName());
        source = new SAXSource(new InputSource(inputStream));

        XMLDocument document = ((SDOXMLHelper)xmlHelper).load(source, null, getOptions());
        verifyAfterLoad(document);

        ByteArrayOutputStream outstream = new ByteArrayOutputStream();
        StreamResult result = new StreamResult(outstream);

        ((SDOXMLHelper)xmlHelper).save(document, result, null);
        compareXML(getControlWriteFileName(), result.getOutputStream().toString());

    }

    public void testLoadFromStreamSourceWithURIAndOptionsSaveDataObjectToStreamResult() throws Exception {
        StreamSource source = null;

        defineTypes();

        FileInputStream inputStream = new FileInputStream(getControlFileName());

        source = new StreamSource(inputStream);

        XMLDocument document = ((SDOXMLHelper)xmlHelper).load(source, null, getOptions());
        verifyAfterLoad(document);

        ByteArrayOutputStream outstream = new ByteArrayOutputStream();

        StreamResult result = new StreamResult(outstream);

        ((SDOXMLHelper)xmlHelper).save(document, result, null);
        compareXML(getControlWriteFileName(), result.getOutputStream().toString());

    }

    protected void compareXML(String controlFileName, String testString) throws Exception {
        compareXML(controlFileName, testString, true);
    }

    protected void compareXML(String controlFileName, String testString, boolean compareNodes) throws Exception {
        String controlString = removeCopyrightFromString(getControlString(controlFileName));
        log("Expected:" + controlString);
        log("Actual  :" + testString);

        StringReader reader = new StringReader(testString);
        InputSource inputSource = new InputSource(reader);
        Document testDocument = parser.parse(inputSource);
        reader.close();

        if (compareNodes) {
            Document controlDocument = getDocument(controlFileName);
            removeCopyrightNode(controlDocument);
            assertXMLIdentical(controlDocument, testDocument);
        }
    }

    protected String getControlString(String fileName) {
        try {
            FileInputStream inputStream = new FileInputStream(fileName);
            byte[] bytes = new byte[inputStream.available()];
            inputStream.read(bytes);
            return new String(bytes);
        } catch (Exception e) {
            e.printStackTrace();
            fail("An error occurred loading the control document");
            return null;
        }
    }

    protected void verifyAfterLoad(XMLDocument document) {
        assertNotNull(document);
        assertNotNull(document.getRootObject());
        assertNull(document.getRootObject().getContainer());
    }

    protected List defineTypes() {
        return xsdHelper.define(getSchema(getSchemaName()));
    }

    protected String getSchemaLocation() {
        return "";
    }

    protected Object getOptions() {
        return null;
    }
}
