/**
 * @file llconversationlog.h
 *
 * $LicenseInfo:firstyear=2002&license=viewerlgpl$
 * Second Life Viewer Source Code
 * Copyright (C) 2012, Linden Research, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation;
 * version 2.1 of the License only.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 *
 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 * $/LicenseInfo$
 */

#ifndef LLCONVERSATIONLOG_H_
#define LLCONVERSATIONLOG_H_

#include "llcallingcard.h"
#include "llfloaterimsession.h"
#include "llimview.h"

class LLConversationLogObserver;
struct ConversationParams;

typedef LLIMModel::LLIMSession::SType SessionType;

/*
 * This class represents a particular session(conversation) of any type(im/voice/p2p/group/...) by storing some of session's data.
 * Each LLConversation object has a corresponding visual representation in a form of LLConversationLogListItem.
 */
class LLConversation
{
public:

    LLConversation(const ConversationParams& params);
    LLConversation(const LLIMModel::LLIMSession& session);
    LLConversation(const LLConversation& conversation);

    ~LLConversation();

    const SessionType&  getConversationType()   const   { return mConversationType; }
    const std::string&  getConversationName()   const   { return mConversationName; }
    const std::string&  getHistoryFileName()    const   { return mHistoryFileName; }
    const LLUUID&       getSessionID()          const   { return mSessionID; }
    const LLUUID&       getParticipantID()      const   { return mParticipantID; }
    const std::string&  getTimestamp()          const   { return mTimestamp; }
    const U64Seconds&
                        getTime()               const   { return mTime; }
    bool                hasOfflineMessages()    const   { return mHasOfflineIMs; }

    void setConversationName(const std::string &conv_name) { mConversationName = conv_name; }
    void setOfflineMessages(bool new_messages) { mHasOfflineIMs = new_messages; }
    bool isOlderThan(U32Days days) const;

    /*
     * updates last interaction time
     */
    void updateTimestamp();

    void updateHistoryFileName(const std::string &new_name) { mHistoryFileName = new_name; }

    /*
     * Resets flag of unread offline message to false when im floater with this conversation is opened.
     */
    void onIMFloaterShown(const LLUUID& session_id);

    /*
     * returns string representation(in form of: mm/dd/yyyy hh:mm) of time when conversation was started
     */
    static const std::string createTimestamp(const U64Seconds& utc_time);

private:

    /*
     * If conversation has unread offline messages sets callback for opening LLFloaterIMSession
     * with this conversation.
     */
    void setListenIMFloaterOpened();

    boost::signals2::connection mIMFloaterShowedConnection;

    U64Seconds mTime; // last interaction time
    SessionType     mConversationType;
    std::string     mConversationName;
    std::string     mHistoryFileName;
    LLUUID          mSessionID;
    LLUUID          mParticipantID;
    bool            mHasOfflineIMs;
    std::string     mTimestamp; // last interaction time in form of: mm/dd/yyyy hh:mm
};

/**
 * LLConversationLog stores all agent's conversations.
 * This class is responsible for creating and storing LLConversation objects when im or voice session starts.
 * Also this class saves/retrieves conversations to/from file.
 *
 * Also please note that it may be several conversations with the same sessionID stored in the conversation log.
 * To distinguish two conversations with the same sessionID it's also needed to compare their creation date.
 */

class LLConversationLog : public LLSingleton<LLConversationLog>, LLIMSessionObserver
{
    LLSINGLETON(LLConversationLog);
public:
    void removeConversation(const LLConversation& conversation);

    /**
     * Returns first conversation with matched session_id
     */
    const LLConversation*               getConversation(const LLUUID& session_id);
    const std::vector<LLConversation>&  getConversations() { return mConversations; }

    void addObserver(LLConversationLogObserver* observer);
    void removeObserver(LLConversationLogObserver* observer);

    // LLIMSessionObserver triggers
    virtual void sessionAdded(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id, bool has_offline_msg) override;
    virtual void sessionActivated(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id) override {}; // Stub
    virtual void sessionRemoved(const LLUUID& session_id) override{}                                            // Stub
    virtual void sessionVoiceOrIMStarted(const LLUUID& session_id) override{};                              // Stub
    virtual void sessionIDUpdated(const LLUUID& old_session_id, const LLUUID& new_session_id) override{};   // Stub

    void notifyObservers();

    void onNewMessageReceived(const LLSD& data);

    /**
     * public method which is called on viewer exit to save conversation log
     */
    void cache();
    // will check if current name is edentical with the one on disk and will rename the one on disk if it isn't
    void verifyFilename(const LLUUID& session_id, const std::string &expected_filename, const std::string &new_session_name);
    bool moveLog(const std::string &originDirectory, const std::string &targetDirectory);
    void getListOfBackupLogs(std::vector<std::string>& list_of_backup_logs);
    void deleteBackupLogs();

    void onClearLog();
    void onClearLogResponse(const LLSD& notification, const LLSD& response);

    bool getIsLoggingEnabled() { return mLoggingEnabled; }
    bool isLogEmpty() { return mConversations.empty(); }

    /**
     * inits connection to per account settings,
     * loads saved file and inits enabled state
     */
    void initLoggingState();

    /**
     * constructs file name in which conversations log will be saved
     * file name is conversation.log
     */
    std::string getFileName();
    LLConversation* findConversation(const LLIMModel::LLIMSession* session);

private:

    virtual ~LLConversationLog()
    {
        if (mAvatarNameCacheConnection.connected())
        {
            mAvatarNameCacheConnection.disconnect();
        }
    }

    void enableLogging(S32 log_mode);

    /**
     * adds conversation to the conversation list and notifies observers
     */
    void logConversation(const LLUUID& session_id, bool has_offline_msg);

    void notifyParticularConversationObservers(const LLUUID& session_id, U32 mask);

    bool saveToFile(const std::string& filename);
    bool loadFromFile(const std::string& filename);

    void onAvatarNameCache(const LLUUID& participant_id, const LLAvatarName& av_name, const LLIMModel::LLIMSession* session);

    void createConversation(const LLIMModel::LLIMSession* session);
    void updateConversationTimestamp(LLConversation* conversation);
    void updateConversationName(const LLIMModel::LLIMSession* session, const std::string& name);
    void updateOfflineIMs(const LLIMModel::LLIMSession* session, bool new_messages);



    typedef std::vector<LLConversation> conversations_vec_t;
    std::vector<LLConversation>             mConversations;
    std::set<LLConversationLogObserver*>    mObservers;

    LLFriendObserver* mFriendObserver;      // Observer of the LLAvatarTracker instance

    boost::signals2::connection mNewMessageSignalConnection;
    boost::signals2::connection mAvatarNameCacheConnection;

    bool mLoggingEnabled;
};

class LLConversationLogObserver
{
public:

    enum EConversationChange
        {
            CHANGED_TIME = 1, // last interaction time changed
            CHANGED_NAME = 2,  // conversation name changed
            CHANGED_OfflineIMs = 3
        };

    virtual ~LLConversationLogObserver(){}
    virtual void changed() = 0;
    virtual void changed(const LLUUID& session_id, U32 mask){};
};

#endif /* LLCONVERSATIONLOG_H_ */