/*
 * This software is distributed under following license based on modified BSD
 * style license.
 * ----------------------------------------------------------------------
 * 
 * Copyright 2009 The Nimbus2 Project. All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * 
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer. 
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE NIMBUS PROJECT ``AS IS'' AND ANY EXPRESS
 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
 * NO EVENT SHALL THE NIMBUS PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * 
 * The views and conclusions contained in the software and documentation are
 * those of the authors and should not be interpreted as representing official
 * policies, either expressed or implied, of the Nimbus2 Project.
 */
package jp.ossc.nimbus.service.journal.editor;

import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.Stack;

import jp.ossc.nimbus.service.journal.EditorFinder;
import jp.ossc.nimbus.service.journal.JournalInfo;
import jp.ossc.nimbus.service.journal.JournalRecord;
import jp.ossc.nimbus.service.journal.JournalEditor;

/**
 * {@link JournalRecord}をJSON形式文字列に編集するジャーナルエディター。<p>
 *
 * @author M.Takata
 */
public class JournalRecordJSONJournalEditorService extends JSONJournalEditorService
 implements JournalRecordJSONJournalEditorServiceMBean{
    
    private static final long serialVersionUID = 781127949886313069L;
    
    public static final String PROPERTY_REQUEST_ID = "RequestId";
    public static final String PROPERTY_START_TIME = "StartTime";
    public static final String PROPERTY_INFO = "Info";
    public static final String PROPERTY_END_TIME = "EndTime";
    public static final String PROPERTY_PERFORMANCE = "Performance";
    
    protected String[] secretInfos;
    protected Set<String> secretInfoSet;
    protected String[] enabledInfos;
    protected Set<String> enabledInfoSet;
    protected String[] disabledInfos;
    protected Set<String> disabledInfoSet;
    
    @Override
    public void setSecretInfos(String[] names){
        secretInfos = names;
    }
    
    @Override
    public String[] getSecretInfos(){
        return secretInfos;
    }
    
    @Override
    public void setEnabledInfos(String[] names){
        enabledInfos = names;
    }
    
    @Override
    public String[] getEnabledInfos(){
        return enabledInfos;
    }
    
    @Override
    public void setDisabledInfos(String[] names){
        disabledInfos = names;
    }
    
    @Override
    public String[] getDisabledInfos(){
        return disabledInfos;
    }
    
    @Override
    public void startService() throws Exception{
        if(secretInfos != null && secretInfos.length != 0){
            secretInfoSet = new HashSet<String>(secretInfos.length);
            for(String prop : secretInfos){
                secretInfoSet.add(prop);
            }
        }
        if(enabledInfos != null && enabledInfos.length != 0){
            enabledInfoSet = new HashSet<String>(enabledInfos.length);
            for(String prop : enabledInfos){
                enabledInfoSet.add(prop);
            }
        }
        if(disabledInfos != null && disabledInfos.length != 0){
            disabledInfoSet = new HashSet<String>(disabledInfos.length);
            for(String prop : disabledInfos){
                disabledInfoSet.add(prop);
            }
        }
    }
    
    protected boolean isOutputInfo(String name){
        if(name != null
            && disabledInfoSet != null
            && disabledInfoSet.contains(name)
        ){
            return false;
        }
        if(name != null
            && enabledInfoSet != null
            && !enabledInfoSet.contains(name)
        ){
            return false;
        }
        return true;
    }
    
    protected boolean isSecretInfo(String name){
        return name != null && secretInfoSet != null && secretInfoSet.contains(name);
    }
    
    protected StringBuilder appendInfo(StringBuilder buf, EditorFinder finder, String name, Object value, Stack<Object> stack){
        appendName(buf, name);
        buf.append(PROPERTY_SEPARATOR);
        if(isSecretInfo(name)){
            appendValue(buf, finder, null, secretString, stack);
        }else{
            appendValue(buf, finder, null, value, stack);
        }
        return buf;
    }
    
    @Override
    protected StringBuilder appendUnknownValue(StringBuilder buf, EditorFinder finder, Class<?> type, Object value, Stack<Object> stack){
        if(!(value instanceof JournalRecord)){
            return super.appendUnknownValue(buf, finder, type, value, stack);
        }
        JournalRecord record = (JournalRecord)value;
        
        buf.append(OBJECT_ENCLOSURE_START);
        boolean isOutput = false;
        if(isOutputProperty(PROPERTY_REQUEST_ID)){
            appendProperty(buf, finder, PROPERTY_REQUEST_ID, record.getRequestId(), stack);
            isOutput = true;
        }
        if(isOutputProperty(PROPERTY_START_TIME)){
            if(isOutput){
                buf.append(ARRAY_SEPARATOR);
            }
            appendProperty(buf, finder, PROPERTY_START_TIME, record.getStartTime(), stack);
            isOutput = true;
        }
        if(isOutputProperty(PROPERTY_INFO)){
            List<JournalInfo> infos = record.getInfos();
            if(infos != null && infos.size() != 0){
                if(isOutput){
                    buf.append(ARRAY_SEPARATOR);
                }
                appendName(
                    buf,
                    PROPERTY_INFO
                );
                buf.append(PROPERTY_SEPARATOR);
                buf.append(ARRAY_ENCLOSURE_START);
                boolean isOutputPre = false;
                for(int i = 0, imax = infos.size(); i < imax; i++){
                    JournalInfo info = infos.get(i);
                    isOutput = isOutputInfo(info.getKey());
                    if(isOutputPre && isOutput){
                        buf.append(ARRAY_SEPARATOR);
                    }
                    if(isOutput){
                        Object infoObj = info.toObject(finder);
                        JournalEditor editor = info.getJournalEditor();
                        if(editor != null && editor instanceof JSONJournalEditorService){
                            buf.append(infoObj);
                        }else{
                            buf.append(OBJECT_ENCLOSURE_START);
                            appendInfo(buf, finder, info.getKey(), infoObj, stack);
                            buf.append(OBJECT_ENCLOSURE_END);
                        }
                        isOutputPre = isOutput;
                    }
                }
                buf.append(ARRAY_ENCLOSURE_END);
                isOutput = true;
            }
        }
        if(isOutputProperty(PROPERTY_END_TIME)){
            if(isOutput){
                buf.append(ARRAY_SEPARATOR);
            }
            appendProperty(buf, finder, PROPERTY_END_TIME, record.getEndTime(), stack);
            isOutput = true;
        }
        if(isOutputProperty(PROPERTY_PERFORMANCE)){
            if(isOutput){
                buf.append(ARRAY_SEPARATOR);
            }
            appendProperty(
                buf,
                finder,
                PROPERTY_PERFORMANCE,
                record.getEndTime().getTime() - record.getStartTime().getTime(),
                stack
            );
            isOutput = true;
        }
        buf.append(OBJECT_ENCLOSURE_END);
        return buf;
    }
}