// This file is a part of the xMule Project.
//
// Copyright (c) 2004 Theodore R. Smith (hopeseekr@xmule.ws / http://www.xmule.ws/)
// DSA-1024 Fingerprint: 10A0 6372 9092 85A2 BB7F 907B CB8B 654B E33B F1ED
//
// Copyright (C)2002 Merkur ( merkur-@users.sourceforge.net / http://www.xmule-project.net )
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
// 
// This program 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 General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

//#include "wintypes.h"
//#include "types.h"
//#include "FriendListCtrl.h"
//#include "Friend.h"

#ifdef PRECOMP
#	include "xmule-headers.h"
#endif
#include "wintypes.h"
#include "FriendList.h"
#include "packets.h"                        // CInvalidPacket
#include "xmule.h"

#include "CFile.h"                          // CFile
#include "ListenSocket.h"                   // CClientReqSocket - audited 5 Nov 2004
#include "otherfunctions.h"                 // GetResString
#include "updownclient.h"                   // CUpDownClient
#include "otherfunctions.h"                 // GetTickCount - audited 5 Nov 2004
#include "FriendListCtrl.h"                 // CFriendListCtrl
#include "Friend.h"                         // CFriend

#include <DynPrefs/DynPrefs.h>              // Needed for DynamicPreferences

#include <wx/utils.h>
#include <wx/listimpl.cpp>
WX_DEFINE_LIST(FriendList);

CFriendList::CFriendList(void)
{
    LoadList();
    m_nLastSaved =::GetTickCount();
}

CFriendList::~CFriendList(void)
{
    SaveList();
    for (FriendList::Node *pos = m_listFriends.GetFirst() ; pos != 0 ; pos = pos->GetNext())
    //m_listFriends.GetAt(pos);:
    delete pos->GetData();
    //RemoveAll();:
    m_listFriends.Clear();
}

bool CFriendList::LoadList()
{
    CFriend *Record = 0;
    CFile file;
    //try {
    wxString strFileName(theApp.dynprefs->Get<wxString>("app-directory") + wxT("emfriends.met"));
    if (!wxFileExists(strFileName))
    return false;
    uint8_t header;
    file.Open(strFileName);
    try
    {
        if (1 != file.Read( &header, 1))
        throw CInvalidPacket();
        if (header != MET_HEADER)
        throw CInvalidPacket();
        uint32_t nRecordsNumber;
        if (4 != file.Read( &nRecordsNumber, 4))
        throw CInvalidPacket();
        for (uint32_t i = 0 ; i < nRecordsNumber ; i++)
        {
            Record = new CFriend();
            Record->LoadFromFile( &file);
            //AddTail(Record);:
            m_listFriends.Append(Record);
        }
    }
    catch(CInvalidPacket)
    {
        file.Close();
        return false;
    }
    file.Close();
    return true;
    //}
#if 0
    catch(CFileException *error)
    {
        OUTPUT_DEBUG_TRACE();
        if (error->m_cause == CFileException::endOfFile)
        theApp.xmuledlg->AddLogLine(true, GetResString(IDS_ERR_EMFRIENDSINVALID));
        else
        {
            char buffer[150];
            error->GetErrorMessage(buffer, 150);
            theApp.xmuledlg->AddLogLine(true, GetResString(IDS_ERR_READEMFRIENDS), buffer);
        }
        error->Delete();
        return false;
    }
#endif
}

void CFriendList::SaveList()
{
    CFile file;
    wxString strFileName(theApp.dynprefs->Get<wxString>("app-directory") + wxT("emfriends.met"));
    //if (!file.Open(strFileName.GetBuffer(),CFile::modeCreate|CFile::modeWrite))
    if (!file.Create(strFileName, TRUE))
    {
        // if this fail, xmule will crash because this is called from OnClose
        // and wxWindows will pop wxLog-window and at the same time application closes itself
        //
        return;
    }
    uint8_t header = MET_HEADER;
    file.Write( &header, 1);
    uint32_t nRecordsNumber = m_listFriends.GetCount();
    file.Write( &nRecordsNumber, 4);
    for (FriendList::Node *pos = m_listFriends.GetFirst() ; pos != 0 ; pos = pos->GetNext())
    {
        pos->GetData()->WriteToFile( &file);
    }
    file.Close();
}

CFriend *CFriendList::LinkFriend(unsigned char *abyUserHash, uint32_t m_dwIp, uint16_t m_nPort)
{
    CFriend *backup_friend = NULL;
    for (FriendList::Node *pos = m_listFriends.GetFirst() ; pos != 0 ; pos = pos->GetNext())
    {
        CFriend *cur_friend = pos->GetData();
        if (cur_friend->m_dwLastUsedIP == m_dwIp &&cur_friend->m_nLastUsedPort == m_nPort)
        backup_friend = cur_friend;
        if (!memcmp(cur_friend->m_abyUserhash, abyUserHash, 16))
        {
            //CTime lwtime;
            //mktime(lwtime.GetLocalTm());:
            cur_friend->m_dwLastSeen = time(NULL);
            if (cur_friend->m_LinkedClient)
            {
                cur_friend->m_LinkedClient->m_Friend = NULL;
                cur_friend->m_LinkedClient = NULL;
            }
            return cur_friend;
        }
    }
    return backup_friend;
}

void CFriendList::RefreshFriend(CFriend *torefresh)
{
    if (m_wndOutput)
    m_wndOutput->RefreshFriend(torefresh);
}

void CFriendList::ShowFriends()
{
    if (!m_wndOutput)
    {
        return;
    }
    m_wndOutput->DeleteAllItems();
    for (FriendList::Node *pos = m_listFriends.GetFirst() ; pos != 0 ; pos = pos->GetNext())
    {
        //m_listFriends.GetAt(pos);:
        CFriend *cur_friend = pos->GetData();
        m_wndOutput->AddFriend(cur_friend);
    }
}

//Added this to work with the IRC.. Probably a better way to do it.. But wanted this in the release..
void CFriendList::AddFriend(unsigned char t_m_abyUserhash[16], uint32_t tm_dwLastSeen, uint32_t tm_dwLastUsedIP, uint32_t tm_nLastUsedPort, uint32_t tm_dwLastChatted, wxString tm_strName, uint32_t tm_dwHasHash)
{
    CFriend *Record = 0;
    Record = new CFriend(t_m_abyUserhash, tm_dwLastSeen, tm_dwLastUsedIP, tm_nLastUsedPort, tm_dwLastChatted, tm_strName, tm_dwHasHash);
    //AddTail(Record);:
    m_listFriends.Append(Record);
    ShowFriends();
}

// Added for the friends function in the IRC..
bool CFriendList::IsAlreadyFriend(uint32_t tm_dwLastUsedIP, uint32_t tm_nLastUsedPort)
{
    for (FriendList::Node *pos = m_listFriends.GetFirst() ; pos != 0 ; pos = pos->GetNext())
    {
        //m_listFriends.GetAt(pos);:
        CFriend *cur_friend = pos->GetData();
        if (cur_friend->m_dwLastUsedIP == tm_dwLastUsedIP &&cur_friend->m_nLastUsedPort == tm_nLastUsedPort)
        {
            return true;
        }
    }
    return false;
}

void CFriendList::AddFriend(CUpDownClient *toadd)
{
    if (toadd->IsFriend())
    return;
    CFriend *NewFriend = new CFriend(toadd);
    toadd->m_Friend = NewFriend;
    //AddTail(NewFriend);:
    m_listFriends.Append(NewFriend);
    if (m_wndOutput)
    m_wndOutput->AddFriend(NewFriend);
}

void CFriendList::RemoveFriend(CFriend *todel)
{
    FriendList::Node *pos = m_listFriends.Find(todel);
    if (!pos)
    {
        return;
    }
    if (todel->m_LinkedClient)
    {
        todel->m_LinkedClient->SetFriendSlot(false);
        todel->m_LinkedClient->m_Friend = NULL;
        todel->m_LinkedClient = NULL;
    }
    if (m_wndOutput)
    m_wndOutput->RemoveFriend(todel);
    //RemoveAt(pos);:
    m_listFriends.DeleteNode(pos);
    delete todel;
}

void CFriendList::RemoveAllFriendSlots()
{
    for (FriendList::Node *pos = m_listFriends.GetFirst() ; pos != 0 ; pos = pos->GetNext())
    {
        //m_listFriends.GetAt(pos);:
        CFriend *cur_friend = pos->GetData();
        if (cur_friend->m_LinkedClient)
        {
            cur_friend->m_LinkedClient->SetFriendSlot(false);
        }
    }
}

void CFriendList::Process()
{
    // Madcat - No need to save friends every second, as they are saved during
    //          exiting anyway.
    //	if (::GetTickCount() - m_nLastSaved > 300000)
    //		this->SaveList();
}

