/*
 * 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;

import java.util.List;
import java.util.ArrayList;
import java.util.Date;
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectOutput;
import java.io.ObjectInput;

import jp.ossc.nimbus.core.Service;
import jp.ossc.nimbus.core.ServiceName;
import jp.ossc.nimbus.core.ServiceManagerFactory;

/**
 * ジャーナルレコード。<p>
 *
 * @author M.Takata
 */
public class JournalRecordImpl implements JournalRecord, Externalizable{
    
    protected String key;
    protected String requestId;
    protected Date startTime;
    protected Date endTime;
    protected ArrayList<JournalInfo> infos;
    
    protected JournalRecord parent;
    protected EditorFinder editorFinder;
    protected JournalEditor editor;
    
    public JournalRecordImpl(){
    }
    
    public JournalRecordImpl(String key, EditorFinder editorFinder){
        this.key = key;
        this.editorFinder = editorFinder;
    }
    
    public void setKey(String key){
        this.key = key;
    }
    
    @Override
    public String getKey(){
        return key;
    }
    
    @Override
    public Object getValue(){
        return this;
    }
    
    @Override
    public void setRequestId(String id){
        requestId = id;
    }
    
    @Override
    public String getRequestId(){
        return requestId;
    }
    
    public void setStartTime(Date time){
        startTime = time;
    }
    
    @Override
    public Date getStartTime(){
        return startTime;
    }
    
    public void setEndTime(Date time){
        endTime = time;
    }
    
    @Override
    public Date getEndTime(){
        return endTime;
    }
    
    @Override
    public void addInfo(JournalInfo info){
        if(infos == null){
            infos = new ArrayList<JournalInfo>();
        }
        if(!(info instanceof JournalRecord)){
            info.toObject(editorFinder);
        }
        infos.add(info);
    }
    
    @Override
    public List<JournalInfo> getInfos(){
        return infos;
    }
    
    @Override
    public List<JournalInfo> getInfos(String key){
        if(infos == null){
            return null;
        }
        List<JournalInfo> result = null;
        for(JournalInfo info : infos){
            if(key.equals(info.getKey())){
                if(result == null){
                    result = new ArrayList<JournalInfo>();
                }
                result.add(info);
            }
        }
        return result;
    }
    
    @Override
    public JournalEditor getJournalEditor(){
        return editor;
    }
    
    @Override
    public Object toObject(EditorFinder finder){
        editor = editorFinder.findEditor(
            getKey(),
            getValue()
        );
        Object result = this;
        if(editor != null){
            result = editor.toObject(editorFinder, getKey(), getValue());
        }
        return result;
    }
    
    @Override
    public void setEditorFinder(EditorFinder finder){
        editorFinder = finder;
    }
    
    @Override
    public EditorFinder getEditorFinder(){
        return editorFinder;
    }
    
    @Override
    public void start(JournalRecord parent, Date startTime){
        this.parent = parent;
        if(parent != null){
            parent.addInfo(this);
            setRequestId(parent.getRequestId());
        }
        setStartTime(startTime);
    }
    
    @Override
    public JournalRecord end(Date endTime){
        setEndTime(endTime);
        if(infos != null){
            infos.trimToSize();
        }
        return parent;
    }
    
    @Override
    public void setParent(JournalRecord parent){
        this.parent = parent;
    }
    
    @Override
    public JournalRecord getParent(){
        return parent;
    }
    
    @Override
    public String toString(){
        final StringBuilder buf = new StringBuilder(super.toString());
        buf.append('{');
        buf.append("key=").append(key);
        buf.append(",requestId=").append(requestId);
        buf.append(",startTime=").append(startTime);
        buf.append(",endTime=").append(endTime);
        buf.append(",infos=");
        if(infos == null){
            buf.append("null");
        }else{
            buf.append('[');
            for(int i = 0, imax = infos.size(); i < imax; i++){
                buf.append(infos.get(i));
                if(i != imax - 1){
                    buf.append(',');
                }
            }
            buf.append(']');
        }
        buf.append('}');
        return buf.toString();
    }
    
    @Override
    public void writeExternal(ObjectOutput out) throws IOException{
        out.writeObject(key);
        out.writeObject(requestId);
        out.writeObject(startTime);
        out.writeObject(endTime);
        out.writeInt(infos == null ? -1 : infos.size());
        if(infos != null){
            for(int i = 0, imax = infos.size(); i < imax; i++){
                out.writeObject(infos.get(i));
            }
        }
        if(editorFinder != null){
            if(editorFinder instanceof Service){
                Service service = (Service)editorFinder;
                ServiceName name = service.getServiceNameObject();
                if(name.getServiceManagerName() == null){
                    out.writeObject(editorFinder);
                }else{
                    out.writeObject(name);
                }
            }else{
                out.writeObject(editorFinder);
            }
        }else{
            out.writeObject(editorFinder);
        }
        if(editor != null){
            if(editor instanceof Service){
                Service service = (Service)editor;
                ServiceName name = service.getServiceNameObject();
                if(name.getServiceManagerName() == null){
                    out.writeObject(editor);
                }else{
                    out.writeObject(name);
                }
            }else{
                out.writeObject(editor);
            }
        }else{
            out.writeObject(editor);
        }
    }
    
    @Override
    public void readExternal(ObjectInput in)
     throws IOException, ClassNotFoundException{
        key = (String)in.readObject();
        requestId = (String)in.readObject();
        startTime = (Date)in.readObject();
        endTime = (Date)in.readObject();
        int length = in.readInt();
        if(length > 0){
            infos = new ArrayList<JournalInfo>();
            for(int i = 0; i < length; i++){
                JournalInfo info = (JournalInfo)in.readObject();
                if(info instanceof JournalRecord){
                    JournalRecord rec = (JournalRecord)info;
                    rec.setParent(this);
                }
                infos.add(info);
            }
        }
        Object obj = in.readObject();
        if(obj instanceof ServiceName){
            editorFinder = ServiceManagerFactory.getServiceObject(
                (ServiceName)obj
            );
        }else{
            editorFinder = (EditorFinder)obj;
        }
        if(obj instanceof ServiceName){
            editor = ServiceManagerFactory.getServiceObject(
                (ServiceName)obj
            );
        }else{
            editor = (JournalEditor)obj;
        }
    }
}