/*
 *  Licensed to the Apache Software Foundation (ASF) under one
 *  or more contributor license agreements.  See the NOTICE file
 *  distributed with this work for additional information
 *  regarding copyright ownership.  The ASF licenses this file
 *  to you 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.apache.vysper.xmpp.modules.extension.xep0045_muc.model;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.List;
import java.util.TimeZone;

import org.apache.vysper.xml.fragment.Renderer;
import org.apache.vysper.xmpp.modules.extension.xep0045_muc.stanzas.History;
import org.apache.vysper.xmpp.stanza.Stanza;

/**
 * The discussion history for a room
 *
 * @author The Apache MINA Project (dev@mina.apache.org)
 */
public class DiscussionHistory {

    public static final int DEFAULT_HISTORY_SIZE = 20;

    private int maxItems = DEFAULT_HISTORY_SIZE;

    private DiscussionMessage subjectMessage;

    private List<DiscussionMessage> items = new ArrayList<DiscussionMessage>();

    public void append(Stanza stanza, Occupant sender) {
        append(stanza, sender, Calendar.getInstance(TimeZone.getTimeZone("UTC")));
    }

    public void append(Stanza stanza, Occupant sender, Calendar timestamp) {
        synchronized (items) {
            DiscussionMessage discMsg = new DiscussionMessage(stanza, sender, timestamp);

            if (discMsg.hasSubject() && !discMsg.hasBody()) {
                subjectMessage = discMsg;
            } else {
                items.add(discMsg);
            }

            // check if size is over limits
            if (getSize() > maxItems) {
                items.remove(0);
            }
        }
    }

    private int getSize() {
        int size = items.size();
        if (subjectMessage != null)
            size++;
        return size;
    }

    public List<Stanza> createStanzas(Occupant receiver, boolean includeJid, History history) {
        synchronized (items) {

            int maxstanzas = history != null && history.getMaxStanzas() != null ? history.getMaxStanzas() : -1;
            int maxchars = history != null && history.getMaxChars() != null ? history.getMaxChars() : -1;
            int seconds = history != null && history.getSeconds() != null ? history.getSeconds() : -1;

            List<Stanza> stanzas = new ArrayList<Stanza>();

            if (maxchars == 0 || maxstanzas == 0 || seconds == 0) {
                // quick return for no-stanza requests
            } else {
                int counter = 0;
                int totalChars = 0;

                List<DiscussionMessage> itemsWithSubject = new ArrayList<DiscussionMessage>(items);
                // add subject if one is provided
                if (subjectMessage != null) {
                    itemsWithSubject.add(subjectMessage);
                }

                // the timestamp at which "seconds" start filtering from 
                long secondsLimit = -1;
                if (seconds != -1) {
                    Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
                    cal.add(Calendar.SECOND, -seconds);
                    secondsLimit = cal.getTimeInMillis();
                }

                // now add all messages, as long as the predicated are fulfilled
                // first, do this in reverse order so that older messages are filtered out
                for (int i = itemsWithSubject.size() - 1; i > -1; i--) {
                    DiscussionMessage item = itemsWithSubject.get(i);
                    Stanza stanza = item.createStanza(receiver, includeJid);
                    counter++;

                    if (secondsLimit != -1 && secondsLimit > item.getTimestamp().getTimeInMillis()) {
                        // too old, break
                        break;
                    }

                    if (history != null && history.getSince() != null && history.getSince().after(item.getTimestamp())) {
                        // too old, break
                        break;
                    }

                    // only count chars if needed
                    if (maxchars != -1) {
                        totalChars += new Renderer(stanza).getComplete().length();

                        if (totalChars > maxchars) {
                            break;
                        }
                    }

                    // checks after this line will include the last stanza
                    stanzas.add(stanza);
                    if (maxstanzas != -1 && counter == maxstanzas) {
                        // max number of stanzas reached, return
                        break;
                    }
                }
            }
            // reverse list so that the oldest message is first
            Collections.reverse(stanzas);
            return stanzas;
        }
    }
}
