/**
 * Copyright 2010 OpenEngSB Division, Vienna University of Technology
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.openengsb.connector.plaintextreport.internal;

import java.lang.reflect.Field;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;

import org.openengsb.core.common.Event;
import org.openengsb.core.common.util.AliveState;
import org.openengsb.domain.report.NoSuchReportException;
import org.openengsb.domain.report.common.AbstractReportDomain;
import org.openengsb.domain.report.model.Report;
import org.openengsb.domain.report.model.ReportPart;

public class PlaintextReportService extends AbstractReportDomain {

    private static final String CONTENT_TYPE = "text/plain";

    private Set<String> activeReportDataCollections = new HashSet<String>();

    private ReportPartStore partStore = new InMemoryReportPartStore();

    private String id;

    public PlaintextReportService(String id) {
        this.id = id;
    }

    @Override
    public Report generateReport(String reportId, String category, String reportName) throws NoSuchReportException {
        checkId(reportId);
        Report report = doGenerateReport(reportName, reportId);
        activeReportDataCollections.remove(reportId);
        partStore.clearParts(reportId);
        storeReport(category, report);
        return report;
    }

    @Override
    public Report getDraft(String reportId, String draftName) throws NoSuchReportException {
        checkId(reportId);
        return doGenerateReport(draftName, reportId);
    }

    @Override
    public String collectData() {
        String reportId = UUID.randomUUID().toString();
        activeReportDataCollections.add(reportId);
        return reportId;
    }

    @Override
    public void addReportPart(String reportId, ReportPart reportPart) throws NoSuchReportException {
        checkContentType(reportPart);
        checkId(reportId);
        partStore.storePart(reportId, reportPart);
    }

    @Override
    public void processEvent(String reportId, Event event) throws NoSuchReportException {
        checkId(reportId);
        StringReportPart reportPart = new StringReportPart(generatePartName(event), CONTENT_TYPE, getContent(event));
        partStore.storePart(reportId, reportPart);
    }

    @Override
    public AliveState getAliveState() {
        return AliveState.ONLINE;
    }

    public String getId() {
        return id;
    }

    private void checkContentType(ReportPart reportPart) {
        String contentType = reportPart.getContentType();
        if (!contentType.equals(CONTENT_TYPE)) {
            throw new IllegalArgumentException(
                "Plaintext report service does not support report parts with content type '" + contentType
                        + "'. It only supports content type '" + CONTENT_TYPE + "'.");
        }
    }

    private void checkId(String reportId) throws NoSuchReportException {
        if (activeReportDataCollections.contains(reportId)) {
            return;
        }
        throw new NoSuchReportException("Currently no report is generated for reportId: " + reportId);
    }

    private Report doGenerateReport(String reportName, String reportId) {
        List<ReportPart> parts = partStore.getParts(reportId);
        Report report = new Report(reportName);
        report.setParts(parts);
        return report;
    }

    private String getContent(Event event) {
        StringBuilder content = new StringBuilder();
        appendAll(content, "Event class: ", event.getClass().getName(), "\n");
        appendAll(content, "Event Fields: \n");
        for (Field field : event.getClass().getDeclaredFields()) {
            appendAll(content, "  ", field.getName(), ": ", getFieldValue(field, event), "\n");
        }
        return content.toString();
    }

    private String getFieldValue(Field field, Event event) {
        Object value = null;
        try {
            boolean accessible = field.isAccessible();
            field.setAccessible(true);
            value = field.get(event);
            field.setAccessible(accessible);
        } catch (IllegalAccessException e) {
            value = "[WARNING - can not access field value]";
        }
        return String.valueOf(value);
    }

    private void appendAll(StringBuilder builder, Object... objects) {
        for (Object o : objects) {
            builder.append(o);
        }
    }

    private String generatePartName(Event e) {
        return e.getClass().getName() + " - " + new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(new Date());
    }

}

@@ -703,26 +703,186 @@
 ava.
-lang.reflect.Field
+beans.BeanInfo;%0Aimport java.beans.IntrospectionException;%0Aimport java.beans.Introspector;%0Aimport java.beans.PropertyDescriptor;%0Aimport java.lang.reflect.InvocationTargetException
 ;%0Aim
@@ -4372,66 +4372,107 @@
     
-for (Field field : event.getClass().getDeclaredField
+BeanInfo beanInfo;%0A        try %7B%0A            beanInfo = Introspector.getBeanInfo(event.getClas
 s())
- %7B
+;
 %0A   
@@ -4484,91 +4484,148 @@
     
-appendAll(content, %22  %22, field.getName(), %22: %22, getFieldValue(field, event), %22%5Cn%22);
+PropertyDescriptor%5B%5D propertyDescriptors = beanInfo.getPropertyDescriptors();%0A            for (PropertyDescriptor p : propertyDescriptors) %7B
 %0A   
@@ -4625,34 +4625,32 @@
 ) %7B%0A        
-%7D%0A
         
 return conte
@@ -4641,130 +4641,127 @@
     
-return content.toString();
+try %7B
 %0A    
-%7D%0A%0A
     
-private String getFieldValue(Field field, Event event) %7B%0A        Object value = null
+            String name = p.getName();%0A                    Object value = p.getReadMethod().invoke(event)
 ;%0A  
@@ -4758,38 +4758,33 @@
 event);%0A        
-try %7B%0A
+ 
             bool
@@ -4782,173 +4782,285 @@
     
- boolean accessible = field.isAccessible();%0A            field.setAccessible(true);%0A            value = field.get(event);%0A            field.setAccessible(accessible);
+appendAll(content, %22  %22, name, %22: %22, value, %22%5Cn%22);%0A                %7D catch (IllegalAccessException e) %7B%0A                    appendAll(content, e);%0A                %7D catch (InvocationTargetException e) %7B%0A                    appendAll(content, e);%0A                %7D%0A            %7D
 %0A   
@@ -5070,36 +5070,36 @@
   %7D catch (I
-llegalAccess
+ntrospection
 Exception e)
@@ -5117,56 +5117,29 @@
     
-value = %22%5BWARNING - can not access field value%5D%22
+appendAll(content, e)
 ;%0A  
@@ -5165,28 +5165,25 @@
 urn 
-String.valueOf(value
+content.toString(
 );%0A 

update report-service to use BeanInfo to parseEvents instead of field-reflection