package org.commcare.logging;

import org.commcare.AppUtils;
import org.commcare.CommCareApplication;
import org.commcare.android.javarosa.DeviceReportRecord;
import org.commcare.models.database.SqlStorage;
import org.javarosa.core.model.User;
import org.javarosa.core.model.utils.DateUtils;
import org.kxml2.io.KXmlSerializer;
import org.xmlpull.v1.XmlSerializer;

import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Date;

/**
 * This class generates and serializes a device report to either a byte array
 * or to a file as designated by a log record
 *
 * @author ctsims
 */
public class DeviceReportWriter {
    public static final String XMLNS = "http://code.javarosa.org/devicereport";

    private final XmlSerializer serializer;
    private final OutputStream os;
    private final ArrayList<DeviceReportElement> elements = new ArrayList<>();

    public DeviceReportWriter(DeviceReportRecord record) throws IOException {
        this(record.openOutputStream());
    }

    public DeviceReportWriter(OutputStream outputStream) throws IOException {
        os = outputStream;

        serializer = new KXmlSerializer();
        serializer.setOutput(os, "UTF-8");
        serializer.setPrefix("", XMLNS);

        serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
    }


    public void addReportElement(DeviceReportElement element) {
        this.elements.add(element);
    }

    public void write() throws IllegalArgumentException, IllegalStateException, IOException {
        try {
            serializer.startDocument("UTF-8", null);
            serializer.startTag(XMLNS, "device_report");
            try {
                //All inner elements are supposed to catch their errors and wrap them, so we
                //can safely catch any of the processing issues
                try {
                    writeHeader();
                } catch (Exception e) {
                }
                try {
                    writeUserReport();
                } catch (Exception e) {
                }

                for (DeviceReportElement element : elements) {
                    try {
                        element.writeToDeviceReport(serializer);
                    } catch (Exception e) {
                    }
                }
            } finally {
                serializer.endTag(XMLNS, "device_report");
            }

            serializer.endDocument();
        } finally {
            try {
                os.close();
            } catch (IOException e) {
            }
        }
    }

    private void writeHeader() throws IllegalArgumentException, IllegalStateException, IOException {
        CommCareApplication application = CommCareApplication.instance();

        String did = application.getPhoneId();
        writeText("device_id", did);
        writeText("report_date", DateUtils.formatDateTime(new Date(), DateUtils.FORMAT_ISO8601));
        writeText("app_version", AppUtils.getCurrentVersionString());
    }

    private void writeUserReport() throws IllegalArgumentException, IllegalStateException, IOException {
        SqlStorage<User> storage = CommCareApplication.instance().getUserStorage(User.STORAGE_KEY, User.class);

        serializer.startTag(XMLNS, "user_subreport");

        try {
            for (User u : storage) {
                writeUser(u);
            }
        } finally {
            serializer.endTag(XMLNS, "user_subreport");
        }
    }

    private void writeUser(User user) throws IllegalArgumentException, IllegalStateException, IOException {
        serializer.startTag(XMLNS, "user");

        try {
            writeText("username", user.getUsername());
            writeText("user_id", user.getUniqueId());
            writeText("sync_token", user.getLastSyncToken());
        } finally {
            serializer.endTag(XMLNS, "user");
        }
    }

    private void writeText(String element, String text) throws IllegalArgumentException, IllegalStateException, IOException {
        serializer.startTag(XMLNS, element);
        try {
            serializer.text(text);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            serializer.endTag(XMLNS, element);
        }
    }
}

@@ -281,24 +281,69 @@
 .DateUtils;%0A
+import org.javarosa.core.util.PropertyUtils;%0A
 import org.k
@@ -2366,32 +2366,67 @@
                %7D
+%0A%0A                writeMetaBlock();
 %0A            %7D f
@@ -2673,32 +2673,287 @@
        %7D%0A    %7D%0A%0A
+    private void writeMetaBlock() throws IllegalArgumentException, IllegalStateException, IOException %7B%0A        serializer.startTag(XMLNS, %22meta%22);%0A        writeText(%22instanceID%22, PropertyUtils.genUUID());%0A        serializer.endTag(XMLNS, %22meta%22);%0A    %7D%0A%0A
     private void

write a uuid to the XML submission file backing a DeviceReportRecord